diff --git a/background.js b/background.js index 6916f41..d37e583 100644 --- a/background.js +++ b/background.js @@ -3668,6 +3668,22 @@ function isIcloudTransientContextError(error) { let lastIcloudLoginPromptAt = 0; const activeIcloudRequestControllers = new Set(); +let lastResolvedIcloudServiceUrl = ''; +const icloudTransientLogThrottle = new Map(); + +function shouldEmitIcloudTransientLog(key, windowMs = 1500) { + const normalizedKey = String(key || '').trim(); + if (!normalizedKey) { + return true; + } + const now = Date.now(); + const lastAt = Number(icloudTransientLogThrottle.get(normalizedKey) || 0); + if (now - lastAt < Math.max(200, Number(windowMs) || 1500)) { + return false; + } + icloudTransientLogThrottle.set(normalizedKey, now); + return true; +} async function openIcloudLoginPage(preferredUrl) { const tabs = await chrome.tabs.query({ @@ -3740,11 +3756,15 @@ async function withIcloudLoginHelp(actionLabel, action) { } if (isIcloudTransientContextError(err)) { if (attempt < maxTransientAttempts) { - await addLog(`iCloud:${actionLabel}受网络/上下文波动影响,正在重试(${attempt}/${maxTransientAttempts})...`, 'warn'); + if (shouldEmitIcloudTransientLog(`${actionLabel}:retry:${attempt}/${maxTransientAttempts}`)) { + await addLog(`iCloud:${actionLabel}受网络/上下文波动影响,正在重试(${attempt}/${maxTransientAttempts})...`, 'warn'); + } await new Promise((resolve) => setTimeout(resolve, retryDelayMs * attempt)); continue; } - await addLog(`iCloud:${actionLabel}受网络/上下文波动影响:${getErrorMessage(err)}`, 'warn'); + if (shouldEmitIcloudTransientLog(`${actionLabel}:final`)) { + await addLog(`iCloud:${actionLabel}受网络/上下文波动影响:${getErrorMessage(err)}`, 'warn'); + } const transientError = new Error('iCloud 别名加载受网络/上下文波动影响,请稍后重试。'); transientError.code = 'ICLOUD_TRANSIENT_CONTEXT'; transientError.cause = err; @@ -3796,6 +3816,14 @@ function normalizeIcloudServiceUrl(rawUrl = '') { } } +function rememberIcloudServiceUrl(rawUrl = '') { + const normalized = normalizeIcloudServiceUrl(rawUrl); + if (normalized) { + lastResolvedIcloudServiceUrl = normalized; + } + return normalized; +} + function isIcloudMaildomainwsHost(rawHost = '') { const host = String(rawHost || '').trim().toLowerCase().replace(/\.$/, ''); if (!host) { @@ -3932,10 +3960,10 @@ async function icloudRequestViaPageContext(method, url, options = {}) { let tabs = await chrome.tabs.query({ url: ICLOUD_TAB_URL_PATTERNS, }); + tabs = await ensureIcloudMailContextTab(tabs, targetHost, preferredHost); if (!tabs.length) { throw new Error('page_context:no_icloud_tab'); } - tabs = await ensureIcloudMailContextTab(tabs, targetHost, preferredHost); const sortedTabs = [...tabs].sort((left, right) => { const score = (tab) => { @@ -4186,11 +4214,14 @@ async function icloudRequest(method, url, options = {}) { contentType: requestContentType || undefined, }); } catch (pageContextError) { - const mergedError = new Error(`${directErrorMessage} | page_context:${getErrorMessage(pageContextError)}`); - if (requestError?.status) { - mergedError.status = requestError.status; + const pageContextMessage = getErrorMessage(pageContextError); + if (!pageContextMessage.includes('page_context:no_icloud_tab')) { + const mergedError = new Error(`${directErrorMessage} | page_context:${pageContextMessage}`); + if (requestError?.status) { + mergedError.status = requestError.status; + } + requestError = mergedError; } - requestError = mergedError; } } @@ -4314,16 +4345,14 @@ async function resolveIcloudPremiumMailServiceViaPageContext(setupUrls, state) { }); } catch (err) { errors.push(`page_context:query_tabs:${getErrorMessage(err)}`); - return { service: null, errors }; - } - - if (!tabs.length) { - errors.push('page_context:no_icloud_tab'); - return { service: null, errors }; + return { service: null, errors, noTab: false }; } const preferredHost = normalizeIcloudHost(state?.preferredIcloudHost); tabs = await ensureIcloudMailContextTab(tabs, preferredHost, preferredHost); + if (!tabs.length) { + return { service: null, errors: [], noTab: true }; + } const sortedTabs = [...tabs].sort((left, right) => { const leftActive = left?.active ? 1 : 0; const rightActive = right?.active ? 1 : 0; @@ -4354,7 +4383,7 @@ async function resolveIcloudPremiumMailServiceViaPageContext(setupUrls, state) { } } - return { service: null, errors }; + return { service: null, errors, noTab: false }; } async function resolveIcloudPremiumMailService(options = {}) { @@ -4377,7 +4406,7 @@ async function resolveIcloudPremiumMailService(options = {}) { } return { setupUrl, - serviceUrl: normalizeIcloudServiceUrl(data.webservices.premiummailsettings.url), + serviceUrl: rememberIcloudServiceUrl(data.webservices.premiummailsettings.url), }; } catch (err) { errors.push(`${new URL(setupUrl).host}: ${getErrorMessage(err)}`); @@ -4385,16 +4414,23 @@ async function resolveIcloudPremiumMailService(options = {}) { } if (shouldTryIcloudPageContextFallback(errors)) { - const { service, errors: pageContextErrors } = await resolveIcloudPremiumMailServiceViaPageContext(setupUrls, state); + const { + service, + errors: pageContextErrors, + noTab: pageContextNoTab = false, + } = await resolveIcloudPremiumMailServiceViaPageContext(setupUrls, state); if (service) { const preferredIcloudHost = normalizeIcloudHost(new URL(service.setupUrl).host); if (preferredIcloudHost && preferredIcloudHost !== normalizeIcloudHost(state.preferredIcloudHost)) { await setState({ preferredIcloudHost }); } await addLog(`iCloud:后台会话校验失败,已切换页面上下文校验(${new URL(service.setupUrl).host})。`, 'warn'); - return service; + return { + ...service, + serviceUrl: rememberIcloudServiceUrl(service.serviceUrl), + }; } - if (Array.isArray(pageContextErrors) && pageContextErrors.length) { + if (!pageContextNoTab && Array.isArray(pageContextErrors) && pageContextErrors.length) { errors.push(...pageContextErrors); } } @@ -4531,7 +4567,18 @@ async function deleteIcloudAlias(payload) { throw new Error(`缺少 ${alias.email} 的 anonymousId,请先刷新 iCloud 别名列表。`); } - const { serviceUrl } = await resolveIcloudPremiumMailService(); + let serviceUrl = ''; + try { + ({ serviceUrl } = await resolveIcloudPremiumMailService()); + } catch (resolveErr) { + const canFallbackToCachedService = isIcloudTransientContextError(resolveErr) + && Boolean(lastResolvedIcloudServiceUrl); + if (!canFallbackToCachedService) { + throw resolveErr; + } + serviceUrl = lastResolvedIcloudServiceUrl; + await addLog(`iCloud:会话校验暂时不可用,已回退最近可用服务节点 ${new URL(serviceUrl).host} 继续删除。`, 'warn'); + } try { const directDelete = await icloudRequest('POST', `${serviceUrl}/v1/hme/delete`, { @@ -4809,7 +4856,11 @@ async function finalizeIcloudAliasAfterSuccessfulFlow(state) { await addLog(`iCloud:流程成功后已自动删除 ${email}。`, 'ok'); return { handled: true, deleted: true }; } catch (err) { - await addLog(`iCloud:自动删除 ${email} 失败:${getErrorMessage(err)}`, 'warn'); + if (isIcloudTransientContextError(err)) { + await addLog(`iCloud:自动删除 ${email} 暂时跳过(网络/上下文波动),可稍后手动删除。`, 'info'); + } else { + await addLog(`iCloud:自动删除 ${email} 失败:${getErrorMessage(err)}`, 'warn'); + } return { handled: true, deleted: false }; } }