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
|
|||
|
};
|