Distinguish expired OAuth token status

This commit is contained in:
elky
2026-06-12 19:43:59 +08:00
parent 308cc88ef7
commit 68038c182b
16 changed files with 332 additions and 58 deletions
@@ -1,6 +1,7 @@
import { describe, expect, it } from 'vitest'
import {
classifyAccountBlockLabel,
cleanAccountBlockReason,
isAccountLevelBlockReason,
isRefreshFailedReason,
@@ -30,4 +31,9 @@ describe('accountBlock helpers', () => {
),
).toBe(false)
})
it('labels invalidated and expired oauth markers separately', () => {
expect(classifyAccountBlockLabel('[OAUTH_EXPIRED] token invalidated')).toBe('Token 失效')
expect(classifyAccountBlockLabel('[OAUTH_EXPIRED] session expired')).toBe('Token 过期')
})
})
+53 -8
View File
@@ -28,9 +28,33 @@ const KEYWORDS_DISABLED = [
]
const KEYWORDS_TOKEN_INVALID = [
'oauth_token_invalid',
'token_invalidated',
'authentication token has been invalidated',
'token has been invalidated',
'codex token 无效或已过期',
'token invalidated',
'invalidated',
'revoked',
'已撤销',
'被撤销',
'撤销',
'作废',
'已失效',
'token 失效',
'令牌失效',
]
const KEYWORDS_TOKEN_EXPIRED = [
'oauth_token_expired',
'session has expired',
'session expired',
'access token expired',
'expired access token',
'token has expired',
'token expired',
'security token included in the request is expired',
'已过期',
'过期',
]
// 需要验证类
@@ -47,9 +71,29 @@ const ACCOUNT_BLOCK_REASON_KEYWORDS = [
...KEYWORDS_SUSPENDED,
...KEYWORDS_DISABLED,
...KEYWORDS_TOKEN_INVALID,
...KEYWORDS_TOKEN_EXPIRED,
...KEYWORDS_VERIFICATION,
]
function normalizeOAuthReasonDetail(reason: string): string {
return reason
.replace(/^\[(ACCOUNT_BLOCK|OAUTH_EXPIRED)\]\s*/i, '')
.replace(/\s*\[REFRESH_FAILED\][\s\S]*$/i, '')
.trim()
}
function isHardTokenInvalidReason(reason: string): boolean {
const lowered = reason.toLowerCase()
if (KEYWORDS_TOKEN_INVALID.some(keyword => lowered.includes(keyword))) return true
return (lowered.includes('token 无效') || lowered.includes('令牌无效'))
&& !KEYWORDS_TOKEN_EXPIRED.some(keyword => lowered.includes(keyword))
}
function isTokenExpiredReason(reason: string): boolean {
const lowered = reason.toLowerCase()
return KEYWORDS_TOKEN_EXPIRED.some(keyword => lowered.includes(keyword))
}
export function isAccountLevelBlockReason(reason: string | null | undefined): boolean {
if (!reason) return false
const text = reason.trim()
@@ -62,9 +106,13 @@ export function isAccountLevelBlockReason(reason: string | null | undefined): bo
}
export function classifyAccountBlockLabel(reason: string): string {
if (reason.trim().startsWith('[OAUTH_EXPIRED]')) return 'Token 失效'
const lowered = reason.toLowerCase()
if (KEYWORDS_TOKEN_INVALID.some(kw => lowered.includes(kw))) return 'Token 失效'
const detail = normalizeOAuthReasonDetail(reason) || reason
if (reason.trim().startsWith('[OAUTH_EXPIRED]')) {
return isHardTokenInvalidReason(detail) ? 'Token 失效' : 'Token 过期'
}
const lowered = detail.toLowerCase()
if (isHardTokenInvalidReason(detail)) return 'Token 失效'
if (isTokenExpiredReason(detail)) return 'Token 过期'
if (KEYWORDS_VERIFICATION.some(kw => lowered.includes(kw))) return '需要验证'
if (lowered.includes('deactivated_workspace')) return '工作区停用'
if (KEYWORDS_DISABLED.some(kw => lowered.includes(kw))) return '账号停用'
@@ -73,10 +121,7 @@ export function classifyAccountBlockLabel(reason: string): string {
}
export function cleanAccountBlockReason(reason: string): string {
return reason
.replace(/^\[(ACCOUNT_BLOCK|OAUTH_EXPIRED)\]\s*/i, '')
.replace(/\s*\[REFRESH_FAILED\][\s\S]*$/i, '')
.trim()
return normalizeOAuthReasonDetail(reason)
}
export function isRefreshFailedReason(reason: string | null | undefined): boolean {