From e2d9481b95b18635c2effb392c62427df1fb8ddc Mon Sep 17 00:00:00 2001 From: Cita Date: Wed, 1 Apr 2026 16:53:01 +0800 Subject: [PATCH] fix anyrouter balance refresh challenge errors (#368) --- src/server/services/platforms/newApi.test.ts | 34 ++++++++++++++++++++ src/server/services/platforms/newApi.ts | 18 +++++++++-- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/server/services/platforms/newApi.test.ts b/src/server/services/platforms/newApi.test.ts index 04c25bd..4321f26 100644 --- a/src/server/services/platforms/newApi.test.ts +++ b/src/server/services/platforms/newApi.test.ts @@ -20,6 +20,7 @@ const CHECKIN_INVALID_URL_EXPIRED_SESSION_TOKEN = 'checkin-invalid-url-expired-s const CHECKIN_INVALID_URL_FORBIDDEN_SESSION_TOKEN = 'checkin-invalid-url-forbidden-session-token'; const CHECKIN_CLOUDFLARE_530_TOKEN = 'checkin-cloudflare-530-token'; const BALANCE_FAIL_TOKEN = 'balance-fail-token'; +const BALANCE_SHIELD_FAILURE_TOKEN = 'balance-shield-failure-token'; const GROUP_EXPIRED_TOKEN = 'group-expired-token'; const SHIELD_LOGIN_USERNAME = 'shield-user'; const SHIELD_LOGIN_PASSWORD = 'shield-pass'; @@ -257,12 +258,38 @@ describe('NewApiAdapter', () => { } if (req.url === '/api/user/self') { + if (typeof req.headers.authorization === 'string' && req.headers.authorization === `Bearer ${BALANCE_SHIELD_FAILURE_TOKEN}`) { + res.writeHead(200, { + 'Content-Type': 'text/html; charset=utf-8', + 'Set-Cookie': `cdn_sec_tc=${SHIELD_LOGIN_COOKIE}; Path=/; HttpOnly`, + }); + res.end(ANYROUTER_CHALLENGE_HTML); + return; + } + if (typeof req.headers.authorization === 'string' && req.headers.authorization === `Bearer ${BALANCE_FAIL_TOKEN}`) { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, message: '无权进行此操作,access token 无效' })); return; } + if ( + typeof req.headers.cookie === 'string' && + ( + req.headers.cookie.includes(`session=${BALANCE_SHIELD_FAILURE_TOKEN}`) || + req.headers.cookie.includes(`token=${BALANCE_SHIELD_FAILURE_TOKEN}`) + ) + ) { + if (!req.headers.cookie.includes(`acw_sc__v2=${ANYROUTER_CHALLENGE_ACW}`)) { + res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); + res.end(ANYROUTER_CHALLENGE_HTML); + return; + } + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ success: false, message: '无权进行此操作,未登录且未提供 access token' })); + return; + } + if ( typeof req.headers.cookie === 'string' && ( @@ -663,6 +690,13 @@ describe('NewApiAdapter', () => { await expect(adapter.getBalance(baseUrl, BALANCE_FAIL_TOKEN)).rejects.toThrow('access token'); }); + it('prefers post-challenge cookie failure over raw html parse error when reading balance', async () => { + const adapter = new AnyRouterAdapter(); + + await expect(adapter.getBalance(baseUrl, BALANCE_SHIELD_FAILURE_TOKEN)).rejects + .toThrow('无权进行此操作,未登录且未提供 access token'); + }); + it('preserves nested checkin error message instead of generic fallback', async () => { const adapter = new NewApiAdapter(); const result = await adapter.checkin(baseUrl, CHECKIN_INVALID_URL_TOKEN, 11494); diff --git a/src/server/services/platforms/newApi.ts b/src/server/services/platforms/newApi.ts index 7e8bf82..9d1b344 100644 --- a/src/server/services/platforms/newApi.ts +++ b/src/server/services/platforms/newApi.ts @@ -549,6 +549,15 @@ export class NewApiAdapter extends BasePlatformAdapter { return ''; } + private isHtmlJsonParseErrorMessage(message?: string | null): boolean { + if (!message) return false; + const text = message.toLowerCase(); + return ( + text.includes("unexpected token '<'") + || (text.includes('not valid json') && (text.includes(' { - if (failureMessage) return; const text = typeof message === 'string' ? message.trim() : ''; if (!text) return; - failureMessage = text; + if (!failureMessage) { + failureMessage = text; + return; + } + if (this.isHtmlJsonParseErrorMessage(failureMessage) && !this.isHtmlJsonParseErrorMessage(text)) { + failureMessage = text; + } }; try {