156 lines
4.9 KiB
JavaScript
156 lines
4.9 KiB
JavaScript
/**
|
|
* 环境数据层:管理多环境配置。
|
|
*
|
|
* 数据存放在 chrome.storage.session(解锁后明文,内存中、不落盘)。
|
|
* 首次创建口令由 crypto-store.setupVault 完成(含预置环境)。
|
|
* 锁定时 session 被清空,本层返回空数据。
|
|
*
|
|
* 浏览器中通过全局 chrome.storage.session 存取;Node 测试中注入 mock。
|
|
*/
|
|
(function (root, factory) {
|
|
if (typeof module === "object" && module.exports) {
|
|
module.exports = factory({
|
|
getStorage: () => (typeof chrome !== "undefined" ? chrome.storage.session : null),
|
|
getLocalStorage: () => (typeof chrome !== "undefined" ? chrome.storage.local : null),
|
|
});
|
|
} else {
|
|
const api = factory({
|
|
getStorage: () => chrome.storage.session,
|
|
getLocalStorage: () => chrome.storage.local,
|
|
});
|
|
root.EnvStore = api;
|
|
}
|
|
})(typeof globalThis !== "undefined" ? globalThis : this, function ({ getStorage, getLocalStorage }) {
|
|
const ENV_KEY = "environments";
|
|
const ACTIVE_ENV_KEY = "activeEnvId";
|
|
|
|
function genId() {
|
|
return "env_" + Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
|
|
}
|
|
|
|
function genCfgId() {
|
|
return Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
|
|
}
|
|
|
|
function storage() {
|
|
const s = getStorage();
|
|
if (!s) throw new Error("chrome.storage.session 不可用");
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* 加载所有环境。未解锁时 session 为空,返回空数组。
|
|
*/
|
|
async function loadEnvironments() {
|
|
const data = await storage().get([ENV_KEY, ACTIVE_ENV_KEY]);
|
|
const environments = data[ENV_KEY] || [];
|
|
const activeEnvId = data[ACTIVE_ENV_KEY] || environments[0]?.id || null;
|
|
return { environments, activeEnvId };
|
|
}
|
|
|
|
async function saveEnvironments(environments, activeEnvId) {
|
|
const patch = { [ENV_KEY]: environments };
|
|
if (activeEnvId !== undefined) patch[ACTIVE_ENV_KEY] = activeEnvId;
|
|
await storage().set(patch);
|
|
}
|
|
|
|
async function getActiveEnv() {
|
|
const { environments, activeEnvId } = await loadEnvironments();
|
|
return environments.find((e) => e.id === activeEnvId) || environments[0] || null;
|
|
}
|
|
|
|
async function setActiveEnv(envId) {
|
|
await storage().set({ [ACTIVE_ENV_KEY]: envId });
|
|
}
|
|
|
|
const MAX_ENVIRONMENTS = 5;
|
|
|
|
async function createEnvironment(name) {
|
|
const { environments } = await loadEnvironments();
|
|
if (environments.length >= MAX_ENVIRONMENTS) {
|
|
throw new Error(`最多 ${MAX_ENVIRONMENTS} 个环境`);
|
|
}
|
|
const env = { id: genId(), name, configs: [] };
|
|
environments.push(env);
|
|
await saveEnvironments(environments, env.id);
|
|
return env;
|
|
}
|
|
|
|
async function renameEnvironment(envId, newName) {
|
|
const { environments } = await loadEnvironments();
|
|
const env = environments.find((e) => e.id === envId);
|
|
if (env) {
|
|
env.name = newName;
|
|
await saveEnvironments(environments);
|
|
}
|
|
return env;
|
|
}
|
|
|
|
async function deleteEnvironment(envId) {
|
|
const { environments, activeEnvId } = await loadEnvironments();
|
|
if (environments.length <= 1) {
|
|
throw new Error("至少保留一个环境");
|
|
}
|
|
const idx = environments.findIndex((e) => e.id === envId);
|
|
if (idx === -1) return;
|
|
environments.splice(idx, 1);
|
|
const newActive = activeEnvId === envId ? environments[0].id : activeEnvId;
|
|
await saveEnvironments(environments, newActive);
|
|
// 清理该环境的失败计数(失败计数非敏感,存于 local 持久化)
|
|
const local = getLocalStorage();
|
|
if (local) {
|
|
const attemptsData = await local.get("loginAttempts");
|
|
const attempts = attemptsData.loginAttempts || {};
|
|
Object.keys(attempts).forEach((k) => {
|
|
if (k.startsWith(envId + "::")) delete attempts[k];
|
|
});
|
|
await local.set({ loginAttempts: attempts });
|
|
}
|
|
}
|
|
|
|
/** 当前激活环境的配置列表 */
|
|
async function loadConfigs() {
|
|
const env = await getActiveEnv();
|
|
return env?.configs || [];
|
|
}
|
|
|
|
/** 覆盖保存当前激活环境的配置 */
|
|
async function saveConfigs(configs) {
|
|
const { environments, activeEnvId } = await loadEnvironments();
|
|
const env = environments.find((e) => e.id === activeEnvId);
|
|
if (!env) throw new Error("未找到当前环境");
|
|
env.configs = configs;
|
|
await saveEnvironments(environments);
|
|
}
|
|
|
|
/** 扁平化所有环境的配置,附带 envId / envName(用于全局匹配) */
|
|
async function loadAllConfigs() {
|
|
const { environments } = await loadEnvironments();
|
|
const all = [];
|
|
environments.forEach((env) => {
|
|
(env.configs || []).forEach((cfg) => {
|
|
all.push({ ...cfg, envId: env.id, envName: env.name });
|
|
});
|
|
});
|
|
return all;
|
|
}
|
|
|
|
return {
|
|
ENV_KEY,
|
|
ACTIVE_ENV_KEY,
|
|
MAX_ENVIRONMENTS,
|
|
loadEnvironments,
|
|
saveEnvironments,
|
|
getActiveEnv,
|
|
setActiveEnv,
|
|
createEnvironment,
|
|
renameEnvironment,
|
|
deleteEnvironment,
|
|
loadConfigs,
|
|
saveConfigs,
|
|
loadAllConfigs,
|
|
genId,
|
|
genCfgId,
|
|
};
|
|
});
|