Files
auto-login/background.js
T
2026-09-15 01:09:11 +08:00

156 lines
5.1 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(() => {});
}
});
// 明文数据无法在无口令下加密,暂存为 pendingImport,待用户设置口令时由 setupVault 导入
async function migrateLegacyVault() {
const data = await chrome.storage.local.get(["environments", "loginConfigs", "vaultMeta"]);
const hasVault = !!data.vaultMeta;
if (hasVault) return;
const legacyEnvs = data.environments;
const legacyConfigs = data.loginConfigs;
if (legacyEnvs || legacyConfigs) {
let pending = null;
if (Array.isArray(legacyEnvs) && legacyEnvs.length > 0) {
pending = { type: "environments", data: legacyEnvs };
} else if (Array.isArray(legacyConfigs) && legacyConfigs.length > 0) {
pending = {
type: "configs",
data: [{ id: "env_legacy", name: "默认", configs: legacyConfigs }],
};
}
if (pending) {
await chrome.storage.local.set({ pendingImport: pending });
// 清掉明文,避免继续以明文存在
await chrome.storage.local.remove(["environments", "loginConfigs"]);
console.log("[AutoLogin] 旧版明文数据已标记为待导入,等待设置口令后加密");
}
}
}
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 || !masterKey) return;
try {
const environments = changes.environments.newValue;
if (!environments) return; // 锁定时被清除,无需加密
await CryptoStore.persistEncrypted(environments, masterKey);
} 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();
const unlocked = hasVault ? !!masterKey : false;
const pending = (await chrome.storage.local.get("pendingImport")).pendingImport || null;
sendResponse({ hasVault, unlocked, pendingImport: !!pending });
break;
}
case "setupVault": {
// 首次设置口令,若有待导入的旧数据则一并加密
const pendingData = await chrome.storage.local.get("pendingImport");
let initialEnvs = [];
if (pendingData.pendingImport) {
initialEnvs = pendingData.pendingImport.data;
} 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("pendingImport");
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; // 异步响应
});