返回 AiToEarn
kwai.exception.ts
1 import type { AxiosError, AxiosResponse, InternalAxiosRequestConfig } from 'axios'
2 import type { PlatformSpecificExceptionInput, PlatformValidationExceptionInput } from '../platforms.exception'
3 import { AccountType, ResponseCode } from '@yikart/common'
4 import { categoryFromHttpStatus, isHttpStatusRetryable, isNetworkErrorCode } from '../../utils/platform-error-classifier.util'
5 import { ChannelPlatformException, PlatformErrorCategory, PlatformErrorCauseType } from '../platforms.exception'
6
7 export interface KwaiPlatformResponseBody {
8 result?: number | string
9 error?: string
10 error_msg?: string
11 }
12
13 export class KwaiPlatformException extends ChannelPlatformException {
14 constructor(input: PlatformSpecificExceptionInput) {
15 super({ ...input, platform: AccountType.Kwai })
16 }
17
18 static validation(input: PlatformValidationExceptionInput): KwaiPlatformException {
19 return new KwaiPlatformException({
20 ...input,
21 cause: { type: PlatformErrorCauseType.Validation, ...input.cause },
22 })
23 }
24
25 static fromAxiosError(error: AxiosError<KwaiPlatformResponseBody>): KwaiPlatformException {
26 const response = error.response
27 const body = response?.data
28 const platformCode = body?.result
29 const endpoint = KwaiPlatformException.endpointFromConfig(response?.config ?? error.config)
30 return new KwaiPlatformException({
31 code: KwaiPlatformException.codeFromEndpoint(endpoint),
32 category: KwaiPlatformException.categoryFromError(endpoint, response?.status),
33 context: endpoint ? { endpoint } : undefined,
34 cause: {
35 type: KwaiPlatformException.causeTypeFromAxiosError(error, platformCode),
36 httpStatus: response?.status,
37 platformCode,
38 platformMessage: body?.error_msg ?? error.message,
39 raw: body ?? error,
40 },
41 retryable: response ? isHttpStatusRetryable(response.status) : true,
42 })
43 }
44
45 static fromPlatformResponse(response: AxiosResponse<KwaiPlatformResponseBody>): KwaiPlatformException {
46 const endpoint = KwaiPlatformException.endpointFromConfig(response.config)
47 return new KwaiPlatformException({
48 code: KwaiPlatformException.codeFromEndpoint(endpoint),
49 category: KwaiPlatformException.categoryFromError(endpoint, response.status),
50 context: endpoint ? { endpoint } : undefined,
51 cause: {
52 type: response.data.result === undefined ? PlatformErrorCauseType.Http : PlatformErrorCauseType.Platform,
53 httpStatus: response.status,
54 platformCode: response.data.result,
55 platformMessage: response.data.error_msg,
56 raw: response.data,
57 },
58 retryable: KwaiPlatformException.isRetryablePlatformError(endpoint, response.data),
59 })
60 }
61
62 static hasPlatformError(response: AxiosResponse<KwaiPlatformResponseBody>): boolean {
63 return response.data.result !== undefined && response.data.result !== 1 && response.data.result !== '1'
64 }
65
66 private static endpointFromConfig(config?: InternalAxiosRequestConfig): string | undefined {
67 if (!config?.url) {
68 return undefined
69 }
70 const method = config.method?.toUpperCase()
71 const path = KwaiPlatformException.pathFromUrl(config.url, config.baseURL)
72 return method ? `${method} ${path}` : path
73 }
74
75 private static pathFromUrl(rawUrl: string, baseURL?: string): string {
76 try {
77 return new URL(rawUrl, baseURL).pathname
78 }
79 catch {
80 return rawUrl.split('?')[0] || rawUrl
81 }
82 }
83
84 private static codeFromEndpoint(endpoint?: string): ResponseCode {
85 if (endpoint?.includes('/oauth2/refresh_token')) {
86 return ResponseCode.ChannelRefreshTokenFailed
87 }
88 if (endpoint?.includes('/oauth2/')) {
89 return ResponseCode.ChannelAccessTokenFailed
90 }
91 if (endpoint?.includes('/photo/')) {
92 return ResponseCode.ChannelPlatformMediaProcessingFailed
93 }
94 return ResponseCode.ChannelPlatformApiFailed
95 }
96
97 private static categoryFromEndpoint(endpoint?: string): PlatformErrorCategory {
98 if (endpoint?.includes('/oauth2/')) {
99 return PlatformErrorCategory.Auth
100 }
101 if (endpoint?.includes('/photo/')) {
102 return PlatformErrorCategory.MediaProcessingFailed
103 }
104 return PlatformErrorCategory.Unknown
105 }
106
107 private static categoryFromError(endpoint?: string, status?: number): PlatformErrorCategory {
108 if (status === undefined) {
109 return PlatformErrorCategory.Network
110 }
111
112 const category = KwaiPlatformException.categoryFromEndpoint(endpoint)
113 if (category !== PlatformErrorCategory.Unknown) {
114 return category
115 }
116
117 return categoryFromHttpStatus(status)
118 }
119
120 private static isRetryablePlatformError(endpoint: string | undefined, body: KwaiPlatformResponseBody): boolean {
121 return endpoint === 'GET /openapi/photo/info'
122 && body.result === 100120001
123 && body.error === 'video_not_exist'
124 && body.error_msg === 'VIDEO_NOT_EXIST'
125 }
126
127 private static causeTypeFromAxiosError(
128 error: AxiosError<KwaiPlatformResponseBody>,
129 platformCode?: string | number,
130 ): PlatformErrorCauseType {
131 if (platformCode !== undefined) {
132 return PlatformErrorCauseType.Platform
133 }
134 if (error.response) {
135 return PlatformErrorCauseType.Http
136 }
137 if (isNetworkErrorCode(error.code)) {
138 return PlatformErrorCauseType.Network
139 }
140 return PlatformErrorCauseType.Unknown
141 }
142 }
143
143 lines TYPESCRIPT