feat: 增强注册和登录验证码流程,添加步骤标识和日志标签,优化 OAuth 刷新逻辑
This commit is contained in:
@@ -167,7 +167,11 @@
|
||||
type: 'PREPARE_SIGNUP_VERIFICATION',
|
||||
step,
|
||||
source: 'background',
|
||||
payload: { password: password || '' },
|
||||
payload: {
|
||||
password: password || '',
|
||||
prepareSource: 'step3_finalize',
|
||||
prepareLogLabel: '步骤 3 收尾',
|
||||
},
|
||||
}, {
|
||||
timeoutMs: 30000,
|
||||
retryDelayMs: 700,
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
executeStep7,
|
||||
getOAuthFlowRemainingMs,
|
||||
getOAuthFlowStepTimeoutMs,
|
||||
getPanelMode,
|
||||
getMailConfig,
|
||||
getState,
|
||||
getTabId,
|
||||
@@ -104,9 +103,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
const shouldRefreshOAuthBeforeSubmit = getPanelMode(state) === 'cpa';
|
||||
let step7ReplayCompleted = false;
|
||||
|
||||
await resolveVerificationStep(8, state, mail, {
|
||||
filterAfterTimestamp: stepStartedAt,
|
||||
getRemainingTimeMs: getStep8RemainingTimeResolver(),
|
||||
@@ -114,22 +110,6 @@
|
||||
resendIntervalMs: (mail.provider === HOTMAIL_PROVIDER || mail.provider === '2925')
|
||||
? 0
|
||||
: STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS,
|
||||
beforeSubmit: shouldRefreshOAuthBeforeSubmit ? async (result) => {
|
||||
if (step7ReplayCompleted) {
|
||||
return;
|
||||
}
|
||||
|
||||
step7ReplayCompleted = true;
|
||||
await addLog(`步骤 8:已拿到登录验证码 ${result.code},先刷新 CPA OAuth 链接并重走步骤 7,再回填验证码。`, 'warn');
|
||||
await rerunStep7ForStep8Recovery({
|
||||
logMessage: '步骤 8:正在重新获取最新 CPA OAuth 链接,并快速重走步骤 7...',
|
||||
postStepDelayMs: 1200,
|
||||
});
|
||||
await ensureStep8VerificationPageReady({
|
||||
timeoutMs: await getStep8ReadyTimeoutMs('重放步骤 7 后重新确认登录验证码页'),
|
||||
});
|
||||
await addLog('步骤 8:登录验证码页面已重新就绪,开始回填刚才获取到的验证码。', 'info');
|
||||
} : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,11 @@
|
||||
type: 'PREPARE_SIGNUP_VERIFICATION',
|
||||
step: 4,
|
||||
source: 'background',
|
||||
payload: { password: state.password || state.customPassword || '' },
|
||||
payload: {
|
||||
password: state.password || state.customPassword || '',
|
||||
prepareSource: 'step4_execute',
|
||||
prepareLogLabel: '步骤 4 执行',
|
||||
},
|
||||
},
|
||||
{
|
||||
timeoutMs: 30000,
|
||||
|
||||
+13
-10
@@ -1355,6 +1355,9 @@ async function waitForSignupVerificationTransition(timeout = 5000) {
|
||||
|
||||
async function prepareSignupVerificationFlow(payload = {}, timeout = 30000) {
|
||||
const { password } = payload;
|
||||
const prepareSource = String(payload?.prepareSource || '').trim() || 'step4_execute';
|
||||
const prepareLogLabel = String(payload?.prepareLogLabel || '').trim()
|
||||
|| (prepareSource === 'step3_finalize' ? '步骤 3 收尾' : '步骤 4 执行');
|
||||
const start = Date.now();
|
||||
let recoveryRound = 0;
|
||||
const maxRecoveryRounds = 3;
|
||||
@@ -1363,17 +1366,17 @@ async function prepareSignupVerificationFlow(payload = {}, timeout = 30000) {
|
||||
throwIfStopped();
|
||||
|
||||
const roundNo = recoveryRound + 1;
|
||||
log(`步骤 4:等待页面进入验证码阶段(第 ${roundNo}/${maxRecoveryRounds} 轮,先等待 5 秒)...`, 'info');
|
||||
log(`${prepareLogLabel}:等待页面进入验证码阶段(第 ${roundNo}/${maxRecoveryRounds} 轮,先等待 5 秒)...`, 'info');
|
||||
const snapshot = await waitForSignupVerificationTransition(5000);
|
||||
|
||||
if (snapshot.state === 'step5') {
|
||||
log('步骤 4:页面已进入验证码后的下一阶段,本步骤按已完成处理。', 'ok');
|
||||
return { ready: true, alreadyVerified: true, retried: recoveryRound };
|
||||
log(`${prepareLogLabel}:页面已进入验证码后的下一阶段,本步骤按已完成处理。`, 'ok');
|
||||
return { ready: true, alreadyVerified: true, retried: recoveryRound, prepareSource };
|
||||
}
|
||||
|
||||
if (snapshot.state === 'verification') {
|
||||
log(`步骤 4:验证码页面已就绪${recoveryRound ? `(期间自动恢复 ${recoveryRound} 次)` : ''}。`, 'ok');
|
||||
return { ready: true, retried: recoveryRound };
|
||||
log(`${prepareLogLabel}:验证码页面已就绪${recoveryRound ? `(期间自动恢复 ${recoveryRound} 次)` : ''}。`, 'ok');
|
||||
return { ready: true, retried: recoveryRound, prepareSource };
|
||||
}
|
||||
|
||||
if (snapshot.state === 'email_exists') {
|
||||
@@ -1385,7 +1388,7 @@ async function prepareSignupVerificationFlow(payload = {}, timeout = 30000) {
|
||||
if (snapshot.state === 'error') {
|
||||
await recoverCurrentAuthRetryPage({
|
||||
flow: 'signup_password',
|
||||
logLabel: `步骤 4:检测到密码页超时报错,正在点击“重试”恢复(第 ${recoveryRound}/${maxRecoveryRounds} 次)`,
|
||||
logLabel: `${prepareLogLabel}:检测到密码页超时报错,正在点击“重试”恢复(第 ${recoveryRound}/${maxRecoveryRounds} 次)`,
|
||||
step: 4,
|
||||
timeoutMs: 12000,
|
||||
});
|
||||
@@ -1398,24 +1401,24 @@ async function prepareSignupVerificationFlow(payload = {}, timeout = 30000) {
|
||||
}
|
||||
|
||||
if ((snapshot.passwordInput.value || '') !== password) {
|
||||
log('步骤 4:页面仍停留在密码页,正在重新填写密码...', 'warn');
|
||||
log(`${prepareLogLabel}:页面仍停留在密码页,正在重新填写密码...`, 'warn');
|
||||
await humanPause(450, 1100);
|
||||
fillInput(snapshot.passwordInput, password);
|
||||
}
|
||||
|
||||
if (snapshot.submitButton && isActionEnabled(snapshot.submitButton)) {
|
||||
log(`步骤 4:页面仍停留在密码页,正在重新点击“继续”(第 ${recoveryRound}/${maxRecoveryRounds} 次)...`, 'warn');
|
||||
log(`${prepareLogLabel}:页面仍停留在密码页,正在重新点击“继续”(第 ${recoveryRound}/${maxRecoveryRounds} 次)...`, 'warn');
|
||||
await humanPause(350, 900);
|
||||
simulateClick(snapshot.submitButton);
|
||||
await sleep(1200);
|
||||
continue;
|
||||
}
|
||||
|
||||
log(`步骤 4:页面仍停留在密码页,但“继续”按钮暂不可用,准备继续等待(${recoveryRound}/${maxRecoveryRounds})...`, 'warn');
|
||||
log(`${prepareLogLabel}:页面仍停留在密码页,但“继续”按钮暂不可用,准备继续等待(${recoveryRound}/${maxRecoveryRounds})...`, 'warn');
|
||||
continue;
|
||||
}
|
||||
|
||||
log(`步骤 4:页面仍在切换中,准备继续等待(${recoveryRound}/${maxRecoveryRounds})...`, 'warn');
|
||||
log(`${prepareLogLabel}:页面仍在切换中,准备继续等待(${recoveryRound}/${maxRecoveryRounds})...`, 'warn');
|
||||
}
|
||||
|
||||
throw new Error(`等待注册验证码页面就绪超时或自动恢复失败(已尝试 ${recoveryRound}/${maxRecoveryRounds} 轮)。URL: ${location.href}`);
|
||||
|
||||
@@ -210,6 +210,10 @@ test('signup flow helper finalizes step 3 submit by reusing signup verification
|
||||
type: 'PREPARE_SIGNUP_VERIFICATION',
|
||||
step: 3,
|
||||
source: 'background',
|
||||
payload: { password: 'Secret123!' },
|
||||
payload: {
|
||||
password: 'Secret123!',
|
||||
prepareSource: 'step3_finalize',
|
||||
prepareLogLabel: '步骤 3 收尾',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@ const source = fs.readFileSync('background/steps/fetch-login-code.js', 'utf8');
|
||||
const globalScope = {};
|
||||
const api = new Function('self', `${source}; return self.MultiPageBackgroundStep8;`)(globalScope);
|
||||
|
||||
test('step 8 refreshes CPA oauth via step 7 replay before submitting verification code', async () => {
|
||||
test('step 8 submits login verification directly without replaying step 7', async () => {
|
||||
const calls = {
|
||||
ensureReady: 0,
|
||||
ensureReadyOptions: [],
|
||||
@@ -43,7 +43,6 @@ test('step 8 refreshes CPA oauth via step 7 replay before submitting verificatio
|
||||
url: 'https://mail.qq.com',
|
||||
navigateOnReuse: false,
|
||||
}),
|
||||
getPanelMode: () => 'cpa',
|
||||
getState: async () => ({ email: 'user@example.com', password: 'secret' }),
|
||||
getTabId: async (sourceName) => (sourceName === 'signup-page' ? 1 : 2),
|
||||
HOTMAIL_PROVIDER: 'hotmail-api',
|
||||
@@ -52,7 +51,6 @@ test('step 8 refreshes CPA oauth via step 7 replay before submitting verificatio
|
||||
LUCKMAIL_PROVIDER: 'luckmail-api',
|
||||
resolveVerificationStep: async (_step, _state, _mail, options) => {
|
||||
calls.resolveOptions = options;
|
||||
await options.beforeSubmit({ code: '654321' });
|
||||
},
|
||||
reuseOrCreateTab: async () => {},
|
||||
setState: async () => {},
|
||||
@@ -77,17 +75,16 @@ test('step 8 refreshes CPA oauth via step 7 replay before submitting verificatio
|
||||
Date.now = realDateNow;
|
||||
}
|
||||
|
||||
assert.equal(typeof calls.resolveOptions.beforeSubmit, 'function');
|
||||
assert.equal(calls.ensureReady, 2);
|
||||
assert.equal(calls.executeStep7, 1);
|
||||
assert.deepStrictEqual(calls.sleep, [1200]);
|
||||
assert.equal(calls.resolveOptions.beforeSubmit, undefined);
|
||||
assert.equal(calls.ensureReady, 1);
|
||||
assert.equal(calls.executeStep7, 0);
|
||||
assert.deepStrictEqual(calls.sleep, []);
|
||||
assert.equal(calls.resolveOptions.filterAfterTimestamp, 123456);
|
||||
assert.equal(typeof calls.resolveOptions.getRemainingTimeMs, 'function');
|
||||
assert.equal(await calls.resolveOptions.getRemainingTimeMs({ actionLabel: '登录验证码流程' }), 5000);
|
||||
assert.equal(calls.resolveOptions.resendIntervalMs, 25000);
|
||||
assert.deepStrictEqual(calls.ensureReadyOptions, [
|
||||
{ timeoutMs: 5000 },
|
||||
{ timeoutMs: 5000 },
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -114,7 +111,6 @@ test('step 8 disables resend interval for 2925 mailbox polling', async () => {
|
||||
url: 'https://2925.com',
|
||||
navigateOnReuse: false,
|
||||
}),
|
||||
getPanelMode: () => 'sub2api',
|
||||
getState: async () => ({ email: 'user@example.com', password: 'secret' }),
|
||||
getTabId: async (sourceName) => (sourceName === 'signup-page' ? 1 : 2),
|
||||
HOTMAIL_PROVIDER: 'hotmail-api',
|
||||
|
||||
+1
-1
@@ -252,7 +252,6 @@
|
||||
- `2925` provider 会拉长单轮轮询窗口,并关闭 Step 4 / 8 的自动重发间隔,减少因邮件延迟过早判负。
|
||||
- Step 4 / 8 会在当前验证码步骤启动时固定一次邮件筛选时间窗;本步后续即使触发重新发送验证码,也继续沿用这一次启动时间作为筛选基准,不再随着重发时间前移。
|
||||
- 自动重新发送验证码次数现在使用 sidepanel 里的单一“验证码重发”配置;普通邮箱仍按 25 秒间隔节流,Hotmail / 2925 不走这个 25 秒间隔。Step 4 若启用先请求新验证码,会先消耗一次当前步骤的自动重发次数。
|
||||
- CPA 模式下,Step 8 在真正回填登录验证码前,会先刷新最新 OAuth 地址并快速重走一次 Step 7,降低验证码等待过久导致 OAuth 过期的概率。
|
||||
|
||||
- Auto 模式下,如果 Step 4 当前轮失败,后台会沿用当前邮箱回到 Step 1 重新开始当前轮,而不是立刻换邮箱开新尝试。
|
||||
|
||||
@@ -310,6 +309,7 @@
|
||||
3. 轮询登录验证码
|
||||
4. 回填登录验证码
|
||||
5. 如遇邮箱轮询类失败,则按有限次数回到 Step 7 重试
|
||||
6. 获取到登录验证码后不再触发“刷新 OAuth 并重走 Step 7”的前置回放,直接在当前验证码页提交并继续进入 Step 9
|
||||
|
||||
### Step 9
|
||||
|
||||
|
||||
+2
-2
@@ -55,7 +55,7 @@
|
||||
|
||||
- `background/steps/clear-login-cookies.js`:步骤 6 实现,负责登录前 Cookie 清理。
|
||||
- `background/steps/confirm-oauth.js`:步骤 9 实现,负责 OAuth 同意页按钮定位、点击、localhost 回调监听与回调完成。
|
||||
- `background/steps/fetch-login-code.js`:步骤 8 实现,负责登录验证码阶段的邮箱轮询、验证码回填与回退控制。
|
||||
- `background/steps/fetch-login-code.js`:步骤 8 实现,负责登录验证码阶段的邮箱轮询、验证码回填与回退控制;验证码获取后直接提交,不再在提交前回放步骤 7 刷新 OAuth。
|
||||
- `background/steps/fetch-signup-code.js`:步骤 4 实现,负责注册验证码阶段的页面准备与验证码流程入口。
|
||||
- `background/steps/fill-password.js`:步骤 3 实现,负责密码生成、保存、回填与提交。
|
||||
- `background/steps/fill-profile.js`:步骤 5 实现,负责姓名、生日填写并把资料提交给注册页内容脚本。
|
||||
@@ -143,7 +143,7 @@
|
||||
- `tests/background-step-modules.test.js`:测试步骤模块文件都已由后台入口加载。
|
||||
- `tests/background-step5-submit-short-circuit.test.js`:测试步骤 5 会把生成好的资料直接转发给内容脚本,并依赖完成信号收尾。
|
||||
- `tests/background-step6-retry-limit.test.js`:测试步骤 6 的 Cookie 清理,以及步骤 7 的有限重试上限。
|
||||
- `tests/background-step7-recovery.test.js`:测试步骤 8 在 CPA 模式下会先回放步骤 7 再提交登录验证码,并为 2925 关闭重发间隔。
|
||||
- `tests/background-step7-recovery.test.js`:测试步骤 8 获取登录验证码后直接提交(不再回放步骤 7),并为 2925 关闭重发间隔。
|
||||
- `tests/background-step-registry.test.js`:测试后台步骤注册表和共享步骤定义已接入。
|
||||
- `tests/background-tab-runtime-module.test.js`:测试标签运行时模块已接入且导出工厂。
|
||||
- `tests/background-verification-flow-module.test.js`:测试验证码流程模块已接入且导出工厂。
|
||||
|
||||
Reference in New Issue
Block a user