cc / src /utils /errorSanitizer.js
hequ's picture
Upload 224 files
6c6056a verified
/**
* 错误消息清理工具
* 用于移除上游错误中的供应商特定信息(如 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(/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) {
if (key === 'message' && 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
}