274 lines
6.2 KiB
JavaScript
274 lines
6.2 KiB
JavaScript
const { Logger } = require('./logger');
|
||
|
||
/**
|
||
* 404错误处理中间件
|
||
*/
|
||
const notFoundHandler = (req, res, next) => {
|
||
const error = new Error(`找不到请求的资源: ${req.method} ${req.originalUrl}`);
|
||
error.status = 404;
|
||
error.code = 'NOT_FOUND';
|
||
|
||
Logger.warn('404错误', {
|
||
method: req.method,
|
||
url: req.originalUrl,
|
||
ip: req.ip,
|
||
userAgent: req.get('User-Agent')
|
||
});
|
||
|
||
next(error);
|
||
};
|
||
|
||
/**
|
||
* 全局错误处理中间件
|
||
*/
|
||
const globalErrorHandler = (err, req, res, next) => {
|
||
// 默认错误状态码
|
||
let statusCode = err.status || err.statusCode || 500;
|
||
let message = err.message || '服务器内部错误';
|
||
let code = err.code || 'INTERNAL_ERROR';
|
||
|
||
// 记录错误日志
|
||
Logger.error('全局错误处理', err, {
|
||
method: req.method,
|
||
url: req.originalUrl,
|
||
ip: req.ip,
|
||
userAgent: req.get('User-Agent'),
|
||
body: req.body,
|
||
params: req.params,
|
||
query: req.query
|
||
});
|
||
|
||
// 根据错误类型设置不同的响应
|
||
switch (err.name) {
|
||
case 'ValidationError':
|
||
statusCode = 400;
|
||
message = '请求参数验证失败';
|
||
code = 'VALIDATION_ERROR';
|
||
break;
|
||
|
||
case 'UnauthorizedError':
|
||
statusCode = 401;
|
||
message = '未授权访问';
|
||
code = 'UNAUTHORIZED';
|
||
break;
|
||
|
||
case 'JsonWebTokenError':
|
||
statusCode = 401;
|
||
message = 'Token无效';
|
||
code = 'INVALID_TOKEN';
|
||
break;
|
||
|
||
case 'TokenExpiredError':
|
||
statusCode = 401;
|
||
message = 'Token已过期';
|
||
code = 'TOKEN_EXPIRED';
|
||
break;
|
||
|
||
case 'CastError':
|
||
statusCode = 400;
|
||
message = '无效的ID格式';
|
||
code = 'INVALID_ID';
|
||
break;
|
||
|
||
case 'SyntaxError':
|
||
if (err.message.includes('JSON')) {
|
||
statusCode = 400;
|
||
message = 'JSON格式错误';
|
||
code = 'INVALID_JSON';
|
||
}
|
||
break;
|
||
}
|
||
|
||
// 数据库相关错误
|
||
if (err.message.includes('SQLITE_')) {
|
||
statusCode = 500;
|
||
message = '数据库操作失败';
|
||
code = 'DATABASE_ERROR';
|
||
|
||
// 具体的SQLite错误处理
|
||
if (err.message.includes('UNIQUE')) {
|
||
statusCode = 409;
|
||
message = '资源已存在';
|
||
code = 'RESOURCE_CONFLICT';
|
||
} else if (err.message.includes('FOREIGN KEY')) {
|
||
statusCode = 400;
|
||
message = '外键约束错误';
|
||
code = 'FOREIGN_KEY_ERROR';
|
||
}
|
||
}
|
||
|
||
// LLM API相关错误
|
||
if (err.message.includes('LLM') || err.message.includes('API')) {
|
||
if (err.message.includes('密钥')) {
|
||
statusCode = 503;
|
||
message = 'AI服务配置错误';
|
||
code = 'LLM_CONFIG_ERROR';
|
||
} else if (err.message.includes('网络') || err.message.includes('连接')) {
|
||
statusCode = 503;
|
||
message = 'AI服务连接失败';
|
||
code = 'LLM_CONNECTION_ERROR';
|
||
} else if (err.message.includes('频率')) {
|
||
statusCode = 429;
|
||
message = '请求过于频繁';
|
||
code = 'RATE_LIMIT_EXCEEDED';
|
||
}
|
||
}
|
||
|
||
// 构建错误响应
|
||
const errorResponse = {
|
||
success: false,
|
||
error: message,
|
||
code: code,
|
||
timestamp: new Date().toISOString(),
|
||
path: req.originalUrl,
|
||
method: req.method
|
||
};
|
||
|
||
// 开发环境包含更多错误信息
|
||
if (process.env.NODE_ENV === 'development') {
|
||
errorResponse.stack = err.stack;
|
||
errorResponse.details = {
|
||
name: err.name,
|
||
originalMessage: err.message
|
||
};
|
||
}
|
||
|
||
// 特殊处理:如果是流式请求,需要特殊处理
|
||
if (req.headers.accept && req.headers.accept.includes('text/plain')) {
|
||
res.status(statusCode).write(JSON.stringify({
|
||
type: 'error',
|
||
data: errorResponse
|
||
}) + '\n');
|
||
res.end();
|
||
return;
|
||
}
|
||
|
||
res.status(statusCode).json(errorResponse);
|
||
};
|
||
|
||
/**
|
||
* 异步错误包装器
|
||
*/
|
||
const asyncHandler = (fn) => {
|
||
return (req, res, next) => {
|
||
Promise.resolve(fn(req, res, next)).catch(next);
|
||
};
|
||
};
|
||
|
||
/**
|
||
* 验证错误处理器
|
||
*/
|
||
const validationErrorHandler = (errors) => {
|
||
const formattedErrors = errors.map(error => ({
|
||
field: error.param,
|
||
message: error.msg,
|
||
value: error.value
|
||
}));
|
||
|
||
const validationError = new Error('请求参数验证失败');
|
||
validationError.status = 400;
|
||
validationError.code = 'VALIDATION_ERROR';
|
||
validationError.details = formattedErrors;
|
||
|
||
return validationError;
|
||
};
|
||
|
||
/**
|
||
* 业务逻辑错误类
|
||
*/
|
||
class BusinessError extends Error {
|
||
constructor(message, statusCode = 400, code = 'BUSINESS_ERROR') {
|
||
super(message);
|
||
this.name = 'BusinessError';
|
||
this.status = statusCode;
|
||
this.code = code;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 资源未找到错误类
|
||
*/
|
||
class NotFoundError extends Error {
|
||
constructor(resource = '资源') {
|
||
super(`${resource}不存在`);
|
||
this.name = 'NotFoundError';
|
||
this.status = 404;
|
||
this.code = 'NOT_FOUND';
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 验证错误类
|
||
*/
|
||
class ValidationError extends Error {
|
||
constructor(message, field = null) {
|
||
super(message);
|
||
this.name = 'ValidationError';
|
||
this.status = 400;
|
||
this.code = 'VALIDATION_ERROR';
|
||
this.field = field;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 权限错误类
|
||
*/
|
||
class ForbiddenError extends Error {
|
||
constructor(message = '权限不足') {
|
||
super(message);
|
||
this.name = 'ForbiddenError';
|
||
this.status = 403;
|
||
this.code = 'FORBIDDEN';
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 服务不可用错误类
|
||
*/
|
||
class ServiceUnavailableError extends Error {
|
||
constructor(service = '服务') {
|
||
super(`${service}暂时不可用`);
|
||
this.name = 'ServiceUnavailableError';
|
||
this.status = 503;
|
||
this.code = 'SERVICE_UNAVAILABLE';
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 进程退出前的清理处理
|
||
*/
|
||
const gracefulShutdown = () => {
|
||
process.on('SIGTERM', () => {
|
||
Logger.info('收到SIGTERM信号,开始优雅关闭...');
|
||
process.exit(0);
|
||
});
|
||
|
||
process.on('SIGINT', () => {
|
||
Logger.info('收到SIGINT信号,开始优雅关闭...');
|
||
process.exit(0);
|
||
});
|
||
|
||
process.on('uncaughtException', (err) => {
|
||
Logger.error('未捕获的异常', err);
|
||
process.exit(1);
|
||
});
|
||
|
||
process.on('unhandledRejection', (reason, promise) => {
|
||
Logger.error('未处理的Promise拒绝', new Error(reason), { promise });
|
||
process.exit(1);
|
||
});
|
||
};
|
||
|
||
module.exports = {
|
||
notFoundHandler,
|
||
globalErrorHandler,
|
||
asyncHandler,
|
||
validationErrorHandler,
|
||
gracefulShutdown,
|
||
// 错误类
|
||
BusinessError,
|
||
NotFoundError,
|
||
ValidationError,
|
||
ForbiddenError,
|
||
ServiceUnavailableError
|
||
}; |