| |
| |
| |
| |
|
|
| |
| |
| |
| export class AppError extends Error { |
| |
| |
| |
| |
| |
| 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); |
| } |
| } |
|
|
| |
| |
| |
| export class UpstreamApiError extends AppError { |
| |
| |
| |
| |
| |
| 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 { |
| |
| |
| |
| constructor(message = '认证失败') { |
| super(message, 401, 'authentication_error'); |
| this.name = 'AuthenticationError'; |
| } |
| } |
|
|
| |
| |
| |
| export class AuthorizationError extends AppError { |
| |
| |
| |
| constructor(message = '无权限访问') { |
| super(message, 403, 'authorization_error'); |
| this.name = 'AuthorizationError'; |
| } |
| } |
|
|
| |
| |
| |
| export class ValidationError extends AppError { |
| |
| |
| |
| |
| constructor(message = '请求参数无效', details = null) { |
| super(message, 400, 'validation_error'); |
| this.name = 'ValidationError'; |
| this.details = details; |
| } |
| } |
|
|
| |
| |
| |
| export class NotFoundError extends AppError { |
| |
| |
| |
| constructor(message = '资源未找到') { |
| super(message, 404, 'not_found'); |
| this.name = 'NotFoundError'; |
| } |
| } |
|
|
| |
| |
| |
| export class RateLimitError extends AppError { |
| |
| |
| |
| |
| constructor(message = '请求过于频繁', retryAfter = null) { |
| super(message, 429, 'rate_limit_error'); |
| this.name = 'RateLimitError'; |
| this.retryAfter = retryAfter; |
| } |
| } |
|
|
| |
| |
| |
| export class TokenError extends AppError { |
| |
| |
| |
| |
| |
| constructor(message, tokenSuffix = null, statusCode = 500) { |
| super(message, statusCode, 'token_error'); |
| this.name = 'TokenError'; |
| this.tokenSuffix = tokenSuffix; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| export function createApiError(message, status, rawBody) { |
| return new UpstreamApiError(message, status, rawBody); |
| } |
|
|
| |
| |
| |
| |
| |
| 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'; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| export function buildOpenAIErrorPayload(error, statusCode) { |
| |
| 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 |
| } |
| }; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| export function buildGeminiErrorPayload(error, statusCode) { |
| return { |
| error: { |
| code: statusCode, |
| message: extractErrorMessage(error), |
| status: "INTERNAL" |
| } |
| }; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| 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) |
| } |
| }; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| 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); |
| } |
|
|
| |
| |
| |
| |
| |
| export function asyncHandler(fn) { |
| return (req, res, next) => { |
| Promise.resolve(fn(req, res, next)).catch(next); |
| }; |
| } |