File size: 7,188 Bytes
4bf0fdd c801c74 4bf0fdd c801c74 4bf0fdd |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 |
/**
* 统一错误处理模块
* @module utils/errors
*/
/**
* 应用错误基类
*/
export class AppError extends Error {
/**
* @param {string} message - 错误消息
* @param {number} statusCode - HTTP 状态码
* @param {string} type - 错误类型
*/
constructor(message, statusCode = 500, type = 'server_error') {
super(message);
this.name = 'AppError';
this.statusCode = statusCode;
this.type = type;
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
/**
* 上游 API 错误
*/
export class UpstreamApiError extends AppError {
/**
* @param {string} message - 错误消息
* @param {number} statusCode - HTTP 状态码
* @param {string|Object} rawBody - 原始响应体
*/
constructor(message, statusCode, rawBody = null) {
super(message, statusCode, 'upstream_api_error');
this.name = 'UpstreamApiError';
this.rawBody = rawBody;
this.isUpstreamApiError = true;
}
}
/**
* 认证错误
*/
export class AuthenticationError extends AppError {
/**
* @param {string} message - 错误消息
*/
constructor(message = '认证失败') {
super(message, 401, 'authentication_error');
this.name = 'AuthenticationError';
}
}
/**
* 授权错误
*/
export class AuthorizationError extends AppError {
/**
* @param {string} message - 错误消息
*/
constructor(message = '无权限访问') {
super(message, 403, 'authorization_error');
this.name = 'AuthorizationError';
}
}
/**
* 验证错误
*/
export class ValidationError extends AppError {
/**
* @param {string} message - 错误消息
* @param {Object} details - 验证详情
*/
constructor(message = '请求参数无效', details = null) {
super(message, 400, 'validation_error');
this.name = 'ValidationError';
this.details = details;
}
}
/**
* 资源未找到错误
*/
export class NotFoundError extends AppError {
/**
* @param {string} message - 错误消息
*/
constructor(message = '资源未找到') {
super(message, 404, 'not_found');
this.name = 'NotFoundError';
}
}
/**
* 速率限制错误
*/
export class RateLimitError extends AppError {
/**
* @param {string} message - 错误消息
* @param {number} retryAfter - 重试等待时间(秒)
*/
constructor(message = '请求过于频繁', retryAfter = null) {
super(message, 429, 'rate_limit_error');
this.name = 'RateLimitError';
this.retryAfter = retryAfter;
}
}
/**
* Token 相关错误
*/
export class TokenError extends AppError {
/**
* @param {string} message - 错误消息
* @param {string} tokenSuffix - Token 后缀(用于日志)
* @param {number} statusCode - HTTP 状态码
*/
constructor(message, tokenSuffix = null, statusCode = 500) {
super(message, statusCode, 'token_error');
this.name = 'TokenError';
this.tokenSuffix = tokenSuffix;
}
}
/**
* 创建上游 API 错误(工厂函数)
* @param {string} message - 错误消息
* @param {number} status - HTTP 状态码
* @param {string|Object} rawBody - 原始响应体
* @returns {UpstreamApiError}
*/
export function createApiError(message, status, rawBody) {
return new UpstreamApiError(message, status, rawBody);
}
/**
* 从错误对象中提取消息
* @param {Error} error - 错误对象
* @returns {string}
*/
function extractErrorMessage(error) {
if (error.isUpstreamApiError && error.rawBody) {
try {
const raw = typeof error.rawBody === 'string' ? JSON.parse(error.rawBody) : error.rawBody;
return raw.error?.message || raw.message || error.message;
} catch {}
}
return error.message || 'Internal server error';
}
/**
* 构建 OpenAI 兼容的错误响应
* @param {Error} error - 错误对象
* @param {number} statusCode - HTTP 状态码
* @returns {{error: {message: string, type: string, code: number}}}
*/
export function buildOpenAIErrorPayload(error, statusCode) {
// 处理上游 API 错误
if (error.isUpstreamApiError && error.rawBody) {
try {
const raw = typeof error.rawBody === 'string' ? JSON.parse(error.rawBody) : error.rawBody;
const inner = raw.error || raw;
return {
error: {
message: inner.message || error.message || 'Upstream API error',
type: inner.type || 'upstream_api_error',
code: inner.code ?? statusCode
}
};
} catch {
return {
error: {
message: error.rawBody || error.message || 'Upstream API error',
type: 'upstream_api_error',
code: statusCode
}
};
}
}
// 处理应用错误
if (error instanceof AppError) {
return {
error: {
message: error.message,
type: error.type,
code: error.statusCode
}
};
}
// 处理通用错误
return {
error: {
message: error.message || 'Internal server error',
type: 'server_error',
code: statusCode
}
};
}
/**
* 构建 Gemini 兼容的错误响应
* @param {Error} error - 错误对象
* @param {number} statusCode - HTTP 状态码
* @returns {{error: {code: number, message: string, status: string}}}
*/
export function buildGeminiErrorPayload(error, statusCode) {
return {
error: {
code: statusCode,
message: extractErrorMessage(error),
status: "INTERNAL"
}
};
}
/**
* 构建 Claude 兼容的错误响应
* @param {Error} error - 错误对象
* @param {number} statusCode - HTTP 状态码
* @returns {{type: string, error: {type: string, message: string}}}
*/
export function buildClaudeErrorPayload(error, statusCode) {
const errorType = statusCode === 401 ? "authentication_error" :
statusCode === 429 ? "rate_limit_error" :
statusCode === 400 ? "invalid_request_error" :
"api_error";
return {
type: "error",
error: {
type: errorType,
message: extractErrorMessage(error)
}
};
}
/**
* Express 错误处理中间件
* @param {Error} err - 错误对象
* @param {import('express').Request} req - 请求对象
* @param {import('express').Response} res - 响应对象
* @param {import('express').NextFunction} next - 下一个中间件
*/
export function errorHandler(err, req, res, next) {
// 如果响应已发送,交给默认处理
if (res.headersSent) {
return next(err);
}
// 处理请求体过大错误
if (err.type === 'entity.too.large') {
return res.status(413).json({
error: {
message: '请求体过大',
type: 'payload_too_large',
code: 413
}
});
}
// 确定状态码
const statusCode = err.statusCode || err.status || 500;
// 构建错误响应
const errorPayload = buildOpenAIErrorPayload(err, statusCode);
return res.status(statusCode).json(errorPayload);
}
/**
* 异步路由包装器(自动捕获异步错误)
* @param {Function} fn - 异步路由处理函数
* @returns {Function} 包装后的路由处理函数
*/
export function asyncHandler(fn) {
return (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
} |