后端版本服务器部署成功
This commit is contained in:
@@ -14,7 +14,7 @@ const options = {
|
||||
description: '开发环境服务器'
|
||||
},
|
||||
{
|
||||
url: 'https://your-domain.com/api/v1',
|
||||
url: 'https://webapi.jiebanke.com/api/v1',
|
||||
description: '生产环境服务器'
|
||||
}
|
||||
],
|
||||
@@ -97,37 +97,26 @@ const options = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
success: {
|
||||
type: 'boolean',
|
||||
description: '请求是否成功'
|
||||
},
|
||||
code: {
|
||||
type: 'integer',
|
||||
description: '状态码'
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
description: '响应消息'
|
||||
type: 'boolean'
|
||||
},
|
||||
data: {
|
||||
type: 'object',
|
||||
description: '响应数据'
|
||||
type: 'object'
|
||||
},
|
||||
message: {
|
||||
type: 'string'
|
||||
},
|
||||
timestamp: {
|
||||
type: 'string',
|
||||
format: 'date-time'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
security: [
|
||||
{
|
||||
bearerAuth: []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
apis: [
|
||||
'./src/routes/*.js',
|
||||
'./src/controllers/*.js'
|
||||
]
|
||||
apis: ['src/routes/*.js', 'src/controllers/*.js']
|
||||
}
|
||||
|
||||
const specs = swaggerJsdoc(options)
|
||||
const swaggerSpec = swaggerJsdoc(options)
|
||||
|
||||
module.exports = specs
|
||||
module.exports = swaggerSpec
|
||||
@@ -1,10 +1,49 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
const Admin = require('../models/admin');
|
||||
const UserMySQL = require('../models/UserMySQL');
|
||||
const { AppError } = require('../utils/errors');
|
||||
|
||||
// 用户认证中间件
|
||||
function authenticateUser(req, res, next) {
|
||||
// TODO: 实现用户认证逻辑
|
||||
next();
|
||||
async function authenticateUser(req, res, next) {
|
||||
try {
|
||||
// 从请求头获取token
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
throw new AppError('未提供认证token', 401);
|
||||
}
|
||||
|
||||
const token = authHeader.split(' ')[1];
|
||||
|
||||
// 验证token
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'your-secret-key');
|
||||
|
||||
// 查找用户
|
||||
const user = await UserMySQL.findById(decoded.userId);
|
||||
if (!user) {
|
||||
throw new AppError('用户不存在', 401);
|
||||
}
|
||||
|
||||
// 检查用户状态
|
||||
if (!UserMySQL.isActive(user)) {
|
||||
throw new AppError('账户已被禁用', 403);
|
||||
}
|
||||
|
||||
// 将用户信息添加到请求对象
|
||||
req.user = UserMySQL.sanitize(user);
|
||||
req.userId = decoded.userId; // 同时设置userId,保持与现有控制器的兼容性
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
if (error.name === 'JsonWebTokenError') {
|
||||
throw new AppError('无效的认证token', 401);
|
||||
}
|
||||
|
||||
if (error.name === 'TokenExpiredError') {
|
||||
throw new AppError('认证token已过期', 401);
|
||||
}
|
||||
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 管理员认证中间件
|
||||
|
||||
@@ -89,7 +89,7 @@ class UserMySQL {
|
||||
|
||||
// 更新密码
|
||||
static async updatePassword(id, newPassword) {
|
||||
const sql = 'UPDATE users SET password = ?, updated_at = NOW() WHERE id = ?';
|
||||
const sql = 'UPDATE users SET password_hash = ?, updated_at = NOW() WHERE id = ?';
|
||||
const result = await query(sql, [newPassword, id]);
|
||||
return result.affectedRows > 0;
|
||||
}
|
||||
@@ -154,25 +154,11 @@ class UserMySQL {
|
||||
return rows[0].count > 0;
|
||||
}
|
||||
|
||||
// 检查用户名是否已存在
|
||||
static async isUsernameExists(username, excludeId = null) {
|
||||
let sql = 'SELECT COUNT(*) as count FROM users WHERE username = ?';
|
||||
const params = [username];
|
||||
|
||||
if (excludeId) {
|
||||
sql += ' AND id != ?';
|
||||
params.push(excludeId);
|
||||
}
|
||||
|
||||
const rows = await query(sql, params);
|
||||
return rows[0].count > 0;
|
||||
}
|
||||
|
||||
// 安全返回用户信息(去除敏感信息)
|
||||
static sanitize(user) {
|
||||
if (!user) return null;
|
||||
|
||||
const { password, ...safeUser } = user;
|
||||
const { password_hash, ...safeUser } = user;
|
||||
return safeUser;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ const { testConnection } = require('./config/database')
|
||||
const redisConfig = require('./config/redis')
|
||||
const rabbitMQConfig = require('./config/rabbitmq')
|
||||
|
||||
const PORT = process.env.PORT || 3100
|
||||
const PORT = process.env.PORT || 3200
|
||||
const HOST = process.env.HOST || '0.0.0.0'
|
||||
|
||||
// 显示启动横幅
|
||||
|
||||
@@ -23,6 +23,39 @@ const notFound = (req, res, next) => {
|
||||
next(error)
|
||||
}
|
||||
|
||||
// MySQL重复键错误处理
|
||||
const handleDuplicateFieldsDB = (err) => {
|
||||
// 提取重复的字段值
|
||||
let value = '未知字段'
|
||||
if (err.sqlMessage && err.sqlMessage.includes('Duplicate entry')) {
|
||||
const match = err.sqlMessage.match(/Duplicate entry '([^']+)' for key '([^']+)'/)
|
||||
if (match && match[1]) {
|
||||
value = match[1]
|
||||
}
|
||||
}
|
||||
const message = `字段值 ${value} 已存在,请使用其他值`
|
||||
return new AppError(message, 400)
|
||||
}
|
||||
|
||||
// MySQL验证错误处理
|
||||
const handleValidationErrorDB = (err) => {
|
||||
// MySQL验证错误通常在sqlMessage中包含详细信息
|
||||
const message = err.sqlMessage || '输入数据无效'
|
||||
return new AppError(message, 400)
|
||||
}
|
||||
|
||||
// JWT错误处理
|
||||
const handleJWTError = () =>
|
||||
new AppError('无效的token,请重新登录', 401)
|
||||
|
||||
const handleJWTExpiredError = () =>
|
||||
new AppError('token已过期,请重新登录', 401)
|
||||
|
||||
// MySQL连接错误处理
|
||||
const handleDBConnectionError = (err) => {
|
||||
return new AppError('数据库连接失败,请稍后再试', 503)
|
||||
}
|
||||
|
||||
// 全局错误处理中间件
|
||||
const globalErrorHandler = (err, req, res, next) => {
|
||||
err.statusCode = err.statusCode || 500
|
||||
@@ -38,35 +71,28 @@ const globalErrorHandler = (err, req, res, next) => {
|
||||
stack: err.stack
|
||||
})
|
||||
} else {
|
||||
// 生产环境:区分不同类型的错误并提供适当的响应
|
||||
let error = { ...err, message: err.message } // 创建错误副本
|
||||
|
||||
// 数据库错误处理
|
||||
if (error.code === 'ER_DUP_ENTRY') error = handleDuplicateFieldsDB(error)
|
||||
if (error.code === 'ER_NO_REFERENCED_ROW_2' || error.code === 'ER_BAD_NULL_ERROR') {
|
||||
error = handleValidationErrorDB(error)
|
||||
}
|
||||
if (error.code === 'ECONNREFUSED') error = handleDBConnectionError(error)
|
||||
|
||||
// JWT错误处理
|
||||
if (error.name === 'JsonWebTokenError') error = handleJWTError()
|
||||
if (error.name === 'TokenExpiredError') error = handleJWTExpiredError()
|
||||
|
||||
// 生产环境简化错误信息
|
||||
res.status(err.statusCode).json({
|
||||
status: err.status,
|
||||
message: err.message
|
||||
res.status(error.statusCode || 500).json({
|
||||
status: error.status || 'error',
|
||||
message: error.message || '服务器内部错误'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// MongoDB重复键错误处理
|
||||
const handleDuplicateFieldsDB = (err) => {
|
||||
const value = err.errmsg.match(/(["'])(\\?.)*?\1/)[0]
|
||||
const message = `字段值 ${value} 已存在,请使用其他值`
|
||||
return new AppError(message, 400)
|
||||
}
|
||||
|
||||
// MongoDB验证错误处理
|
||||
const handleValidationErrorDB = (err) => {
|
||||
const errors = Object.values(err.errors).map(el => el.message)
|
||||
const message = `输入数据无效: ${errors.join('. ')}`
|
||||
return new AppError(message, 400)
|
||||
}
|
||||
|
||||
// JWT错误处理
|
||||
const handleJWTError = () =>
|
||||
new AppError('无效的token,请重新登录', 401)
|
||||
|
||||
const handleJWTExpiredError = () =>
|
||||
new AppError('token已过期,请重新登录', 401)
|
||||
|
||||
module.exports = {
|
||||
AppError,
|
||||
catchAsync,
|
||||
@@ -75,5 +101,6 @@ module.exports = {
|
||||
handleDuplicateFieldsDB,
|
||||
handleValidationErrorDB,
|
||||
handleJWTError,
|
||||
handleJWTExpiredError
|
||||
handleJWTExpiredError,
|
||||
handleDBConnectionError
|
||||
}
|
||||
Reference in New Issue
Block a user