mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-22 16:43:35 +00:00
165 lines
4.6 KiB
JavaScript
165 lines
4.6 KiB
JavaScript
/**
|
||
* 错误消息清理工具
|
||
* 用于移除上游错误中的供应商特定信息(如 URL、引用等)
|
||
*/
|
||
|
||
/**
|
||
* 清理错误消息中的 URL 和供应商引用
|
||
* @param {string} message - 原始错误消息
|
||
* @returns {string} - 清理后的消息
|
||
*/
|
||
function sanitizeErrorMessage(message) {
|
||
if (typeof message !== 'string') {
|
||
return message
|
||
}
|
||
|
||
// 移除 URL(http:// 或 https://)
|
||
let cleaned = message.replace(/https?:\/\/[^\s]+/gi, '')
|
||
|
||
// 移除常见的供应商引用模式
|
||
cleaned = cleaned.replace(/For more (?:details|information|help)[,\s]*/gi, '')
|
||
cleaned = cleaned.replace(/(?:please\s+)?visit\s+\S*/gi, '') // 移除 "visit xxx"
|
||
cleaned = cleaned.replace(/(?:see|check)\s+(?:our|the)\s+\S*/gi, '') // 移除 "see our xxx"
|
||
cleaned = cleaned.replace(/(?:contact|reach)\s+(?:us|support)\s+at\s+\S*/gi, '') // 移除联系信息
|
||
|
||
// 移除供应商特定关键词(包括整个单词)
|
||
cleaned = cleaned.replace(/88code\S*/gi, '')
|
||
cleaned = cleaned.replace(/duck\S*/gi, '')
|
||
cleaned = cleaned.replace(/packy\S*/gi, '')
|
||
cleaned = cleaned.replace(/ikun\S*/gi, '')
|
||
cleaned = cleaned.replace(/privnode\S*/gi, '')
|
||
cleaned = cleaned.replace(/yescode\S*/gi, '')
|
||
cleaned = cleaned.replace(/yes.vg\S*/gi, '')
|
||
cleaned = cleaned.replace(/share\S*/gi, '')
|
||
cleaned = cleaned.replace(/yhlxj\S*/gi, '')
|
||
cleaned = cleaned.replace(/gac\S*/gi, '')
|
||
cleaned = cleaned.replace(/driod\S*/gi, '')
|
||
|
||
cleaned = cleaned.replace(/\s+/g, ' ').trim()
|
||
|
||
// 如果消息被清理得太短或为空,返回通用消息
|
||
if (cleaned.length < 5) {
|
||
return 'The requested model is currently unavailable'
|
||
}
|
||
|
||
return cleaned
|
||
}
|
||
|
||
/**
|
||
* 递归清理对象中的所有错误消息字段
|
||
* @param {Object} errorData - 原始错误数据对象
|
||
* @returns {Object} - 清理后的错误数据
|
||
*/
|
||
function sanitizeUpstreamError(errorData) {
|
||
if (!errorData || typeof errorData !== 'object') {
|
||
return errorData
|
||
}
|
||
|
||
// 深拷贝避免修改原始对象
|
||
const sanitized = JSON.parse(JSON.stringify(errorData))
|
||
|
||
// 递归清理嵌套的错误对象
|
||
const sanitizeObject = (obj) => {
|
||
if (!obj || typeof obj !== 'object') {
|
||
return obj
|
||
}
|
||
|
||
for (const key in obj) {
|
||
// 清理所有字符串字段,不仅仅是 message
|
||
if (typeof obj[key] === 'string') {
|
||
obj[key] = sanitizeErrorMessage(obj[key])
|
||
} else if (typeof obj[key] === 'object') {
|
||
sanitizeObject(obj[key])
|
||
}
|
||
}
|
||
|
||
return obj
|
||
}
|
||
|
||
return sanitizeObject(sanitized)
|
||
}
|
||
|
||
/**
|
||
* 提取错误消息(支持多种错误格式)
|
||
* @param {*} body - 错误响应体(字符串或对象)
|
||
* @returns {string} - 提取的错误消息
|
||
*/
|
||
function extractErrorMessage(body) {
|
||
if (!body) {
|
||
return ''
|
||
}
|
||
|
||
// 处理字符串类型
|
||
if (typeof body === 'string') {
|
||
const trimmed = body.trim()
|
||
if (!trimmed) {
|
||
return ''
|
||
}
|
||
try {
|
||
const parsed = JSON.parse(trimmed)
|
||
return extractErrorMessage(parsed)
|
||
} catch (error) {
|
||
return trimmed
|
||
}
|
||
}
|
||
|
||
// 处理对象类型
|
||
if (typeof body === 'object') {
|
||
// 常见错误格式: { error: "message" }
|
||
if (typeof body.error === 'string') {
|
||
return body.error
|
||
}
|
||
// 嵌套错误格式: { error: { message: "..." } }
|
||
if (body.error && typeof body.error === 'object') {
|
||
if (typeof body.error.message === 'string') {
|
||
return body.error.message
|
||
}
|
||
if (typeof body.error.error === 'string') {
|
||
return body.error.error
|
||
}
|
||
}
|
||
// 直接消息格式: { message: "..." }
|
||
if (typeof body.message === 'string') {
|
||
return body.message
|
||
}
|
||
}
|
||
|
||
return ''
|
||
}
|
||
|
||
/**
|
||
* 检测是否为账户被禁用或不可用的 400 错误
|
||
* @param {number} statusCode - HTTP 状态码
|
||
* @param {*} body - 响应体
|
||
* @returns {boolean} - 是否为账户禁用错误
|
||
*/
|
||
function isAccountDisabledError(statusCode, body) {
|
||
if (statusCode !== 400) {
|
||
return false
|
||
}
|
||
|
||
const message = extractErrorMessage(body)
|
||
if (!message) {
|
||
return false
|
||
}
|
||
// 将消息全部转换为小写,进行模糊匹配(避免大小写问题)
|
||
const lowerMessage = message.toLowerCase()
|
||
// 检测常见的账户禁用/不可用模式
|
||
return (
|
||
lowerMessage.includes('organization has been disabled') ||
|
||
lowerMessage.includes('account has been disabled') ||
|
||
lowerMessage.includes('account is disabled') ||
|
||
lowerMessage.includes('no account supporting') ||
|
||
lowerMessage.includes('account not found') ||
|
||
lowerMessage.includes('invalid account') ||
|
||
lowerMessage.includes('too many active sessions')
|
||
)
|
||
}
|
||
|
||
module.exports = {
|
||
sanitizeErrorMessage,
|
||
sanitizeUpstreamError,
|
||
extractErrorMessage,
|
||
isAccountDisabledError
|
||
}
|