no message

This commit is contained in:
yinjun.chen1
2026-09-16 16:12:52 +08:00
parent 49c809f513
commit 2dbce0d83b
2 changed files with 68 additions and 38 deletions
+30 -3
View File
@@ -57,6 +57,7 @@ async function ensurePlainEnvironments() {
// SW 启动即初始化明文环境到 session,供 content script 自动登录读取 // SW 启动即初始化明文环境到 session,供 content script 自动登录读取
ensurePlainEnvironments().catch((err) => console.warn("[AutoLogin] 启动初始化失败:", err)); ensurePlainEnvironments().catch((err) => console.warn("[AutoLogin] 启动初始化失败:", err));
autoRestoreUnlock().catch((err) => console.warn("[AutoLogin] 自动恢复解锁失败:", err));
function resetAutoLockTimer() { function resetAutoLockTimer() {
@@ -73,6 +74,23 @@ async function doLock() {
masterKey = null; masterKey = null;
if (autoLockTimer) { clearTimeout(autoLockTimer); autoLockTimer = null; } if (autoLockTimer) { clearTimeout(autoLockTimer); autoLockTimer = null; }
await CryptoStore.lock(); await CryptoStore.lock();
await chrome.storage.session.remove("__autoPwd");
}
// SW 重启后自动恢复解锁:从 session 取已存密码解锁 vault
async function autoRestoreUnlock() {
try {
if (!(await CryptoStore.hasVault())) return;
if (masterKey) return;
const { __autoPwd } = await chrome.storage.session.get("__autoPwd");
if (!__autoPwd) return;
const { key } = await CryptoStore.unlock(__autoPwd);
masterKey = key;
resetAutoLockTimer();
console.log("[AutoLogin] 自动恢复解锁成功");
} catch (err) {
await chrome.storage.session.remove("__autoPwd").catch(() => {});
}
} }
// session 中 environments 变化时用内存密钥重新加密写回 local // session 中 environments 变化时用内存密钥重新加密写回 local
@@ -102,11 +120,18 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
const hasVault = await CryptoStore.hasVault(); const hasVault = await CryptoStore.hasVault();
if (!hasVault) { if (!hasVault) {
await ensurePlainEnvironments(); await ensurePlainEnvironments();
sendResponse({ hasVault: false, unlocked: true, pendingImport: false }); const local = await chrome.storage.local.get("environments");
const sess = await chrome.storage.session.get("environments");
const cfgCount = (arr) => (arr || []).reduce((s, e) => s + (e.configs?.length || 0), 0);
sendResponse({
hasVault: false, unlocked: true, pendingImport: false,
localEnvs: (local.environments || []).length, localCfgs: cfgCount(local.environments),
sessEnvs: (sess.environments || []).length, sessCfgs: cfgCount(sess.environments),
});
break; break;
} }
const unlocked = !!masterKey; if (!masterKey) await autoRestoreUnlock();
sendResponse({ hasVault, unlocked, pendingImport: false }); sendResponse({ hasVault, unlocked: !!masterKey, pendingImport: false });
break; break;
} }
@@ -125,6 +150,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
})); }));
} }
masterKey = await CryptoStore.setupVault(message.password, initialEnvs); masterKey = await CryptoStore.setupVault(message.password, initialEnvs);
await chrome.storage.session.set({ __autoPwd: message.password });
await chrome.storage.local.remove("environments"); // 已加密,明文不再需要 await chrome.storage.local.remove("environments"); // 已加密,明文不再需要
resetAutoLockTimer(); resetAutoLockTimer();
sendResponse({ success: true }); sendResponse({ success: true });
@@ -135,6 +161,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
try { try {
const { key } = await CryptoStore.unlock(message.password); const { key } = await CryptoStore.unlock(message.password);
masterKey = key; masterKey = key;
await chrome.storage.session.set({ __autoPwd: message.password });
resetAutoLockTimer(); resetAutoLockTimer();
sendResponse({ success: true }); sendResponse({ success: true });
} catch (err) { } catch (err) {
+38 -35
View File
@@ -1,6 +1,16 @@
const t = (key, ...args) => chrome.i18n.getMessage(key, args) || key; const t = (key, ...args) => chrome.i18n.getMessage(key, args) || key;
function showDiag(msg) {
try {
const el = document.createElement("div");
el.textContent = "[AutoLogin] " + msg;
el.style.cssText = "position:fixed;top:10px;right:10px;z-index:2147483647;background:#1a1a2e;color:#e94560;padding:8px 12px;border-radius:6px;font-size:12px;font-family:monospace;max-width:90vw;word-break:break-all;box-shadow:0 2px 8px rgba(0,0,0,.4)";
(document.body || document.documentElement).appendChild(el);
setTimeout(() => el.remove(), 10000);
} catch {}
}
async function safeStorageGet(area, keys) { async function safeStorageGet(area, keys) {
try { try {
return await chrome.storage[area].get(keys); return await chrome.storage[area].get(keys);
@@ -378,64 +388,55 @@ function attemptKey(envId, domain) {
} }
async function getDomainAttempts(envId, domain) { async function getDomainAttempts(envId, domain) {
const key = attemptKey(envId, domain); const resp = await chrome.runtime.sendMessage({ action: "getDomainAttempts", envId, domain }).catch(() => null);
const data = await safeStorageGet("local", "loginAttempts"); return resp?.attempts || { count: 0, lastAttempt: 0 };
const attempts = data.loginAttempts || {};
return attempts[key] || { count: 0, lastAttempt: 0 };
} }
async function incrementDomainAttempts(envId, domain) { async function incrementDomainAttempts(envId, domain) {
const key = attemptKey(envId, domain); const resp = await chrome.runtime.sendMessage({ action: "incrementDomainAttempts", envId, domain }).catch(() => null);
const data = await safeStorageGet("local", "loginAttempts"); return resp?.attempts || { count: 0, lastAttempt: 0 };
const attempts = data.loginAttempts || {};
const cur = attempts[key] || { count: 0, lastAttempt: 0 };
cur.count += 1;
cur.lastAttempt = Date.now();
attempts[key] = cur;
await safeStorageSet("local", { loginAttempts: attempts });
return cur;
} }
async function resetDomainAttempts(envId, domain) { async function resetDomainAttempts(envId, domain) {
const key = attemptKey(envId, domain); await chrome.runtime.sendMessage({ action: "resetDomainAttempts", envId, domain }).catch(() => {});
const data = await safeStorageGet("local", "loginAttempts");
const attempts = data.loginAttempts || {};
if (attempts[key]) {
delete attempts[key];
await safeStorageSet("local", { loginAttempts: attempts });
}
} }
async function loadAllConfigs() { async function loadAllConfigs() {
const data = await safeStorageGet("session", "environments"); const resp = await chrome.runtime.sendMessage({ action: "loadAllConfigs" }).catch(() => null);
const environments = data.environments || []; return resp?.configs || [];
const all = [];
environments.forEach((env) => {
(env.configs || []).forEach((cfg) => {
all.push({ ...cfg, envId: env.id, envName: env.name });
});
});
return all;
} }
async function autoFillOnLoad() { async function autoFillOnLoad() {
let allConfigs = await loadAllConfigs(); let allConfigs = await loadAllConfigs();
if (allConfigs.length === 0) { if (allConfigs.length === 0) {
// session 可能为空(明文模式 SW 重启后未初始化),触发 background 加载后重试 // session 可能为空(明文模式 SW 重启后未初始化),触发 background 加载后重试
let resp = null;
try { try {
await chrome.runtime.sendMessage({ action: "getVaultStatus" }); resp = await chrome.runtime.sendMessage({ action: "getVaultStatus" });
allConfigs = await loadAllConfigs(); allConfigs = await loadAllConfigs();
} catch {} } catch (e) {
showDiag("初始化失败: " + (e.message || e));
return;
}
if (allConfigs.length === 0) {
showDiag("无配置 | local: envs=" + (resp?.localEnvs) + " cfgs=" + (resp?.localCfgs) + " | session: envs=" + (resp?.sessEnvs) + " cfgs=" + (resp?.sessCfgs));
return;
}
} }
if (allConfigs.length === 0) return;
// 每个配置有独立开关 enabled // 每个配置有独立开关 enabled
const enabledConfigs = allConfigs.filter((c) => c.enabled !== false); const enabledConfigs = allConfigs.filter((c) => c.enabled !== false);
if (enabledConfigs.length === 0) return; if (enabledConfigs.length === 0) {
showDiag("所有配置已禁用");
return;
}
const matched = enabledConfigs.filter((c) => isDomainMatch(window.location.href, c.domain)); const matched = enabledConfigs.filter((c) => isDomainMatch(window.location.href, c.domain));
if (matched.length === 0) return; if (matched.length === 0) {
showDiag("未匹配: " + window.location.href + " | 配置: " + enabledConfigs.map((c) => c.domain).join(", "));
return;
}
if (matched.length === 1) { if (matched.length === 1) {
await fillSingleConfig(matched[0]); await fillSingleConfig(matched[0]);
@@ -484,7 +485,9 @@ async function fillSingleConfig(config) {
const tryFill = () => { const tryFill = () => {
const elapsed = Date.now() - startTime; const elapsed = Date.now() - startTime;
if (elapsed > SPA_FILL_TIMEOUT_MS) { if (elapsed > SPA_FILL_TIMEOUT_MS) {
console.warn("[AutoLogin]", t("logSpaTimeout", String(SPA_FILL_TIMEOUT_MS / 1000))); const inputs = deepQuerySelectorAll("input");
const pwd = deepQuerySelectorAll("input[type='password']");
showDiag("填充超时: input=" + inputs.length + " pwd=" + pwd.length + " form=" + document.querySelectorAll("form").length + " | " + inputs.slice(0, 6).map((i) => "type=" + (i.type || "?") + "/name=" + (i.name || i.id || "?")).join(", "));
return; return;
} }
const filled = fillForm(config, canSubmit); const filled = fillForm(config, canSubmit);