feat: add account run history module and logging functionality

- Implemented `background/account-run-history.js` to persist account run results (success, failure, stop) in `chrome.storage.local` and log to a text file when the local Hotmail helper is enabled.
- Enhanced `background/auto-run-controller.js` and `background/message-router.js` to utilize the new account run record functionality.
- Updated steps in `background/steps/fetch-login-code.js`, `background/steps/fetch-signup-code.js`, and `background/steps/oauth-login.js` to handle retries and logging appropriately.
- Introduced tests for account run history and step retry limits to ensure functionality and reliability.
- Modified documentation to reflect new features and changes in behavior for specific providers (e.g., 2925).
This commit is contained in:
QLHazyCoder
2026-04-17 02:52:26 +08:00
parent e48941806f
commit 6652899758
15 changed files with 858 additions and 67 deletions
+65 -48
View File
@@ -5,6 +5,7 @@
const {
addLog,
completeStepFromBackground,
getErrorMessage,
getLoginAuthStateLabel,
getState,
isStep6RecoverableResult,
@@ -15,10 +16,12 @@
sendToContentScriptResilient,
shouldSkipLoginVerificationForCpaCallback,
skipLoginVerificationStepsForCpaCallback,
STEP6_MAX_ATTEMPTS,
throwIfStopped,
} = deps;
async function executeStep6(state) {
async function executeStep6(state, options = {}) {
const { skipPreLoginCleanup = false } = options;
if (shouldSkipLoginVerificationForCpaCallback(state)) {
await skipLoginVerificationStepsForCpaCallback();
return;
@@ -27,63 +30,77 @@
throw new Error('缺少邮箱地址,请先完成步骤 3。');
}
await runPreStep6CookieCleanup();
if (!skipPreLoginCleanup) {
await runPreStep6CookieCleanup();
}
let attempt = 0;
let lastError = null;
while (true) {
while (attempt < STEP6_MAX_ATTEMPTS) {
throwIfStopped();
attempt += 1;
const currentState = attempt === 1 ? state : await getState();
const password = currentState.password || currentState.customPassword || '';
const oauthUrl = await refreshOAuthUrlBeforeStep6(currentState);
try {
const currentState = attempt === 1 ? state : await getState();
const password = currentState.password || currentState.customPassword || '';
const oauthUrl = await refreshOAuthUrlBeforeStep6(currentState);
if (attempt === 1) {
await addLog('步骤 6:正在打开最新 OAuth 链接并登录...');
} else {
await addLog(`步骤 6:上一轮登录未进入验证码页,正在重新发起第 ${attempt} 轮登录尝试...`, 'warn');
}
await reuseOrCreateTab('signup-page', oauthUrl);
const result = await sendToContentScriptResilient(
'signup-page',
{
type: 'EXECUTE_STEP',
step: 6,
source: 'background',
payload: {
email: currentState.email,
password,
},
},
{
timeoutMs: 180000,
retryDelayMs: 700,
logMessage: '步骤 6:认证页正在切换,等待页面重新就绪后继续登录...',
if (attempt === 1) {
await addLog('步骤 6:正在打开最新 OAuth 链接并登录...');
} else {
await addLog(`步骤 6:上一轮失败后,正在进行第 ${attempt} 次尝试(最多 ${STEP6_MAX_ATTEMPTS} 次)...`, 'warn');
}
);
if (result?.error) {
throw new Error(result.error);
await reuseOrCreateTab('signup-page', oauthUrl);
const result = await sendToContentScriptResilient(
'signup-page',
{
type: 'EXECUTE_STEP',
step: 6,
source: 'background',
payload: {
email: currentState.email,
password,
},
},
{
timeoutMs: 180000,
retryDelayMs: 700,
logMessage: '步骤 6:认证页正在切换,等待页面重新就绪后继续登录...',
}
);
if (result?.error) {
throw new Error(result.error);
}
if (isStep6SuccessResult(result)) {
await completeStepFromBackground(6, {
loginVerificationRequestedAt: result.loginVerificationRequestedAt || null,
});
return;
}
if (isStep6RecoverableResult(result)) {
const reasonMessage = result.message
|| `当前停留在${getLoginAuthStateLabel(result.state)},准备重新执行步骤 6。`;
throw new Error(reasonMessage);
}
throw new Error('步骤 6:认证页未返回可识别的登录结果。');
} catch (err) {
throwIfStopped(err);
lastError = err;
if (attempt >= STEP6_MAX_ATTEMPTS) {
break;
}
await addLog(`步骤 6:第 ${attempt} 次尝试失败,原因:${getErrorMessage(err)};准备重试...`, 'warn');
}
if (isStep6SuccessResult(result)) {
await completeStepFromBackground(6, {
loginVerificationRequestedAt: result.loginVerificationRequestedAt || null,
});
return;
}
if (isStep6RecoverableResult(result)) {
const reasonMessage = result.message
|| `当前停留在${getLoginAuthStateLabel(result.state)},准备重新执行步骤 6。`;
await addLog(`步骤 6${reasonMessage}`, 'warn');
continue;
}
throw new Error('步骤 6:认证页未返回可识别的登录结果。');
}
throw new Error(`步骤 6:判断失败后已重试 ${STEP6_MAX_ATTEMPTS - 1} 次,仍未成功。最后原因:${getErrorMessage(lastError)}`);
}
return { executeStep6 };