[codex] fix anyrouter checkin session failure classification (#353)

* fix anyrouter checkin session failure classification

* fix auth failure classification review follow-up
This commit is contained in:
Cita
2026-04-01 12:28:09 +08:00
committed by GitHub
parent 6206c5bd77
commit 4096c757d4
2 changed files with 149 additions and 0 deletions
@@ -16,6 +16,8 @@ const COOKIE_SESSION_TOKEN = 'cookie-session-token';
const COOKIE_REQUIRES_USER_TOKEN = 'cookie-requires-user'; const COOKIE_REQUIRES_USER_TOKEN = 'cookie-requires-user';
const CHECKIN_ALREADY_TOKEN = 'checkin-already-token'; const CHECKIN_ALREADY_TOKEN = 'checkin-already-token';
const CHECKIN_INVALID_URL_TOKEN = 'checkin-invalid-url-token'; const CHECKIN_INVALID_URL_TOKEN = 'checkin-invalid-url-token';
const CHECKIN_INVALID_URL_EXPIRED_SESSION_TOKEN = 'checkin-invalid-url-expired-session-token';
const CHECKIN_INVALID_URL_FORBIDDEN_SESSION_TOKEN = 'checkin-invalid-url-forbidden-session-token';
const CHECKIN_CLOUDFLARE_530_TOKEN = 'checkin-cloudflare-530-token'; const CHECKIN_CLOUDFLARE_530_TOKEN = 'checkin-cloudflare-530-token';
const BALANCE_FAIL_TOKEN = 'balance-fail-token'; const BALANCE_FAIL_TOKEN = 'balance-fail-token';
const GROUP_EXPIRED_TOKEN = 'group-expired-token'; const GROUP_EXPIRED_TOKEN = 'group-expired-token';
@@ -354,6 +356,28 @@ describe('NewApiAdapter', () => {
return; return;
} }
if (
typeof req.headers.cookie === 'string'
&& (
req.headers.cookie.includes(`session=${CHECKIN_INVALID_URL_TOKEN}`)
|| req.headers.cookie.includes(`token=${CHECKIN_INVALID_URL_TOKEN}`)
)
) {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: false, message: 'temporary self probe failure' }));
return;
}
if (typeof req.headers.cookie === 'string' && req.headers.cookie.includes(`session=${CHECKIN_INVALID_URL_EXPIRED_SESSION_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=${CHECKIN_INVALID_URL_FORBIDDEN_SESSION_TOKEN}`)) {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: false, message: 'forbidden' }));
return;
}
res.writeHead(401, { 'Content-Type': 'application/json' }); res.writeHead(401, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: false, message: 'unauthorized' })); res.end(JSON.stringify({ success: false, message: 'unauthorized' }));
return; return;
@@ -375,6 +399,26 @@ describe('NewApiAdapter', () => {
res.end(JSON.stringify({ error: { message: 'Invalid URL (POST /api/user/checkin)' } })); res.end(JSON.stringify({ error: { message: 'Invalid URL (POST /api/user/checkin)' } }));
return; return;
} }
if (typeof req.headers.authorization === 'string' && req.headers.authorization === `Bearer ${CHECKIN_INVALID_URL_EXPIRED_SESSION_TOKEN}`) {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: { message: 'Invalid URL (POST /api/user/checkin)' } }));
return;
}
if (typeof req.headers.cookie === 'string' && req.headers.cookie.includes(`session=${CHECKIN_INVALID_URL_EXPIRED_SESSION_TOKEN}`)) {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: { message: 'Invalid URL (POST /api/user/checkin)' } }));
return;
}
if (typeof req.headers.authorization === 'string' && req.headers.authorization === `Bearer ${CHECKIN_INVALID_URL_FORBIDDEN_SESSION_TOKEN}`) {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: { message: 'Invalid URL (POST /api/user/checkin)' } }));
return;
}
if (typeof req.headers.cookie === 'string' && req.headers.cookie.includes(`session=${CHECKIN_INVALID_URL_FORBIDDEN_SESSION_TOKEN}`)) {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: { message: 'Invalid URL (POST /api/user/checkin)' } }));
return;
}
if (typeof req.headers.authorization === 'string' && req.headers.authorization === `Bearer ${CHECKIN_ALREADY_TOKEN}`) { if (typeof req.headers.authorization === 'string' && req.headers.authorization === `Bearer ${CHECKIN_ALREADY_TOKEN}`) {
res.writeHead(200, { 'Content-Type': 'application/json' }); res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: false, message: '今天已经签到过啦' })); res.end(JSON.stringify({ success: false, message: '今天已经签到过啦' }));
@@ -421,6 +465,16 @@ describe('NewApiAdapter', () => {
} }
if (req.url === '/api/user/sign_in') { if (req.url === '/api/user/sign_in') {
if (typeof req.headers.cookie === 'string' && req.headers.cookie.includes(`session=${CHECKIN_INVALID_URL_EXPIRED_SESSION_TOKEN}`)) {
res.writeHead(401, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({}));
return;
}
if (typeof req.headers.cookie === 'string' && req.headers.cookie.includes(`session=${CHECKIN_INVALID_URL_FORBIDDEN_SESSION_TOKEN}`)) {
res.writeHead(401, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({}));
return;
}
if (typeof req.headers.cookie === 'string' && req.headers.cookie.includes(`session=${CHECKIN_ALREADY_TOKEN}`)) { if (typeof req.headers.cookie === 'string' && req.headers.cookie.includes(`session=${CHECKIN_ALREADY_TOKEN}`)) {
res.writeHead(200, { 'Content-Type': 'application/json' }); res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: false, message: '无权进行此操作,未登录且未提供 access token' })); res.end(JSON.stringify({ success: false, message: '无权进行此操作,未登录且未提供 access token' }));
@@ -617,6 +671,24 @@ describe('NewApiAdapter', () => {
expect(result.message).toContain('Invalid URL'); expect(result.message).toContain('Invalid URL');
}); });
it('prefers cookie session auth failure over invalid-url fallback when cookie session is expired', async () => {
const adapter = new NewApiAdapter();
const result = await adapter.checkin(baseUrl, CHECKIN_INVALID_URL_EXPIRED_SESSION_TOKEN, 131936);
expect(result.success).toBe(false);
expect(result.message).toContain('access token');
expect(result.message).not.toContain('Invalid URL');
});
it('treats forbidden self probe responses as cookie session auth failures', async () => {
const adapter = new NewApiAdapter();
const result = await adapter.checkin(baseUrl, CHECKIN_INVALID_URL_FORBIDDEN_SESSION_TOKEN, 131936);
expect(result.success).toBe(false);
expect(result.message).toContain('forbidden');
expect(result.message).not.toContain('Invalid URL');
});
it('summarizes cloudflare tunnel HTML failures to concise checkin error', async () => { it('summarizes cloudflare tunnel HTML failures to concise checkin error', async () => {
const adapter = new NewApiAdapter(); const adapter = new NewApiAdapter();
const result = await adapter.checkin(baseUrl, CHECKIN_CLOUDFLARE_530_TOKEN, 11494); const result = await adapter.checkin(baseUrl, CHECKIN_CLOUDFLARE_530_TOKEN, 11494);
+77
View File
@@ -629,6 +629,72 @@ export class NewApiAdapter extends BasePlatformAdapter {
); );
} }
private isMissingCheckinEndpointMessage(message?: string | null): boolean {
if (!message) return false;
const text = message.toLowerCase();
return (
text.includes('invalid url (post /api/user/checkin)') ||
(text.includes('http 404') && text.includes('/api/user/checkin')) ||
text.includes('checkin endpoint not found') ||
text.includes('check-in is not supported') ||
text.includes('checkin is not supported') ||
text.includes('does not support checkin') ||
text.includes('not support checkin')
);
}
private isCookieSessionFailureMessage(message?: string | null): boolean {
if (!message) return false;
const text = message.toLowerCase();
return (
text.includes('access token') ||
text.includes('unauthorized') ||
text.includes('forbidden') ||
text.includes('new-api-user') ||
text.includes('user id') ||
text.includes('invalid token') ||
text.includes('expired') ||
text.includes('无权') ||
text.includes('未登录') ||
text.includes('未提供') ||
text.includes('未授权') ||
text.includes('not login') ||
text.includes('not logged')
);
}
private async detectCookieSessionFailureMessage(
baseUrl: string,
accessToken: string,
candidateUserIds: Array<number | null | undefined>,
): Promise<string | null> {
let failureMessage: string | null = null;
const rememberFailure = (message: string) => {
if (failureMessage) return;
const text = message.trim();
if (!this.isCookieSessionFailureMessage(text)) return;
failureMessage = text;
};
const uniqueCandidateUserIds = Array.from(new Set(
candidateUserIds.filter((value): value is number => typeof value === 'number' && value > 0),
));
if (uniqueCandidateUserIds.length === 0) {
await this.fetchUserSelfByCookie(baseUrl, accessToken, undefined, rememberFailure);
return failureMessage;
}
for (const userId of uniqueCandidateUserIds) {
await this.fetchUserSelfByCookie(baseUrl, accessToken, userId, rememberFailure);
if (failureMessage) {
return failureMessage;
}
}
return failureMessage;
}
private async fetchJsonRawWithCookie<T>( private async fetchJsonRawWithCookie<T>(
url: string, url: string,
options?: UndiciRequestInit, options?: UndiciRequestInit,
@@ -1078,6 +1144,17 @@ export class NewApiAdapter extends BasePlatformAdapter {
if (retriedCookieResult) return retriedCookieResult; if (retriedCookieResult) return retriedCookieResult;
} }
if (this.isMissingCheckinEndpointMessage(firstFailureMessage)) {
const cookieSessionFailureMessage = await this.detectCookieSessionFailureMessage(
baseUrl,
accessToken,
[resolvedUserId, alternateCookieUserId],
);
if (cookieSessionFailureMessage) {
return { success: false, message: cookieSessionFailureMessage };
}
}
return { success: false, message: firstFailureMessage || 'checkin failed' }; return { success: false, message: firstFailureMessage || 'checkin failed' };
} }