177 lines
5.9 KiB
JavaScript
177 lines
5.9 KiB
JavaScript
importScripts("crypto-store.js");
|
|
|
|
let masterKey = null; // 解锁后持有,锁定或 SW 重启后清空
|
|
let autoLockTimer = null;
|
|
const AUTO_LOCK_MS = 10 * 60 * 1000; // 10 分钟无操作自动锁定
|
|
|
|
chrome.runtime.onInstalled.addListener(() => {
|
|
chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true })
|
|
.catch((err) => console.warn("[AutoLogin] 设置 sidePanel 行为失败:", err));
|
|
chrome.sidePanel.setOptions({ panelWidth: 340 })
|
|
.catch((err) => console.warn("[AutoLogin] 设置 sidePanel 宽度失败:", err));
|
|
migrateLegacyVault();
|
|
console.log("[AutoLogin] 扩展已安装");
|
|
});
|
|
|
|
chrome.action.onClicked.addListener((tab) => {
|
|
if (tab?.id !== undefined) {
|
|
chrome.sidePanel.open({ tabId: tab.id }).catch(() => {});
|
|
}
|
|
});
|
|
|
|
// 旧版明文数据迁移:把 loginConfigs 转为 environments 明文结构
|
|
async function migrateLegacyVault() {
|
|
const data = await chrome.storage.local.get(["environments", "loginConfigs", "vaultMeta"]);
|
|
if (data.vaultMeta) return;
|
|
if (Array.isArray(data.environments) && data.environments.length > 0) return;
|
|
if (Array.isArray(data.loginConfigs) && data.loginConfigs.length > 0) {
|
|
const envs = [{ id: "env_legacy", name: "默认", configs: data.loginConfigs }];
|
|
await chrome.storage.local.set({ environments: envs });
|
|
await chrome.storage.local.remove("loginConfigs");
|
|
console.log("[AutoLogin] 旧版 loginConfigs 已迁移为明文 environments");
|
|
}
|
|
}
|
|
|
|
// 无 vault 明文模式:确保 session 有 environments(从 local 加载或创建预设)
|
|
async function ensurePlainEnvironments() {
|
|
if (await CryptoStore.hasVault()) return; // 加密模式不处理
|
|
const data = await chrome.storage.local.get("environments");
|
|
let envs = data.environments;
|
|
if (!Array.isArray(envs) || envs.length === 0) {
|
|
const PRESET = ["个人", "测试", "生产"];
|
|
envs = PRESET.map((name) => ({
|
|
id: "env_" + Date.now().toString(36) + Math.random().toString(36).slice(2, 8),
|
|
name,
|
|
configs: [],
|
|
}));
|
|
await chrome.storage.local.set({ environments: envs });
|
|
}
|
|
const session = await chrome.storage.session.get("environments");
|
|
if (!session.environments) {
|
|
await chrome.storage.session.set({
|
|
environments: envs,
|
|
activeEnvId: envs[0]?.id || null,
|
|
});
|
|
}
|
|
}
|
|
|
|
// SW 启动即初始化明文环境到 session,供 content script 自动登录读取
|
|
ensurePlainEnvironments().catch((err) => console.warn("[AutoLogin] 启动初始化失败:", err));
|
|
|
|
|
|
function resetAutoLockTimer() {
|
|
if (autoLockTimer) clearTimeout(autoLockTimer);
|
|
if (masterKey) {
|
|
autoLockTimer = setTimeout(() => {
|
|
console.log("[AutoLogin] 闲置超时,自动锁定");
|
|
doLock();
|
|
}, AUTO_LOCK_MS);
|
|
}
|
|
}
|
|
|
|
async function doLock() {
|
|
masterKey = null;
|
|
if (autoLockTimer) { clearTimeout(autoLockTimer); autoLockTimer = null; }
|
|
await CryptoStore.lock();
|
|
}
|
|
|
|
// session 中 environments 变化时用内存密钥重新加密写回 local
|
|
chrome.storage.onChanged.addListener(async (changes, areaName) => {
|
|
if (areaName !== "session") return;
|
|
if (!changes.environments) return;
|
|
const environments = changes.environments.newValue;
|
|
if (!environments) return; // 锁定时被清除,无需持久化
|
|
try {
|
|
if (masterKey) {
|
|
await CryptoStore.persistEncrypted(environments, masterKey);
|
|
} else {
|
|
// 无 vault 明文模式:明文回写 local
|
|
await chrome.storage.local.set({ environments });
|
|
}
|
|
} catch (err) {
|
|
console.warn("[AutoLogin] 持久化失败:", err);
|
|
}
|
|
});
|
|
|
|
|
|
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
(async () => {
|
|
try {
|
|
switch (message.action) {
|
|
case "getVaultStatus": {
|
|
const hasVault = await CryptoStore.hasVault();
|
|
if (!hasVault) {
|
|
await ensurePlainEnvironments();
|
|
sendResponse({ hasVault: false, unlocked: true, pendingImport: false });
|
|
break;
|
|
}
|
|
const unlocked = !!masterKey;
|
|
sendResponse({ hasVault, unlocked, pendingImport: false });
|
|
break;
|
|
}
|
|
|
|
case "setupVault": {
|
|
// 设置口令:若已有明文 environments 则加密它,否则创建预设
|
|
const localData = await chrome.storage.local.get("environments");
|
|
let initialEnvs;
|
|
if (Array.isArray(localData.environments) && localData.environments.length > 0) {
|
|
initialEnvs = localData.environments;
|
|
} else {
|
|
const PRESET = ["个人", "测试", "生产"];
|
|
initialEnvs = PRESET.map((name) => ({
|
|
id: "env_" + Date.now().toString(36) + Math.random().toString(36).slice(2, 8),
|
|
name,
|
|
configs: [],
|
|
}));
|
|
}
|
|
masterKey = await CryptoStore.setupVault(message.password, initialEnvs);
|
|
await chrome.storage.local.remove("environments"); // 已加密,明文不再需要
|
|
resetAutoLockTimer();
|
|
sendResponse({ success: true });
|
|
break;
|
|
}
|
|
|
|
case "unlock": {
|
|
try {
|
|
const { key } = await CryptoStore.unlock(message.password);
|
|
masterKey = key;
|
|
resetAutoLockTimer();
|
|
sendResponse({ success: true });
|
|
} catch (err) {
|
|
sendResponse({ success: false, error: err.message });
|
|
}
|
|
break;
|
|
}
|
|
|
|
case "lock": {
|
|
await doLock();
|
|
sendResponse({ success: true });
|
|
break;
|
|
}
|
|
|
|
case "getActivity": {
|
|
|
|
sendResponse({
|
|
unlocked: !!masterKey,
|
|
autoLockMs: AUTO_LOCK_MS,
|
|
});
|
|
break;
|
|
}
|
|
|
|
case "ping": {
|
|
// 任何消息都视为用户活动,顺带重置自动锁定计时
|
|
resetAutoLockTimer();
|
|
sendResponse({ pong: true });
|
|
break;
|
|
}
|
|
|
|
default:
|
|
sendResponse({ error: "unknown action" });
|
|
}
|
|
} catch (err) {
|
|
sendResponse({ success: false, error: err.message });
|
|
}
|
|
})();
|
|
return true; // 异步响应
|
|
});
|