From 75e259ae5ce821b2616ce8b6d1faa2431e701c3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B4=E5=9C=A3=E4=BD=91?= Date: Sun, 26 Apr 2026 22:21:58 +0800 Subject: [PATCH] =?UTF-8?q?fix(accounts):=20=E7=BB=9F=E4=B8=80=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=AE=8C=E6=88=90=E5=90=8E=E7=9A=84=E8=B4=A6=E5=8F=B7?= =?UTF-8?q?=E6=A0=87=E8=AE=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 4d2016015f1218c2277ba6bc3447f81a81b234ed) --- background.js | 67 ++++++++++- background/message-router.js | 43 +++++-- tests/background-luckmail.test.js | 78 +++++++++++++ ...ckground-registration-account-used.test.js | 106 ++++++++++++++++++ 4 files changed, 279 insertions(+), 15 deletions(-) create mode 100644 tests/background-registration-account-used.test.js diff --git a/background.js b/background.js index 7f0b1a3..3d76f54 100644 --- a/background.js +++ b/background.js @@ -1500,7 +1500,7 @@ function getCustomEmailPoolEntries(state = {}) { })); } -async function markCurrentCustomEmailPoolEntryUsed(state = {}) { +async function markCurrentCustomEmailPoolEntryUsed(state = {}, options = {}) { if (!isCustomEmailPoolGenerator(state)) { return { updated: false }; } @@ -1551,7 +1551,8 @@ async function markCurrentCustomEmailPoolEntryUsed(state = {}) { customEmailPoolEntries: nextEntries, customEmailPool: nextCustomEmailPool, }); - await addLog(`自定义邮箱池:流程成功后已将 ${currentEmail} 标记为已用。`, 'ok'); + const logPrefix = String(options.logPrefix || '').trim() || '自定义邮箱池:流程成功后'; + await addLog(`${logPrefix}已将 ${currentEmail} 标记为已用。`, options.level || 'ok'); return { updated: true, customEmailPoolEntries: nextEntries, @@ -1559,6 +1560,58 @@ async function markCurrentCustomEmailPoolEntryUsed(state = {}) { }; } +async function markCurrentRegistrationAccountUsed(state = {}, options = {}) { + const providedState = state && typeof state === 'object' ? state : {}; + const currentState = await getState(); + const latestState = { + ...providedState, + ...(currentState && typeof currentState === 'object' ? currentState : {}), + }; + const reasonPrefix = String(options.logPrefix || '').trim() || '当前账号'; + let updated = false; + + if (latestState.currentHotmailAccountId && isHotmailProvider(latestState)) { + await patchHotmailAccount(latestState.currentHotmailAccountId, { + used: true, + lastUsedAt: Date.now(), + }); + await addLog(`${reasonPrefix}:Hotmail 账号已标记为已用。`, options.level || 'warn'); + updated = true; + } + + if (isLuckmailProvider(latestState)) { + const currentPurchase = getCurrentLuckmailPurchase(latestState); + if (currentPurchase?.id) { + await setLuckmailPurchaseUsedState(currentPurchase.id, true); + await clearLuckmailRuntimeState({ clearEmail: true }); + await addLog(`${reasonPrefix}:LuckMail 邮箱 ${currentPurchase.email_address} 已标记为已用。`, options.level || 'warn'); + updated = true; + } + } + + if (String(latestState.mailProvider || '').trim().toLowerCase() === '2925' && latestState.currentMail2925AccountId) { + await patchMail2925Account(latestState.currentMail2925AccountId, { + lastUsedAt: Date.now(), + lastError: '', + }); + await addLog(`${reasonPrefix}:2925 账号已记录最近使用时间。`, options.level || 'warn'); + updated = true; + } + + const icloudResult = await finalizeIcloudAliasAfterSuccessfulFlow(latestState); + updated = Boolean(icloudResult?.handled) || updated; + + if (typeof markCurrentCustomEmailPoolEntryUsed === 'function') { + const result = await markCurrentCustomEmailPoolEntryUsed(latestState, { + logPrefix: `${reasonPrefix}:自定义邮箱池`, + level: options.level || 'warn', + }); + updated = Boolean(result?.updated) || updated; + } + + return { updated }; +} + function getCustomEmailPoolEmailForRun(state = {}, targetRun = 1) { const entries = getCustomEmailPool(state); const numericRun = Math.max(1, Math.floor(Number(targetRun) || 1)); @@ -7773,11 +7826,18 @@ async function handleStepData(step, payload) { broadcastDataUpdate({ localhostUrl: payload.localhostUrl }); } break; - case 10: { + case 10: + case 13: { if (payload.localhostUrl) { await closeLocalhostCallbackTabs(payload.localhostUrl); } const latestState = await getState(); + const lastStepId = typeof getLastStepIdForState === 'function' + ? getLastStepIdForState(latestState) + : 10; + if (Number(step) !== Number(lastStepId)) { + break; + } if (latestState.currentHotmailAccountId && isHotmailProvider(latestState)) { await patchHotmailAccount(latestState.currentHotmailAccountId, { used: true, @@ -9913,6 +9973,7 @@ const messageRouter = self.MultiPageBackgroundMessageRouter?.createMessageRouter listIcloudAliases, listLuckmailPurchasesForManagement, markCurrentCustomEmailPoolEntryUsed, + markCurrentRegistrationAccountUsed, getCurrentMail2925Account, normalizeHotmailAccounts, normalizeMail2925Accounts, diff --git a/background/message-router.js b/background/message-router.js index bc1462f..6497faa 100644 --- a/background/message-router.js +++ b/background/message-router.js @@ -67,6 +67,7 @@ listIcloudAliases, listLuckmailPurchasesForManagement, markCurrentCustomEmailPoolEntryUsed, + markCurrentRegistrationAccountUsed, normalizeHotmailAccounts, normalizeMail2925Accounts, normalizePayPalAccounts, @@ -183,21 +184,26 @@ await closeLocalhostCallbackTabs(payload.localhostUrl); } const latestState = await getState(); - if (latestState.currentHotmailAccountId && isHotmailProvider(latestState)) { + if (typeof markCurrentRegistrationAccountUsed === 'function') { + await markCurrentRegistrationAccountUsed(latestState, { + logPrefix: '流程完成', + level: 'ok', + }); + } else if (latestState.currentHotmailAccountId && isHotmailProvider(latestState)) { await patchHotmailAccount(latestState.currentHotmailAccountId, { used: true, lastUsedAt: Date.now(), }); await addLog('当前 Hotmail 账号已自动标记为已用。', 'ok'); } - if (String(latestState.mailProvider || '').trim().toLowerCase() === '2925' && latestState.currentMail2925AccountId) { + if (typeof markCurrentRegistrationAccountUsed !== 'function' && String(latestState.mailProvider || '').trim().toLowerCase() === '2925' && latestState.currentMail2925AccountId) { await patchMail2925Account(latestState.currentMail2925AccountId, { lastUsedAt: Date.now(), lastError: '', }); await addLog('当前 2925 账号已记录最近使用时间。', 'ok'); } - if (isLuckmailProvider(latestState)) { + if (typeof markCurrentRegistrationAccountUsed !== 'function' && isLuckmailProvider(latestState)) { const currentPurchase = getCurrentLuckmailPurchase(latestState); if (currentPurchase?.id) { await setLuckmailPurchaseUsedState(currentPurchase.id, true); @@ -213,9 +219,8 @@ excludeLocalhostCallbacks: true, }); } - await finalizeIcloudAliasAfterSuccessfulFlow(latestState); - if (typeof finalizePhoneActivationAfterSuccessfulFlow === 'function') { - await finalizePhoneActivationAfterSuccessfulFlow(latestState); + if (typeof markCurrentRegistrationAccountUsed !== 'function') { + await finalizeIcloudAliasAfterSuccessfulFlow(latestState); } } @@ -354,26 +359,38 @@ broadcastDataUpdate({ localhostUrl: payload.localhostUrl }); } break; - case 10: { + case 10: + case 13: { if (payload.localhostUrl) { await closeLocalhostCallbackTabs(payload.localhostUrl); } const latestState = await getState(); - if (latestState.currentHotmailAccountId && isHotmailProvider(latestState)) { + const lastStepId = typeof getLastStepIdForState === 'function' + ? getLastStepIdForState(latestState) + : 10; + if (Number(step) !== Number(lastStepId)) { + break; + } + if (typeof markCurrentRegistrationAccountUsed === 'function') { + await markCurrentRegistrationAccountUsed(latestState, { + logPrefix: '流程完成', + level: 'ok', + }); + } else if (latestState.currentHotmailAccountId && isHotmailProvider(latestState)) { await patchHotmailAccount(latestState.currentHotmailAccountId, { used: true, lastUsedAt: Date.now(), }); await addLog('当前 Hotmail 账号已自动标记为已用。', 'ok'); } - if (String(latestState.mailProvider || '').trim().toLowerCase() === '2925' && latestState.currentMail2925AccountId) { + if (typeof markCurrentRegistrationAccountUsed !== 'function' && String(latestState.mailProvider || '').trim().toLowerCase() === '2925' && latestState.currentMail2925AccountId) { await patchMail2925Account(latestState.currentMail2925AccountId, { lastUsedAt: Date.now(), lastError: '', }); await addLog('当前 2925 账号已记录最近使用时间。', 'ok'); } - if (isLuckmailProvider(latestState)) { + if (typeof markCurrentRegistrationAccountUsed !== 'function' && isLuckmailProvider(latestState)) { const currentPurchase = getCurrentLuckmailPurchase(latestState); if (currentPurchase?.id) { await setLuckmailPurchaseUsedState(currentPurchase.id, true); @@ -389,8 +406,10 @@ excludeLocalhostCallbacks: true, }); } - await finalizeIcloudAliasAfterSuccessfulFlow(latestState); - if (typeof markCurrentCustomEmailPoolEntryUsed === 'function') { + if (typeof markCurrentRegistrationAccountUsed !== 'function') { + await finalizeIcloudAliasAfterSuccessfulFlow(latestState); + } + if (typeof markCurrentRegistrationAccountUsed !== 'function' && typeof markCurrentCustomEmailPoolEntryUsed === 'function') { await markCurrentCustomEmailPoolEntryUsed(latestState); } break; diff --git a/tests/background-luckmail.test.js b/tests/background-luckmail.test.js index bccf0c8..76ec930 100644 --- a/tests/background-luckmail.test.js +++ b/tests/background-luckmail.test.js @@ -801,3 +801,81 @@ return { assert.deepStrictEqual(snapshot.clearedOptions, { clearEmail: true }); assert.equal(snapshot.logs.at(-1).message, '当前 LuckMail 邮箱运行态已清空,下轮将优先复用未用邮箱或重新购买邮箱。'); }); + +test('handleStepData marks current LuckMail purchase as used on Plus final step 13', async () => { + const bundle = extractFunction('handleStepData'); + + const factory = new Function(` +let usedMarker = null; +const logs = []; + +async function closeLocalhostCallbackTabs() {} +async function getState() { + return { + plusModeEnabled: true, + mailProvider: 'luckmail-api', + currentHotmailAccountId: null, + currentLuckmailPurchase: { + id: 456, + email_address: 'plus@outlook.com', + }, + email: 'plus@outlook.com', + }; +} +function getLastStepIdForState(state) { + return state.plusModeEnabled ? 13 : 10; +} +function getCurrentLuckmailPurchase(state) { + return state.currentLuckmailPurchase; +} +function isHotmailProvider() { + return false; +} +async function patchHotmailAccount() {} +function isLuckmailProvider(state) { + return state.mailProvider === 'luckmail-api'; +} +async function setLuckmailPurchaseUsedState(purchaseId, used) { + usedMarker = { purchaseId, used }; +} +async function clearLuckmailRuntimeState() {} +async function addLog(message, level) { + logs.push({ message, level }); +} +function buildLocalhostCleanupPrefix() { + return ''; +} +async function closeTabsByUrlPrefix() {} +function shouldUseCustomRegistrationEmail() { + return false; +} +async function setEmailStateSilently() {} +async function setState() {} +function broadcastDataUpdate() {} +function isLocalhostOAuthCallbackUrl() { + return true; +} +async function finalizeIcloudAliasAfterSuccessfulFlow() {} + +${bundle} + +return { + handleStepData, + snapshot() { + return { usedMarker, logs }; + }, +}; +`); + + const api = factory(); + await api.handleStepData(10, {}); + assert.equal(api.snapshot().usedMarker, null); + + await api.handleStepData(13, { + localhostUrl: 'http://localhost:1455/auth/callback?code=abc&state=xyz', + }); + + const snapshot = api.snapshot(); + assert.deepStrictEqual(snapshot.usedMarker, { purchaseId: 456, used: true }); + assert.equal(snapshot.logs.some((entry) => /已在本地标记为已用/.test(entry.message)), true); +}); diff --git a/tests/background-registration-account-used.test.js b/tests/background-registration-account-used.test.js new file mode 100644 index 0000000..96833f5 --- /dev/null +++ b/tests/background-registration-account-used.test.js @@ -0,0 +1,106 @@ +const test = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('node:fs'); + +const source = fs.readFileSync('background.js', 'utf8'); + +function extractFunction(name) { + const markers = [`async function ${name}(`, `function ${name}(`]; + const start = markers + .map((marker) => source.indexOf(marker)) + .find((index) => index >= 0); + if (start < 0) { + throw new Error(`missing function ${name}`); + } + + let parenDepth = 0; + let signatureEnded = false; + let braceStart = -1; + for (let i = start; i < source.length; i += 1) { + const ch = source[i]; + if (ch === '(') { + parenDepth += 1; + } else if (ch === ')') { + parenDepth -= 1; + if (parenDepth === 0) { + signatureEnded = true; + } + } else if (ch === '{' && signatureEnded) { + braceStart = i; + break; + } + } + if (braceStart < 0) { + throw new Error(`missing body for function ${name}`); + } + + let depth = 0; + let end = braceStart; + for (; end < source.length; end += 1) { + const ch = source[end]; + if (ch === '{') depth += 1; + if (ch === '}') { + depth -= 1; + if (depth === 0) { + end += 1; + break; + } + } + } + + return source.slice(start, end); +} + +test('markCurrentRegistrationAccountUsed uses fresh state when checkout passes stale state', async () => { + const bundle = extractFunction('markCurrentRegistrationAccountUsed'); + const factory = new Function(` +const patchCalls = []; +const logs = []; +async function getState() { + return { + mailProvider: 'hotmail', + currentHotmailAccountId: 'hot-1', + email: 'fresh@example.com', + }; +} +function isHotmailProvider(state) { + return String(state.mailProvider || '').toLowerCase() === 'hotmail'; +} +function isLuckmailProvider() { + return false; +} +function getCurrentLuckmailPurchase() { + return null; +} +async function patchHotmailAccount(id, updates) { + patchCalls.push({ id, updates }); +} +async function setLuckmailPurchaseUsedState() {} +async function clearLuckmailRuntimeState() {} +async function patchMail2925Account() {} +async function finalizeIcloudAliasAfterSuccessfulFlow() { + return { handled: false }; +} +async function markCurrentCustomEmailPoolEntryUsed() { + return { updated: false }; +} +async function addLog(message, level) { + logs.push({ message, level }); +} + +${bundle} + +return { markCurrentRegistrationAccountUsed, patchCalls, logs }; +`); + const api = factory(); + + const result = await api.markCurrentRegistrationAccountUsed({ email: 'stale@example.com' }, { + logPrefix: 'Plus Checkout:当前账号没有免费试用资格', + }); + + assert.equal(result.updated, true); + assert.equal(api.patchCalls.length, 1); + assert.equal(api.patchCalls[0].id, 'hot-1'); + assert.equal(api.patchCalls[0].updates.used, true); + assert.equal(api.logs.some((entry) => /Hotmail 账号已标记为已用/.test(entry.message)), true); +});