Files
geo/apps/desktop-client/src/main/platform-auth-adapters.ts
T
Xu Liang 0afd082d96
Desktop Client Build / Resolve Build Metadata (push) Successful in 26s
Frontend CI / Frontend (push) Successful in 3m12s
Backend CI / Backend (push) Failing after 10m8s
Desktop Client Build / Build Desktop Client (push) Successful in 23m53s
Desktop Client Build / Publish Client Artifacts to NAS (push) Successful in 26s
feat(doubao): implement challenge handling and monitoring improvements
- Added classification for `doubao_challenge_required` as a challenge in platform-auth-adapters.
- Enhanced account action summaries to include specific messages for Doubao human verification.
- Introduced monitoring functions to filter out blocked platforms based on account health.
- Updated task leasing to support platform-specific filtering for monitor tasks.
- Refined issue aggregation in HomeView to consolidate Doubao challenge failures into a single actionable item.
- Improved backend logic to handle platform IDs in task leasing requests and responses.
- Added tests for new functionality, ensuring proper handling of Doubao challenges and task leasing logic.
2026-06-24 02:31:35 +08:00

416 lines
12 KiB
TypeScript

import { aiPlatformCatalog } from '@geo/shared-types'
import {
probeAIAccountSession,
probePublishAccountSession,
silentRefreshAIAccountSession,
silentRefreshPublishAccountSession,
type PublishAccountIdentity,
} from './account-binder'
import type { AuthProbeResult, PlatformFailureClassification } from './auth-types'
export interface PlatformFailureInput {
summary?: string | null
error?: Record<string, unknown> | null
message?: string | null
}
export interface PlatformAdapter {
probe(account: PublishAccountIdentity, partition: string): Promise<AuthProbeResult>
silentRefresh(account: PublishAccountIdentity, partition: string): Promise<boolean>
classifyFailure(input: PlatformFailureInput): PlatformFailureClassification
}
function normalizeFailureText(input: PlatformFailureInput): string {
const values = [
input.summary,
input.message,
typeof input.error?.code === 'string' ? input.error.code : null,
typeof input.error?.reason_code === 'string' ? input.error.reason_code : null,
typeof input.error?.message === 'string' ? input.error.message : null,
typeof input.error?.detail === 'string' ? input.error.detail : null,
typeof input.error?.page_error === 'string' ? input.error.page_error : null,
typeof input.error?.verify_scene === 'string' ? input.error.verify_scene : null,
]
return values
.filter((value): value is string => typeof value === 'string' && Boolean(value.trim()))
.join(' | ')
.toLowerCase()
}
function classifyFailure(input: PlatformFailureInput): PlatformFailureClassification {
const text = normalizeFailureText(input)
if (!text) {
return 'ok'
}
if (
/(verify_scene|verification required|captcha|challenge|验证码|滑块|人机验证|安全验证|请完成验证|短信验证|扫码验证)/i.test(
text,
)
) {
return 'challenge'
}
if (
/(not_logged_in|unauthorized|unauthenticated|login redirect|login required|signin|sign in|expired|401|登录态失效|登录超时|请重新登录|请先登录|未登录)/i.test(
text,
)
) {
return 'auth_failure'
}
if (
/(network|timeout|timed out|failed to fetch|econnreset|econnrefused|enotfound|dns|socket hang up|502|503|504)/i.test(
text,
)
) {
return 'network'
}
return 'ok'
}
function classifyDoubaoFailure(input: PlatformFailureInput): PlatformFailureClassification {
const code = typeof input.error?.code === 'string' ? input.error.code : ''
if (code === 'doubao_challenge_required') {
return 'challenge'
}
const text = normalizeFailureText(input)
if (!text) {
return 'ok'
}
if (
/(rate limited|rate limit|too many requests|request limit|frequency limit|请求过于频繁|频率过快|访问过于频繁|限流|风控|服务繁忙|请稍后|稍后再试)/i.test(
text,
)
) {
return 'risk_control'
}
return classifyFailure(input)
}
function classifyWenxinFailure(input: PlatformFailureInput): PlatformFailureClassification {
const text = normalizeFailureText(input)
if (!text) {
return 'ok'
}
if (
/(当前访问环境存在异常|访问环境存在异常|环境存在异常|更换浏览器再尝试提问|当前环境异常|访问受限|环境异常)/i.test(
text,
)
) {
return 'risk_control'
}
return classifyFailure(input)
}
function classifyBaijiahaoFailure(input: PlatformFailureInput): PlatformFailureClassification {
const code = typeof input.error?.code === 'string' ? input.error.code : ''
if (code === 'baijiahao_challenge_required') {
return 'challenge'
}
if (code === 'baijiahao_not_logged_in' || code === 'baijiahao_token_missing') {
return 'auth_failure'
}
if (code.startsWith('baijiahao_')) {
return 'ok'
}
return classifyFailure(input)
}
function classifySohuhaoFailure(input: PlatformFailureInput): PlatformFailureClassification {
const code = typeof input.error?.code === 'string' ? input.error.code : ''
if (code === 'sohuhao_not_logged_in') {
return 'auth_failure'
}
if (code === 'sohuhao_challenge_required') {
return 'challenge'
}
if (code.startsWith('sohuhao_')) {
return 'ok'
}
return classifyFailure(input)
}
function classifyQiehaoFailure(input: PlatformFailureInput): PlatformFailureClassification {
const code = typeof input.error?.code === 'string' ? input.error.code : ''
if (code === 'qiehao_challenge_required') {
return 'challenge'
}
if (code === 'qiehao_not_logged_in' || code === 'qiehao_cookie_missing') {
return 'auth_failure'
}
if (code.startsWith('qiehao_')) {
return 'ok'
}
return classifyFailure(input)
}
function classifyBilibiliFailure(input: PlatformFailureInput): PlatformFailureClassification {
const code = typeof input.error?.code === 'string' ? input.error.code : ''
if (code === 'bilibili_not_logged_in' || code === 'bilibili_csrf_missing') {
return 'auth_failure'
}
if (code.startsWith('bilibili_')) {
return 'ok'
}
return classifyFailure(input)
}
function classifyJuejinFailure(input: PlatformFailureInput): PlatformFailureClassification {
const code = typeof input.error?.code === 'string' ? input.error.code : ''
if (code === 'juejin_not_logged_in') {
return 'auth_failure'
}
if (code.startsWith('juejin_')) {
return 'ok'
}
return classifyFailure(input)
}
function classifyWangyihaoFailure(input: PlatformFailureInput): PlatformFailureClassification {
const code = typeof input.error?.code === 'string' ? input.error.code : ''
if (code === 'wangyihao_not_logged_in') {
return 'auth_failure'
}
if (code === 'wangyihao_token_missing') {
return 'ok'
}
if (code === 'wangyihao_challenge_required') {
return 'challenge'
}
if (code.startsWith('wangyihao_')) {
return 'ok'
}
return classifyFailure(input)
}
function classifyWeixinGzhFailure(input: PlatformFailureInput): PlatformFailureClassification {
const code = typeof input.error?.code === 'string' ? input.error.code : ''
if (code === 'weixin_gzh_not_logged_in') {
return 'auth_failure'
}
if (code === 'weixin_gzh_challenge_required') {
return 'challenge'
}
if (code.startsWith('weixin_gzh_')) {
return 'ok'
}
return classifyFailure(input)
}
function classifyZolFailure(input: PlatformFailureInput): PlatformFailureClassification {
const code = typeof input.error?.code === 'string' ? input.error.code : ''
if (code === 'zol_not_logged_in') {
return 'auth_failure'
}
if (code === 'zol_challenge_required') {
return 'challenge'
}
if (code.startsWith('zol_')) {
return 'ok'
}
return classifyFailure(input)
}
function classifyDongchediFailure(input: PlatformFailureInput): PlatformFailureClassification {
const code = typeof input.error?.code === 'string' ? input.error.code : ''
if (code === 'dongchedi_not_logged_in') {
return 'auth_failure'
}
if (code === 'dongchedi_challenge_required') {
return 'challenge'
}
if (code.startsWith('dongchedi_')) {
return 'ok'
}
return classifyFailure(input)
}
const aiAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probeAIAccountSession(account, partition)
},
async silentRefresh(account, partition) {
return await silentRefreshAIAccountSession(account, partition)
},
classifyFailure,
}
const genericAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probePublishAccountSession(account, partition)
},
async silentRefresh(account, partition) {
return await silentRefreshPublishAccountSession(account, partition)
},
classifyFailure,
}
const doubaoAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probeAIAccountSession(account, partition)
},
async silentRefresh(account, partition) {
return await silentRefreshAIAccountSession(account, partition)
},
classifyFailure: classifyDoubaoFailure,
}
const wenxinAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probeAIAccountSession(account, partition)
},
async silentRefresh(account, partition) {
return await silentRefreshAIAccountSession(account, partition)
},
classifyFailure: classifyWenxinFailure,
}
const baijiahaoAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probePublishAccountSession(account, partition)
},
async silentRefresh(account, partition) {
return await silentRefreshPublishAccountSession(account, partition)
},
classifyFailure: classifyBaijiahaoFailure,
}
const sohuhaoAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probePublishAccountSession(account, partition)
},
async silentRefresh(account, partition) {
return await silentRefreshPublishAccountSession(account, partition)
},
classifyFailure: classifySohuhaoFailure,
}
const qiehaoAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probePublishAccountSession(account, partition)
},
async silentRefresh(account, partition) {
return await silentRefreshPublishAccountSession(account, partition)
},
classifyFailure: classifyQiehaoFailure,
}
const bilibiliAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probePublishAccountSession(account, partition)
},
async silentRefresh(account, partition) {
return await silentRefreshPublishAccountSession(account, partition)
},
classifyFailure: classifyBilibiliFailure,
}
const wangyihaoAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probePublishAccountSession(account, partition)
},
async silentRefresh(account, partition) {
return await silentRefreshPublishAccountSession(account, partition)
},
classifyFailure: classifyWangyihaoFailure,
}
const juejinAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probePublishAccountSession(account, partition)
},
async silentRefresh(account, partition) {
return await silentRefreshPublishAccountSession(account, partition)
},
classifyFailure: classifyJuejinFailure,
}
const weixinGzhAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probePublishAccountSession(account, partition)
},
async silentRefresh(account, partition) {
return await silentRefreshPublishAccountSession(account, partition)
},
classifyFailure: classifyWeixinGzhFailure,
}
const zolAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probePublishAccountSession(account, partition)
},
async silentRefresh(account, partition) {
return await silentRefreshPublishAccountSession(account, partition)
},
classifyFailure: classifyZolFailure,
}
const dongchediAdapter: PlatformAdapter = {
async probe(account, partition) {
return await probePublishAccountSession(account, partition)
},
async silentRefresh(account, partition) {
return await silentRefreshPublishAccountSession(account, partition)
},
classifyFailure: classifyDongchediFailure,
}
const adapterRegistry = new Map<string, PlatformAdapter>(
[
...aiPlatformCatalog.map((platform) => platform.id),
'toutiaohao',
'baijiahao',
'sohuhao',
'qiehao',
'zhihu',
'wangyihao',
'jianshu',
'bilibili',
'juejin',
'smzdm',
'weixin_gzh',
'zol',
'dongchedi',
].map((platform) => [
platform,
platform === 'doubao'
? doubaoAdapter
: platform === 'wenxin'
? wenxinAdapter
: platform === 'baijiahao'
? baijiahaoAdapter
: platform === 'sohuhao'
? sohuhaoAdapter
: platform === 'qiehao'
? qiehaoAdapter
: platform === 'wangyihao'
? wangyihaoAdapter
: platform === 'bilibili'
? bilibiliAdapter
: platform === 'juejin'
? juejinAdapter
: platform === 'weixin_gzh'
? weixinGzhAdapter
: platform === 'zol'
? zolAdapter
: platform === 'dongchedi'
? dongchediAdapter
: aiPlatformCatalog.some((item) => item.id === platform)
? aiAdapter
: genericAdapter,
]),
)
export function getPlatformAdapter(platformId: string): PlatformAdapter {
return adapterRegistry.get(platformId) ?? genericAdapter
}