forked from boos/BoosAPI
[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:
@@ -16,6 +16,8 @@ const COOKIE_SESSION_TOKEN = 'cookie-session-token';
|
||||
const COOKIE_REQUIRES_USER_TOKEN = 'cookie-requires-user';
|
||||
const CHECKIN_ALREADY_TOKEN = 'checkin-already-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 BALANCE_FAIL_TOKEN = 'balance-fail-token';
|
||||
const GROUP_EXPIRED_TOKEN = 'group-expired-token';
|
||||
@@ -354,6 +356,28 @@ describe('NewApiAdapter', () => {
|
||||
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.end(JSON.stringify({ success: false, message: 'unauthorized' }));
|
||||
return;
|
||||
@@ -375,6 +399,26 @@ describe('NewApiAdapter', () => {
|
||||
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_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}`) {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ success: false, message: '今天已经签到过啦' }));
|
||||
@@ -421,6 +465,16 @@ describe('NewApiAdapter', () => {
|
||||
}
|
||||
|
||||
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}`)) {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({ success: false, message: '无权进行此操作,未登录且未提供 access token' }));
|
||||
@@ -617,6 +671,24 @@ describe('NewApiAdapter', () => {
|
||||
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 () => {
|
||||
const adapter = new NewApiAdapter();
|
||||
const result = await adapter.checkin(baseUrl, CHECKIN_CLOUDFLARE_530_TOKEN, 11494);
|
||||
|
||||
@@ -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>(
|
||||
url: string,
|
||||
options?: UndiciRequestInit,
|
||||
@@ -1078,6 +1144,17 @@ export class NewApiAdapter extends BasePlatformAdapter {
|
||||
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' };
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user