diff --git a/src/lib/server/cloudflare-audience.test.ts b/src/lib/server/cloudflare-audience.test.ts index 1cc4997..4166f14 100644 --- a/src/lib/server/cloudflare-audience.test.ts +++ b/src/lib/server/cloudflare-audience.test.ts @@ -99,7 +99,32 @@ describe('Web Analytics 站点解析与统计', () => { it('站点列表返回失败状态时明确报错', async () => { mockAnalytics(() => Response.json({ success: false, result: [] })); const { audience } = await fetchAnalytics(platform, undefined, siteToken); - expect(audience?.error).toContain('站点列表不可用'); + expect(audience?.error).toContain('站点解析失败'); + expect(audience?.error).toContain('未返回具体错误说明'); + }); + + it('保留 Cloudflare 返回的错误码和说明,避免将所有 403 都归因为缺少权限', async () => { + mockAnalytics(() => + Response.json( + { + success: false, + errors: [{ code: 10000, message: 'Authentication error' }] + }, + { status: 403 } + ) + ); + const { audience } = await fetchAnalytics(platform, undefined, siteToken); + expect(audience?.error).toContain('HTTP 403'); + expect(audience?.error).toContain('10000: Authentication error'); + expect(audience?.pageViews).toBeNull(); + }); + + it('非 JSON 错误响应仍保留 HTTP 状态', async () => { + mockAnalytics(() => new Response('Forbidden', { status: 403 })); + const { audience } = await fetchAnalytics(platform, undefined, siteToken); + expect(audience?.error).toContain('HTTP 403'); + expect(audience?.error).toContain('未返回具体错误说明'); + expect(audience?.error).not.toContain(''); }); it('网络或超时异常只影响访问统计,不导致整个统计 Promise 失败', async () => { diff --git a/src/lib/server/cloudflare.ts b/src/lib/server/cloudflare.ts index 6ecf611..71a6c34 100644 --- a/src/lib/server/cloudflare.ts +++ b/src/lib/server/cloudflare.ts @@ -51,18 +51,21 @@ async function getSiteTagByToken( signal } ); - if (!response.ok) { - throw new Error( - `Web Analytics 站点解析失败(HTTP ${response.status}),请检查 CF_ACCOUNT_ID 和 CF_API_TOKEN 的 Account Settings Read 权限` - ); - } - const data = (await response.json()) as { - success: boolean; + // 错误响应也包含诊断信息;代理返回非 JSON 时仍保留 HTTP 状态。 + const data = (await response.json().catch(() => null)) as { + success?: boolean; result?: { site_tag: string; site_token: string }[]; result_info?: { total_pages?: number }; - }; - if (!data.success || !Array.isArray(data.result)) { - throw new Error('Web Analytics 站点列表不可用,请检查账户和 API Token 权限'); + errors?: { code?: number; message?: string }[]; + } | null; + if (!response.ok || !data?.success || !Array.isArray(data.result)) { + const details = data?.errors + ?.map((error) => [error.code, error.message].filter((value) => value != null).join(': ')) + .filter(Boolean) + .join('; '); + throw new Error( + `Web Analytics 站点解析失败(HTTP ${response.status})${details ? `:${details}` : ':Cloudflare 未返回具体错误说明'}。请核对当前部署的 CF_API_TOKEN、CF_ACCOUNT_ID、账户读取权限及 Token 的 IP/有效期限制。` + ); } const matched = data.result.find( (site) => site.site_token === siteToken || site.site_tag === siteToken