后端版本服务器部署成功

This commit is contained in:
2025-09-11 13:11:04 +08:00
parent a1cd342c99
commit 9b7a0482e1
24 changed files with 1039 additions and 1157 deletions

View File

@@ -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

View File

@@ -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);
}
}
// 管理员认证中间件

View File

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

View File

@@ -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'
// 显示启动横幅

View File

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