From 9b7a0482e18c8e68c5e9dc9bbf0c1d79cd74a7de Mon Sep 17 00:00:00 2001 From: aiotagro Date: Thu, 11 Sep 2025 13:11:04 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=8E=E7=AB=AF=E7=89=88=E6=9C=AC=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=99=A8=E9=83=A8=E7=BD=B2=E6=88=90=E5=8A=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/.env | 2 +- backend/.env.example | 2 +- backend/README_DEVELOPMENT.md | 249 --------------------------- backend/README_SCRIPTS.md | 181 -------------------- backend/api-documentation.md | 290 ++++++++++++++++++++++++++++++++ backend/cleanup_files.sh | 134 +++++++++++++++ backend/config/env.js | 6 +- backend/docker-compose.yml | 26 --- backend/ecosystem.config.js | 4 +- backend/jiebanke.conf | 129 -------------- backend/jiebanke_nginx.conf | 110 ++++++++++++ backend/restart.sh | 107 ------------ backend/src/config/swagger.js | 39 ++--- backend/src/middleware/auth.js | 45 ++++- backend/src/models/UserMySQL.js | 18 +- backend/src/server.js | 2 +- backend/src/utils/errors.js | 77 ++++++--- backend/start.sh | 93 ---------- backend/start_server.sh | 278 ++++++++++++++++++++++++++++++ backend/status.sh | 90 ---------- backend/stop.sh | 72 -------- backend/sync_to_server.sh | 109 ++++++++++++ backend/test-api.js | 103 ------------ backend/test-swagger.js | 30 ---- 24 files changed, 1039 insertions(+), 1157 deletions(-) delete mode 100644 backend/README_DEVELOPMENT.md delete mode 100644 backend/README_SCRIPTS.md create mode 100644 backend/api-documentation.md create mode 100755 backend/cleanup_files.sh delete mode 100644 backend/docker-compose.yml delete mode 100644 backend/jiebanke.conf create mode 100644 backend/jiebanke_nginx.conf delete mode 100755 backend/restart.sh delete mode 100755 backend/start.sh create mode 100755 backend/start_server.sh delete mode 100755 backend/status.sh delete mode 100755 backend/stop.sh create mode 100755 backend/sync_to_server.sh delete mode 100644 backend/test-api.js delete mode 100644 backend/test-swagger.js diff --git a/backend/.env b/backend/.env index 14e4d44..0693543 100644 --- a/backend/.env +++ b/backend/.env @@ -1,6 +1,6 @@ # 服务器配置 NODE_ENV=development -PORT=3000 +PORT=3200 HOST=0.0.0.0 # 数据库配置 diff --git a/backend/.env.example b/backend/.env.example index 62649cb..158630f 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -1,6 +1,6 @@ # 服务器配置 NODE_ENV=development -PORT=3000 +PORT=3200 HOST=0.0.0.0 # 数据库配置 diff --git a/backend/README_DEVELOPMENT.md b/backend/README_DEVELOPMENT.md deleted file mode 100644 index 2dce018..0000000 --- a/backend/README_DEVELOPMENT.md +++ /dev/null @@ -1,249 +0,0 @@ -# 结伴客后端开发完善指南 - -## 📋 完善内容概述 - -本次后端开发完善主要包含以下内容: - -### 1. 环境配置优化 -- ✅ 更新环境配置文件 (`config/env.js`),移除MongoDB配置,完善MySQL配置 -- ✅ 更新环境变量示例文件 (`.env.example`),添加MySQL相关配置 -- ✅ 数据库配置文件 (`src/config/database.js`) 现在使用环境配置而非硬编码 - -### 2. 测试数据支持 -- ✅ 创建测试数据初始化脚本 (`scripts/init-test-data.js`) -- ✅ 提供标准测试账号(管理员、运营、普通用户、商家用户) -- ✅ 支持密码加密存储(bcrypt) - -### 3. 自动化测试脚本 -- ✅ 创建API端点测试脚本 (`scripts/test-api-endpoints.js`) -- ✅ 创建数据库连接测试脚本 (`scripts/test-database-connection.js`) -- ✅ 支持完整的测试用例和结果统计 - -### 4. 开发工具链完善 -- ✅ 更新package.json脚本命令 -- ✅ 提供一键测试和数据初始化功能 - -## 🚀 快速开始 - -### 环境准备 -```bash -# 复制环境配置文件 -cp .env.example .env - -# 安装依赖 -npm install -``` - -### 数据库配置 -编辑 `.env` 文件,配置MySQL数据库连接: - -```env -# 数据库配置 -DB_HOST=mysql.jiebanke.com -DB_PORT=3306 -DB_USER=root -DB_PASSWORD=your-mysql-password -DB_NAME=jiebandata - -# 连接池配置 -DB_CONNECTION_LIMIT=10 -DB_CHARSET=utf8mb4 -DB_TIMEZONE=+08:00 -``` - -### 开发命令 - -```bash -# 启动开发服务器 -npm run dev - -# 测试数据库连接 -npm run test-db - -# 初始化测试数据 -npm run init-test-data - -# 测试API端点 -npm run test-api - -# 运行单元测试 -npm test - -# 代码检查 -npm run lint -``` - -## 📊 测试账号 - -初始化后会创建以下测试账号: - -| 角色 | 用户名 | 密码 | 描述 | -|------|--------|------|------| -| 超级管理员 | admin | admin123 | 系统最高权限 | -| 运营经理 | manager | manager123 | 日常运营管理 | -| 普通用户 | user1 | user123 | 旅行爱好者 | -| 商家用户 | merchant1 | merchant123 | 农家乐老板 | - -## 🔧 API测试 - -API测试脚本会自动测试以下接口: - -### 管理员接口 -- ✅ POST `/api/v1/admin/login` - 管理员登录 -- ✅ GET `/api/v1/admin/profile` - 获取管理员信息 -- ✅ GET `/api/v1/admin/list` - 获取管理员列表 - -### 用户接口 -- ✅ POST `/api/v1/auth/login` - 用户登录 -- ✅ GET `/api/v1/users/profile` - 获取用户信息 - -### 系统接口 -- ✅ GET `/health` - 健康检查 -- ✅ GET `/system-stats` - 系统统计 - -## 🗄️ 数据库配置 - -### 开发环境 -```javascript -{ - host: '192.168.0.240', - port: 3306, - user: 'root', - password: 'aiotAiot123!', - database: 'jiebandata', - connectionLimit: 10 -} -``` - -### 测试环境 -```javascript -{ - host: '192.168.0.240', - port: 3306, - user: 'root', - password: 'aiotAiot123!', - database: 'jiebandata_test', - connectionLimit: 5 -} -``` - -### 生产环境 -```javascript -{ - host: 'nj-cdb-3pwh2kz1.sql.tencentcdb.com', - port: 20784, - user: 'jiebanke', - password: 'aiot741$12346', - database: 'jbkdata', - connectionLimit: 20 -} -``` - -## ⚡ 生产环境连接说明 - - -### 注意事项 -1. **谨慎操作**: 直接连接生产数据库,所有操作都会影响真实数据 -2. **备份优先**: 在执行任何修改操作前,建议先备份数据 -3. **权限控制**: 确保只有授权人员可以访问生产环境 -4. **监控日志**: 密切监控数据库操作日志,及时发现异常 -5. **连接限制**: 生产环境连接数限制为20,避免过度消耗资源 - -### 安全建议 -- 使用VPN连接生产环境 -- 启用SSL加密连接 -- 定期更换密码 -- 实施IP白名单限制 -- 启用数据库审计功能 - -### 连接问题排查 -如果遇到连接问题,请检查: - -1. **密码验证**: 确认生产服务器MySQL的root密码是否为'Aiot123' -2. **权限配置**: 检查MySQL用户权限设置,确保允许从当前IP连接 -3. **防火墙**: 确认服务器防火墙已开放9527端口 -4. **网络连通性**: 使用telnet或ping测试网络连接 - -### 当前连接状态 -- ❌ 生产服务器(129.211.213.226:9527): 权限被拒绝(ER_ACCESS_DENIED_ERROR) -- ❌ 开发服务器(192.168.0.240:3306): 连接超时(ETIMEDOUT) - -### 本地开发解决方案 -推荐使用Docker本地MySQL进行开发: - -1. **启动本地MySQL容器** -```bash -cd /Users/ainongkeji/code/vue/jiebanke/backend -docker-compose up -d mysql -``` - -2. **配置本地环境变量** -```bash -# 使用本地Docker MySQL -export DB_HOST=mysql.jiebanke.com -export DB_PORT=3306 -export DB_PASSWORD=rootpassword -export DB_DATABASE=jiebanke_dev -``` - -3. **初始化数据库** -```bash -npm run db:reset # 重置并初始化数据库 -npm run db:seed # 填充测试数据 -``` - -### 生产环境连接说明 -如需连接生产环境,请联系运维团队: -- 确认生产服务器MySQL root密码 -- 检查IP白名单配置 -- 验证网络连通性 -- 确认防火墙规则 - -### 紧急开发方案 -如果所有远程服务器都无法连接,可以使用SQLite进行临时开发: -```bash -export DB_DIALECT=sqlite -export DB_STORAGE=./database.sqlite -npm run dev -``` - -## 📝 开发规范 - -### 代码风格 -- 使用ESLint进行代码检查 -- 遵循JavaScript标准风格 -- 使用async/await处理异步操作 - -### 安全规范 -- 密码使用bcrypt加密存储 -- 使用环境变量存储敏感信息 -- 实施SQL注入防护 -- 启用CORS和HTTPS - -### 日志规范 -- 开发环境使用详细日志 -- 生产环境使用合并日志 -- 记录关键操作和错误信息 - -## 🐛 常见问题 - -### Q: 数据库连接失败 -A: 检查MySQL服务是否启动,配置是否正确 - -### Q: 测试数据初始化失败 -A: 确保数据库表结构已创建,可先运行迁移脚本 - -### Q: API测试失败 -A: 确认后端服务已启动,检查网络连接 - -### Q: 权限不足 -A: 检查数据库用户权限,确认有足够的操作权限 - -## 📞 技术支持 - -如有问题请联系开发团队或查看详细文档。 - ---- - -**最后更新: 2024年** -**版本: 1.0.0** \ No newline at end of file diff --git a/backend/README_SCRIPTS.md b/backend/README_SCRIPTS.md deleted file mode 100644 index b82f278..0000000 --- a/backend/README_SCRIPTS.md +++ /dev/null @@ -1,181 +0,0 @@ -# 结伴客后端服务管理脚本 - -## 概述 - -本目录包含用于管理结伴客后端服务的一组脚本,包括启动、停止、重启和状态检查等功能。 - -## 脚本说明 - -### start.sh - 启动脚本 - -用于启动结伴客后端服务。 - -#### 使用方法 - -```bash -# 生产模式启动(默认) -./start.sh - -# 开发模式启动(支持热重载) -./start.sh dev - -# 显示帮助信息 -./start.sh help -``` - -#### 功能特点 - -- 自动检查并安装依赖(如果未安装) -- 自动复制环境变量文件(如果不存在) -- 支持生产模式和开发模式 -- 开发模式下优先使用 nodemon(如果已安装) - -### stop.sh - 停止脚本 - -用于停止正在运行的结伴客后端服务。 - -#### 使用方法 - -```bash -# 停止服务 -./stop.sh - -# 显示帮助信息 -./stop.sh help -``` - -#### 功能特点 - -- 自动查找并停止所有相关的后端服务进程 -- 优雅地停止进程,超时后强制终止 -- 显示详细的进程停止信息 - -### restart.sh - 重启脚本 - -用于重启结伴客后端服务。 - -#### 使用方法 - -```bash -# 生产模式重启(默认) -./restart.sh - -# 开发模式重启 -./restart.sh dev - -# 显示帮助信息 -./restart.sh help -``` - -#### 功能特点 - -- 结合了 stop.sh 和 start.sh 的所有功能 -- 支持生产模式和开发模式 -- 在停止和启动之间添加了延迟以确保服务完全停止 - -### status.sh - 状态检查脚本 - -用于检查结伴客后端服务的运行状态。 - -#### 使用方法 - -```bash -# 检查服务状态 -./status.sh - -# 显示详细信息 -./status.sh detail - -# 显示帮助信息 -./status.sh help -``` - -#### 功能特点 - -- 显示服务是否正在运行 -- 显示相关的进程信息 -- 详细模式下显示端口占用、工作目录等信息 - -## 使用示例 - -### 日常使用 - -```bash -# 进入后端目录 -cd backend - -# 启动服务 -./start.sh - -# 检查服务状态 -./status.sh - -# 重启服务 -./restart.sh - -# 停止服务 -./stop.sh -``` - -### 开发环境使用 - -```bash -# 进入后端目录 -cd backend - -# 以开发模式启动(支持热重载) -./start.sh dev - -# 检查服务状态 -./status.sh - -# 重启服务(保持开发模式) -./restart.sh dev - -# 停止服务 -./stop.sh -``` - -## 注意事项 - -1. 首次运行脚本前,请确保已安装 Node.js 和 npm -2. 脚本会自动检查依赖并安装(如果未安装) -3. 如果没有安装 nodemon,开发模式将回退到使用 node -4. 脚本需要在后端项目根目录下运行 -5. 确保运行脚本的用户具有足够的权限 - -## 故障排除 - -### 权限问题 - -如果遇到权限问题,请为脚本添加执行权限: - -```bash -chmod +x start.sh stop.sh restart.sh status.sh -``` - -或者使用 npm 命令: - -```bash -npm run start-scripts -``` - -### 服务无法启动 - -1. 检查端口是否被占用: - ```bash - netstat -tlnp | grep :3000 - ``` - -2. 检查环境变量配置是否正确 - -3. 查看详细日志信息 - -### 服务无法停止 - -1. 脚本会自动等待10秒后强制终止进程 -2. 如果仍有问题,可以手动终止进程: - ```bash - ps aux | grep "node src/server.js" - kill -9 - ``` \ No newline at end of file diff --git a/backend/api-documentation.md b/backend/api-documentation.md new file mode 100644 index 0000000..aaa0c4e --- /dev/null +++ b/backend/api-documentation.md @@ -0,0 +1,290 @@ +# 结伴客后端API文档 + +## 1. 认证相关接口 + +### 用户注册 + +**请求方法**: POST +**请求路径**: `/api/v1/auth/register` +**请求体**: +```json +{ + "username": "string", // 用户名(必需) + "password": "string", // 密码(必需,至少6位) + "nickname": "string", // 昵称(可选) + "email": "string", // 邮箱(可选) + "phone": "string" // 手机号(可选) +} +``` +**响应**: +```json +{ + "success": true, + "data": { + "user": { // 用户信息(不含敏感信息) + "id": 1, + "username": "testuser", + "nickname": "测试用户", + "email": "test@jiebanke.com", + "phone": "13800000000", + // 其他用户字段... + }, + "token": "string" + }, + "message": "注册成功" +} +``` +**错误响应**: +- 400: 用户名已存在 / 邮箱已存在 / 手机号已存在 / 密码长度不能少于6位 +- 500: 服务器内部错误 + +### 用户登录 + +**请求方法**: POST +**请求路径**: `/api/v1/auth/login` +**请求体**: +```json +{ + "username": "string", // 用户名/邮箱/手机号(必需) + "password": "string" // 密码(必需) +} +``` +**响应**: +```json +{ + "success": true, + "data": { + "user": { // 用户信息(不含敏感信息) + "id": 1, + "username": "testuser", + "nickname": "测试用户", + "email": "test@jiebanke.com", + "phone": "13800000000", + // 其他用户字段... + }, + "token": "string" + }, + "message": "登录成功" +} +``` +**错误响应**: +- 400: 用户名和密码不能为空 +- 401: 密码错误 +- 403: 账户已被禁用 +- 404: 用户不存在 +- 500: 服务器内部错误 + +## 2. 用户管理接口 + +### 获取当前用户信息 + +**请求方法**: GET +**请求路径**: `/api/v1/users/profile` +**请求头**: `Authorization: Bearer {token}` +**响应**: +```json +{ + "success": true, + "data": { + "user": { // 用户信息(不含敏感信息) + "id": 1, + "username": "testuser", + "nickname": "测试用户", + "email": "test@jiebanke.com", + "phone": "13800000000", + // 其他用户字段... + } + } +} +``` +**错误响应**: +- 401: 未提供认证token / 无效的认证token / token已过期 / 用户不存在 +- 403: 账户已被禁用 +- 500: 服务器内部错误 + +### 更新用户个人信息 + +**请求方法**: PUT +**请求路径**: `/api/v1/users/profile` +**请求头**: `Authorization: Bearer {token}` +**请求体**: +```json +{ + "nickname": "string", // 昵称(可选) + "avatar": "string", // 头像URL(可选) + "gender": "string", // 性别(可选,可选值:male, female, other) + "birthday": "string", // 生日(可选,格式:YYYY-MM-DD) + "phone": "string", // 手机号(可选) + "email": "string" // 邮箱(可选) +} +``` +**响应**: +```json +{ + "success": true, + "data": { + "user": { // 更新后的用户信息(不含敏感信息) + "id": 1, + "username": "testuser", + "nickname": "新昵称", + "email": "new@email.com", + "phone": "13900000000", + // 其他用户字段... + }, + "message": "个人信息更新成功" + } +} +``` +**错误响应**: +- 400: 没有有效的更新字段 / 邮箱已被其他用户使用 / 手机号已被其他用户使用 +- 401: 未提供认证token / 无效的认证token / token已过期 / 用户不存在 +- 403: 账户已被禁用 +- 500: 更新用户信息失败 / 服务器内部错误 + +## 3. 管理员接口 + +### 搜索用户 + +**请求方法**: GET +**请求路径**: `/api/v1/admin/users/search` +**请求头**: `Authorization: Bearer {token}` +**请求参数**: +- keyword: 搜索关键词(可选,模糊匹配用户名、昵称、邮箱、手机号) +- userType: 用户类型(可选) +- page: 当前页码(可选,默认:1) +- pageSize: 每页记录数(可选,默认:10) + +**响应**: +```json +{ + "success": true, + "data": { + "users": [ + { + "id": 1, + "username": "testuser", + "user_type": "farmer", + "real_name": "测试用户", + "avatar_url": "", + "email": "test@jiebanke.com", + "phone": "13800000000", + "status": "active", + "created_at": "2024-01-01T12:00:00Z", + "updated_at": "2024-01-01T12:00:00Z" + } + // 更多用户... + ], + "total": 1, + "page": 1, + "pageSize": 10, + "pages": 1 + } +} +``` +**错误响应**: +- 401: 未提供认证token / 无效的认证token / token已过期 / 管理员不存在 +- 403: 管理员账号已被禁用 / 需要管理员权限 / 权限不足 +- 500: 服务器内部错误 + +### 批量更新用户状态 + +**请求方法**: POST +**请求路径**: `/api/v1/admin/users/batch-status` +**请求头**: `Authorization: Bearer {token}` +**请求体**: +```json +{ + "userIds": [1, 2, 3], // 用户ID列表(必需) + "status": "active" // 状态(必需,可选值:active, inactive) +} +``` +**响应**: +```json +{ + "success": true, + "data": { + "message": "成功更新 3 个用户状态", + "affectedRows": 3 + } +} +``` +**错误响应**: +- 400: 请选择要操作的用户 / 无效的状态值 +- 401: 未提供认证token / 无效的认证token / token已过期 / 管理员不存在 +- 403: 管理员账号已被禁用 / 需要管理员权限 / 权限不足 +- 500: 服务器内部错误 + +## 4. 系统接口 + +### 健康检查 + +**请求方法**: GET +**请求路径**: `/health` +**响应**: +```json +{ + "status": "OK", + "timestamp": "2024-01-01T12:00:00Z", + "uptime": 1234.56, + "environment": "production" +} +``` + +### 系统统计 + +**请求方法**: GET +**请求路径**: `/system-stats` +**响应**: +```json +{ + "status": "OK", + "timestamp": "2024-01-01T12:00:00Z", + "environment": "production", + "nodeVersion": "v18.16.0", + "memoryUsage": { + "rss": 123456789, + "heapTotal": 12345678, + "heapUsed": 1234567, + "external": 123456 + }, + "uptime": 1234.56, + "cpuCount": 8, + "platform": "linux", + "architecture": "x64" +} +``` + +## 5. 错误码说明 + +| 错误码 | 描述 | HTTP状态码 | +|-------|------|-----------| +| 400 | 请求参数错误 | 400 | +| 401 | 未授权(无效token、token过期等) | 401 | +| 403 | 权限不足或账户被禁用 | 403 | +| 404 | 资源不存在 | 404 | +| 429 | 请求过于频繁 | 429 | +| 500 | 服务器内部错误 | 500 | +| 503 | 服务不可用(如数据库连接失败) | 503 | + +## 6. 认证机制 + +系统使用JWT(JSON Web Token)进行认证,所有需要认证的接口都需要在请求头中包含`Authorization: Bearer {token}`。 + +### Token有效期 +- 默认有效期为7天 +- 可通过环境变量`JWT_EXPIRE`自定义 + +### Token安全 +- 生产环境请确保设置安全的`JWT_SECRET`环境变量 +- 避免在客户端存储敏感信息 + +## 7. 接口限制 + +- 接口请求频率限制:生产环境15分钟内最多100次请求,开发环境15分钟内最多1000次请求 +- 请求体大小限制:10kb +- 支持的请求内容类型:application/json + +## 8. Swagger文档 + +在开发环境或设置`ENABLE_SWAGGER=true`环境变量时,可通过以下地址访问Swagger文档: +- https://webapi.jiebanke.com/api-docs \ No newline at end of file diff --git a/backend/cleanup_files.sh b/backend/cleanup_files.sh new file mode 100755 index 0000000..ad40128 --- /dev/null +++ b/backend/cleanup_files.sh @@ -0,0 +1,134 @@ +#!/bin/bash + +# 结伴客后端文件清理脚本 +# 删除不需要的文件,保持项目结构整洁 + +# 设置颜色输出 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# 配置参数 +BACKEND_DIR="$(pwd)" + +# 显示警告信息 +show_warning() { + echo -e "${YELLOW}警告:此脚本将删除backend目录中不需要的文件。${NC}" + echo -e "${YELLOW}请确保在运行此脚本前已备份重要数据。${NC}" + echo -e "${BLUE}按Enter键继续,或按Ctrl+C取消...${NC}" + read -r +} + +# 删除指定文件 +remove_file() { + local file_path="$1" + if [ -f "$file_path" ]; then + echo -e "${BLUE}删除文件: $file_path${NC}" + rm -f "$file_path" + if [ $? -eq 0 ]; then + echo -e "${GREEN}已删除: $file_path${NC}" + else + echo -e "${RED}删除失败: $file_path${NC}" + fi + else + echo -e "${YELLOW}文件不存在: $file_path${NC}" + fi +} + +# 删除指定目录(如果为空) +remove_empty_dir() { + local dir_path="$1" + if [ -d "$dir_path" ] && [ -z "$(ls -A "$dir_path")" ]; then + echo -e "${BLUE}删除空目录: $dir_path${NC}" + rm -d "$dir_path" + if [ $? -eq 0 ]; then + echo -e "${GREEN}已删除: $dir_path${NC}" + else + echo -e "${RED}删除失败: $dir_path${NC}" + fi + fi +} + +# 清理测试文件 +cleanup_test_files() { + echo -e "${BLUE}\n清理测试相关文件...${NC}" + remove_file "$BACKEND_DIR/test-api.js" + remove_file "$BACKEND_DIR/test-swagger.js" + # 可以保留scripts目录下的测试脚本,因为它们可能对开发有用 +} + +# 清理文档文件 +cleanup_docs() { + echo -e "${BLUE}\n清理文档文件...${NC}" + remove_file "$BACKEND_DIR/README_DEVELOPMENT.md" + remove_file "$BACKEND_DIR/README_SCRIPTS.md" + # 保留主要的README.md文件 +} + +# 清理Docker相关文件 +cleanup_docker_files() { + echo -e "${BLUE}\n清理Docker相关文件...${NC}" + remove_file "$BACKEND_DIR/docker-compose.yml" + remove_file "$BACKEND_DIR/jiebanke.conf" +} + +# 清理旧的启动脚本 +cleanup_old_scripts() { + echo -e "${BLUE}\n清理旧的启动脚本...${NC}" + remove_file "$BACKEND_DIR/start.sh" + remove_file "$BACKEND_DIR/stop.sh" + remove_file "$BACKEND_DIR/restart.sh" + remove_file "$BACKEND_DIR/status.sh" +} + +# 清理临时文件和缓存 +cleanup_temp_files() { + echo -e "${BLUE}\n清理临时文件和缓存...${NC}" + find "$BACKEND_DIR" -name "*.log" -exec rm -f {} \; + find "$BACKEND_DIR" -name "*.tmp" -exec rm -f {} \; + find "$BACKEND_DIR" -name "*.temp" -exec rm -f {} \; + find "$BACKEND_DIR" -name ".DS_Store" -exec rm -f {} \; +} + +# 验证并设置脚本权限 +set_script_permissions() { + echo -e "${BLUE}\n设置脚本执行权限...${NC}" + # 为新创建的脚本设置执行权限 + chmod +x "$BACKEND_DIR/sync_to_server.sh" 2>/dev/null + chmod +x "$BACKEND_DIR/start_server.sh" 2>/dev/null + chmod +x "$BACKEND_DIR/cleanup_files.sh" 2>/dev/null + echo -e "${GREEN}脚本权限设置完成${NC}" +} + +# 显示清理结果摘要 +show_summary() { + echo -e "${GREEN}\n文件清理完成!${NC}" + echo -e "${YELLOW}剩余核心文件:${NC}" + echo -e " - package.json, package-lock.json (项目依赖配置)" + echo -e " - .env, .env.example (环境配置)" + echo -e " - ecosystem.config.js (PM2配置)" + echo -e " - src/ (源代码目录)" + echo -e " - config/ (配置目录)" + echo -e " - scripts/ (脚本目录)" + echo -e " - README.md (项目说明)" + echo -e " - sync_to_server.sh (新的同步脚本)" + echo -e " - start_server.sh (新的服务器启动脚本)" + echo -e " - cleanup_files.sh (此清理脚本)" +} + +# 执行清理 +main() { + show_warning + cleanup_test_files + cleanup_docs + cleanup_docker_files + cleanup_old_scripts + cleanup_temp_files + set_script_permissions + show_summary +} + +# 执行主函数 +main \ No newline at end of file diff --git a/backend/config/env.js b/backend/config/env.js index a0597b5..655b968 100644 --- a/backend/config/env.js +++ b/backend/config/env.js @@ -5,7 +5,7 @@ require('dotenv').config({ path: path.join(__dirname, '../../.env') }) const config = { // 开发环境 development: { - port: process.env.PORT || 3100, + port: process.env.PORT || 3200, mysql: { host: process.env.DB_HOST || 'nj-cdb-3pwh2kz1.sql.tencentcdb.com', port: process.env.DB_PORT || 20784, @@ -38,7 +38,7 @@ const config = { // 测试环境 test: { - port: process.env.PORT || 3100, + port: process.env.PORT || 3200, mysql: { host: process.env.DB_HOST || 'nj-cdb-3pwh2kz1.sql.tencentcdb.com', port: process.env.DB_PORT || 20784, @@ -62,7 +62,7 @@ const config = { // 生产环境 production: { - port: process.env.PORT || 3100, + port: process.env.PORT || 3200, mysql: { host: process.env.DB_HOST || 'nj-cdb-3pwh2kz1.sql.tencentcdb.com', port: process.env.DB_PORT || 20784, diff --git a/backend/docker-compose.yml b/backend/docker-compose.yml deleted file mode 100644 index 9350c0b..0000000 --- a/backend/docker-compose.yml +++ /dev/null @@ -1,26 +0,0 @@ -version: '3.8' - -services: - mysql: - image: mysql:8.0 - container_name: jiebanke-mysql - environment: - MYSQL_ROOT_PASSWORD: rootpassword - MYSQL_DATABASE: jiebanke_dev - MYSQL_USER: jiebanke_user - MYSQL_PASSWORD: jiebanke_pass - ports: - - "3306:3306" - volumes: - - mysql_data:/var/lib/mysql - - ./scripts/init-database.sql:/docker-entrypoint-initdb.d/init.sql - restart: unless-stopped - healthcheck: - test: ["CMD", "mysqladmin", "ping", "-h", "mysql.jiebanke.com", "-u", "root", "-p$$MYSQL_ROOT_PASSWORD"] - interval: 10s - timeout: 5s - retries: 3 - -volumes: - mysql_data: - driver: local \ No newline at end of file diff --git a/backend/ecosystem.config.js b/backend/ecosystem.config.js index dfd56fb..bb26ffc 100644 --- a/backend/ecosystem.config.js +++ b/backend/ecosystem.config.js @@ -6,12 +6,12 @@ module.exports = { exec_mode: 'cluster', env: { NODE_ENV: 'development', - PORT: 3000, + PORT: 3200, WATCH: true }, env_production: { NODE_ENV: 'production', - PORT: 3000, + PORT: 3200, WATCH: false }, env_test: { diff --git a/backend/jiebanke.conf b/backend/jiebanke.conf deleted file mode 100644 index c46c1cf..0000000 --- a/backend/jiebanke.conf +++ /dev/null @@ -1,129 +0,0 @@ -# Nginx配置文件 - 结伴客后端服务 -# 适用于Node.js Express应用 - -# 定义Node.js应用服务器 -upstream nodejs_backend { - server 127.0.0.1:3100; - keepalive 64; -} - -# HTTP服务器配置 -server { - listen 80; - server_name webapi.jiebanke.com; - - # 重定向所有HTTP请求到HTTPS - return 301 https://$server_name$request_uri; -} - -# HTTPS服务器配置 -server { - listen 443 ssl http2; - server_name webapi.jiebanke.com; - - # SSL证书配置(需要替换为实际证书路径) - ssl_certificate /path/to/your/certificate.crt; - ssl_certificate_key /path/to/your/private.key; - - # SSL安全配置 - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384; - ssl_prefer_server_ciphers off; - - # 启用HSTS - add_header Strict-Transport-Security "max-age=31536000" always; - - # 客户端上传大小限制 - client_max_body_size 10M; - - # 日志配置 - access_log /var/log/nginx/webapi.jiebanke.com.access.log; - error_log /var/log/nginx/webapi.jiebanke.com.error.log; - - # API接口代理 - location /api/ { - proxy_pass http://nodejs_backend; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_cache_bypass $http_upgrade; - proxy_read_timeout 90; - } - - # 管理员API接口代理 - location /admin/ { - proxy_pass http://nodejs_backend; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_cache_bypass $http_upgrade; - proxy_read_timeout 90; - } - - # 健康检查端点 - location /health { - proxy_pass http://nodejs_backend; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_cache_bypass $http_upgrade; - } - - # Swagger API文档 - location /api-docs { - proxy_pass http://nodejs_backend; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_cache_bypass $http_upgrade; - } - - # 上传文件访问 - location /uploads/ { - proxy_pass http://nodejs_backend; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_cache_bypass $http_upgrade; - } - - # 默认根路径处理 - location / { - proxy_pass http://nodejs_backend; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_cache_bypass $http_upgrade; - } - - # 安全头设置 - add_header X-Frame-Options "SAMEORIGIN" always; - add_header X-XSS-Protection "1; mode=block" always; - add_header X-Content-Type-Options "nosniff" always; - add_header Referrer-Policy "no-referrer-when-downgrade" always; - add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always; -} \ No newline at end of file diff --git a/backend/jiebanke_nginx.conf b/backend/jiebanke_nginx.conf new file mode 100644 index 0000000..1eaf726 --- /dev/null +++ b/backend/jiebanke_nginx.conf @@ -0,0 +1,110 @@ +# 结伴客后端Nginx配置文件 +# SSL配置和webapi.jiebanke.com域名设置 + +# HTTP服务器配置 - 重定向到HTTPS +server { + listen 80; + server_name webapi.jiebanke.com; + + # 强制HTTPS重定向 + return 301 https://$server_name$request_uri; +} + +# HTTPS服务器配置 +server { + # 监听443端口并启用SSL + listen 443 ssl http2; + server_name webapi.jiebanke.com; + + # SSL证书配置 + ssl_certificate /etc/nginx/ssl/webapi.jiebanke.com.crt; + ssl_certificate_key /etc/nginx/ssl/webapi.jiebanke.com.key; + + # SSL优化配置 + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers off; + ssl_session_timeout 1d; + ssl_session_cache shared:SSL:10m; + ssl_session_tickets off; + + # HSTS配置 + add_header Strict-Transport-Security "max-age=63072000" always; + + # 安全头部配置 + add_header X-Frame-Options DENY; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + add_header Referrer-Policy "no-referrer-when-downgrade"; + + # 访问日志配置 + access_log /var/log/nginx/webapi_access.log; + error_log /var/log/nginx/webapi_error.log; + + # 代理配置 + location / { + # 代理到Node.js后端服务 + proxy_pass http://localhost:3200; + + # 代理头信息配置 + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # WebSocket支持 + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + + # 代理超时设置 + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 120s; + + # 缓冲区设置 + proxy_buffering on; + proxy_buffer_size 8k; + proxy_buffers 8 16k; + proxy_busy_buffers_size 16k; + } + + # 健康检查端点 + location /health { + access_log off; + proxy_pass http://localhost:3200/health; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + # API文档端点 + location /api-docs { + proxy_pass http://localhost:3200/api-docs; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + # 静态资源缓存控制(如果后端有静态资源) + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { + proxy_pass http://localhost:3200; + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # 错误页面配置 + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} + +# 可选:负载均衡配置(如果有多个后端实例) +# upstream jiebanke_backend { +# server localhost:3200; +# # 可以添加更多后端实例 +# # server localhost:3201; +# # server localhost:3202; +# +# # 负载均衡策略 +# # least_conn; +# # ip_hash; +# } \ No newline at end of file diff --git a/backend/restart.sh b/backend/restart.sh deleted file mode 100755 index 8900545..0000000 --- a/backend/restart.sh +++ /dev/null @@ -1,107 +0,0 @@ -#!/bin/bash - -# 结伴客后端服务重启脚本 - -# 设置颜色输出 -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# 停止服务 -stop_server() { - echo -e "${BLUE}正在停止结伴客后端服务...${NC}" - - # 查找并停止结伴客后端服务进程 - PIDS=$(ps aux | grep "node src/server.js" | grep -v grep | awk '{print $2}') - - if [ -z "$PIDS" ]; then - echo -e "${YELLOW}未找到正在运行的结伴客后端服务进程${NC}" - return 0 - fi - - echo -e "${BLUE}找到以下结伴客后端服务进程: $PIDS${NC}" - - for PID in $PIDS; do - echo -e "${BLUE}正在停止进程 $PID...${NC}" - kill $PID - - # 等待进程结束 - COUNT=0 - while kill -0 $PID 2>/dev/null; do - sleep 1 - COUNT=$((COUNT + 1)) - if [ $COUNT -gt 10 ]; then - echo -e "${YELLOW}进程 $PID 未能正常停止,正在强制终止...${NC}" - kill -9 $PID - break - fi - done - - echo -e "${GREEN}进程 $PID 已停止${NC}" - done - - echo -e "${GREEN}结伴客后端服务已停止${NC}" -} - -# 启动服务 -start_server() { - echo -e "${BLUE}正在启动结伴客后端服务...${NC}" - - # 检查是否提供了参数 - if [ "$1" = "dev" ]; then - # 开发模式 - if command -v nodemon &> /dev/null; then - nodemon src/server.js - else - echo -e "${YELLOW}未安装 nodemon,使用 node 运行...${NC}" - node src/server.js - fi - else - # 生产模式 - node src/server.js - fi -} - -# 重启服务 -restart_server() { - stop_server - sleep 2 - start_server "$1" -} - -# 显示帮助信息 -show_help() { - echo "结伴客后端服务重启脚本" - echo "" - echo "使用方法:" - echo " ./restart.sh - 重启服务(生产模式)" - echo " ./restart.sh dev - 重启服务(开发模式)" - echo " ./restart.sh help - 显示帮助信息" - echo "" - echo "说明:" - echo " 生产模式: 使用 node 直接运行服务" - echo " 开发模式: 使用 nodemon 运行服务(支持热重载)" -} - -# 主逻辑 -main() { - echo -e "${GREEN}========== 结伴客后端服务重启脚本 ==========${NC}" - - # 检查参数 - case "$1" in - "help"|"-h"|"--help") - show_help - ;; - "dev") - restart_server "dev" - ;; - *) - restart_server - ;; - esac -} - -# 执行主逻辑 -main "$@" \ No newline at end of file diff --git a/backend/src/config/swagger.js b/backend/src/config/swagger.js index 0c7ac81..02caa0d 100644 --- a/backend/src/config/swagger.js +++ b/backend/src/config/swagger.js @@ -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 \ No newline at end of file +module.exports = swaggerSpec \ No newline at end of file diff --git a/backend/src/middleware/auth.js b/backend/src/middleware/auth.js index 3473ec4..b140778 100644 --- a/backend/src/middleware/auth.js +++ b/backend/src/middleware/auth.js @@ -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); + } } // 管理员认证中间件 diff --git a/backend/src/models/UserMySQL.js b/backend/src/models/UserMySQL.js index 506935b..aef4ad3 100644 --- a/backend/src/models/UserMySQL.js +++ b/backend/src/models/UserMySQL.js @@ -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; } } diff --git a/backend/src/server.js b/backend/src/server.js index 8627143..45e217a 100644 --- a/backend/src/server.js +++ b/backend/src/server.js @@ -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' // 显示启动横幅 diff --git a/backend/src/utils/errors.js b/backend/src/utils/errors.js index af30531..f980fe5 100644 --- a/backend/src/utils/errors.js +++ b/backend/src/utils/errors.js @@ -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 } \ No newline at end of file diff --git a/backend/start.sh b/backend/start.sh deleted file mode 100755 index 609bca5..0000000 --- a/backend/start.sh +++ /dev/null @@ -1,93 +0,0 @@ -#!/bin/bash - -# 结伴客后端服务启动脚本 - -# 设置颜色输出 -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# 检查是否已经安装依赖 -check_dependencies() { - if [ ! -d "node_modules" ]; then - echo -e "${YELLOW}检测到未安装依赖,正在安装...${NC}" - npm install - if [ $? -ne 0 ]; then - echo -e "${RED}依赖安装失败!${NC}" - exit 1 - fi - echo -e "${GREEN}依赖安装完成!${NC}" - fi -} - -# 检查环境变量文件 -check_env() { - if [ ! -f ".env" ]; then - echo -e "${YELLOW}未找到 .env 文件,正在复制示例文件...${NC}" - if [ -f ".env.example" ]; then - cp .env.example .env - echo -e "${GREEN}.env 文件已创建,请根据需要修改配置!${NC}" - else - echo -e "${RED}未找到 .env.example 文件!${NC}" - fi - fi -} - -# 启动服务 -start_server() { - echo -e "${GREEN}正在启动结伴客后端服务...${NC}" - - # 检查是否提供了参数 - if [ "$1" = "dev" ]; then - # 开发模式 - if command -v nodemon &> /dev/null; then - nodemon src/server.js - else - echo -e "${YELLOW}未安装 nodemon,使用 node 运行...${NC}" - node src/server.js - fi - else - # 生产模式 - node src/server.js - fi -} - -# 显示帮助信息 -show_help() { - echo "结伴客后端服务启动脚本" - echo "" - echo "使用方法:" - echo " ./start.sh - 以生产模式启动服务" - echo " ./start.sh dev - 以开发模式启动服务" - echo " ./start.sh help - 显示帮助信息" - echo "" - echo "说明:" - echo " 生产模式: 使用 node 直接运行服务" - echo " 开发模式: 使用 nodemon 运行服务(支持热重载)" -} - -# 主逻辑 -main() { - echo -e "${GREEN}========== 结伴客后端服务启动脚本 ==========${NC}" - - # 检查参数 - case "$1" in - "help"|"-h"|"--help") - show_help - ;; - "dev") - check_dependencies - check_env - start_server "dev" - ;; - *) - check_dependencies - check_env - start_server - ;; - esac -} - -# 执行主逻辑 -main "$@" \ No newline at end of file diff --git a/backend/start_server.sh b/backend/start_server.sh new file mode 100755 index 0000000..2e22b68 --- /dev/null +++ b/backend/start_server.sh @@ -0,0 +1,278 @@ +#!/bin/bash + +# 结伴客后端服务启动脚本 - 在CentOS服务器上运行 +# 使用PM2管理Node.js应用 + +# 设置颜色输出 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# 配置参数 +APP_DIR="$(pwd)" +NODE_ENV=${NODE_ENV:-"production"} +PORT=${PORT:-"3200"} + +# 检查Node.js环境 +check_node() { + if ! command -v node &> /dev/null; then + echo -e "${RED}错误:Node.js环境未安装!${NC}" + echo -e "${YELLOW}请先安装Node.js: curl -sL https://rpm.nodesource.com/setup_16.x | sudo bash - && sudo yum install -y nodejs${NC}" + exit 1 + fi + echo -e "${GREEN}Node.js版本: $(node -v)${NC}" +} + +# 检查PM2是否安装 +check_pm2() { + if ! command -v pm2 &> /dev/null; then + echo -e "${YELLOW}PM2未安装,正在全局安装...${NC}" + npm install -g pm2 + if [ $? -ne 0 ]; then + echo -e "${RED}PM2安装失败!${NC}" + exit 1 + fi + fi + echo -e "${GREEN}PM2版本: $(pm2 -v)${NC}" +} + +# 检查环境变量文件 +check_env() { + if [ ! -f "$APP_DIR/.env" ]; then + echo -e "${RED}错误:未找到.env文件!${NC}" + echo -e "${YELLOW}请确保.env文件已正确配置,包含必要的数据库连接信息和其他配置。${NC}" + exit 1 + fi + echo -e "${GREEN}已找到.env文件${NC}" +} + +# 检查ecosystem.config.js文件 +check_ecosystem() { + if [ ! -f "$APP_DIR/ecosystem.config.js" ]; then + echo -e "${YELLOW}未找到ecosystem.config.js文件,正在创建默认配置...${NC}" + cat > "$APP_DIR/ecosystem.config.js" << EOF +module.exports = { + apps: [{ + name: 'jiebanke-backend', + script: './src/server.js', + instances: 'max', + exec_mode: 'cluster', + env: { + NODE_ENV: 'development', + PORT: $PORT, + WATCH: true + }, + env_production: { + NODE_ENV: 'production', + PORT: $PORT, + WATCH: false + }, + log_file: '$APP_DIR/logs/combined.log', + out_file: '$APP_DIR/logs/out.log', + error_file: '$APP_DIR/logs/error.log', + max_memory_restart: '1G', + kill_timeout: 3000, + wait_ready: true, + listen_timeout: 3000, + autorestart: true, + max_restarts: 10, + restart_delay: 4000, + ignore_watch: [ + 'node_modules', + 'logs', + '.git', + 'uploads' + ] + }] +}; +EOF + echo -e "${GREEN}ecosystem.config.js文件已创建${NC}" + fi +} + +# 安装依赖 +install_dependencies() { + echo -e "${BLUE}正在安装生产依赖...${NC}" + cd "$APP_DIR" + npm install --production + if [ $? -ne 0 ]; then + echo -e "${RED}依赖安装失败!${NC}" + exit 1 + fi + echo -e "${GREEN}依赖安装完成!${NC}" +} + +# 创建日志和上传目录 +create_directories() { + echo -e "${BLUE}正在创建必要的目录...${NC}" + mkdir -p "$APP_DIR/logs" "$APP_DIR/uploads" + chmod 755 "$APP_DIR/logs" "$APP_DIR/uploads" + echo -e "${GREEN}目录创建完成!${NC}" +} + +# 启动服务 +start_service() { + echo -e "${BLUE}正在使用PM2启动结伴客后端服务...${NC}" + cd "$APP_DIR" + + # 设置NODE_ENV环境变量 + export NODE_ENV=$NODE_ENV + + # 使用PM2启动应用 + pm2 start ecosystem.config.js --env $NODE_ENV + + if [ $? -ne 0 ]; then + echo -e "${RED}服务启动失败!${NC}" + exit 1 + fi + + echo -e "${GREEN}服务启动成功!${NC}" + echo -e "${BLUE}应用名称: jiebanke-backend${NC}" + echo -e "${BLUE}环境: $NODE_ENV${NC}" + echo -e "${BLUE}端口: $PORT${NC}" + echo -e "${YELLOW}提示:使用 pm2 logs jiebanke-backend 查看日志${NC}" + echo -e "${YELLOW}提示:使用 pm2 monit 监控应用状态${NC}" +} + +# 设置PM2开机自启 +setup_autostart() { + echo -e "${BLUE}正在配置PM2开机自启...${NC}" + pm2 startup + if [ $? -ne 0 ]; then + echo -e "${RED}PM2开机自启配置失败!${NC}" + else + pm2 save + echo -e "${GREEN}PM2开机自启配置完成!${NC}" + fi +} + +# 显示帮助信息 +show_help() { + echo "" + echo "结伴客后端服务管理脚本" + echo "" + echo "用法: $0 [命令] [环境]" + echo "" + echo "命令选项:" + echo " start - 启动服务(默认)" + echo " stop - 停止服务" + echo " restart - 重启服务" + echo " status - 查看服务状态" + echo " logs - 查看服务日志" + echo " install - 仅安装依赖" + echo " setup - 配置PM2开机自启" + echo " help - 显示此帮助信息" + echo "" + echo "环境选项:" + echo " production - 生产环境(默认)" + echo " development - 开发环境" + echo " test - 测试环境" + echo "" + echo "示例:" + echo " $0 start production # 在生产环境启动服务" + echo " $0 restart development # 在开发环境重启服务" + echo " $0 logs # 查看服务日志" + echo "" +} + +# 停止服务 +stop_service() { + echo -e "${BLUE}正在停止结伴客后端服务...${NC}" + pm2 stop jiebanke-backend + if [ $? -ne 0 ]; then + echo -e "${YELLOW}服务可能未在运行!${NC}" + else + echo -e "${GREEN}服务已停止!${NC}" + fi +} + +# 重启服务 +restart_service() { + echo -e "${BLUE}正在重启结伴客后端服务...${NC}" + export NODE_ENV=$NODE_ENV + pm2 restart ecosystem.config.js --env $NODE_ENV + if [ $? -ne 0 ]; then + echo -e "${RED}服务重启失败!${NC}" + exit 1 + fi + echo -e "${GREEN}服务重启成功!${NC}" +} + +# 查看服务状态 +status_service() { + echo -e "${BLUE}正在查看结伴客后端服务状态...${NC}" + pm2 status jiebanke-backend +} + +# 查看服务日志 +logs_service() { + echo -e "${BLUE}正在查看结伴客后端服务日志...${NC}" + pm2 logs jiebanke-backend +} + +# 主函数 +handle_command() { + # 解析命令和环境参数 + COMMAND="start" + if [ $# -ge 1 ]; then + case $1 in + start|stop|restart|status|logs|install|setup|help) COMMAND=$1 ;; + *) echo -e "${RED}未知命令:$1${NC}"; show_help; exit 1 ;; + esac + fi + + if [ $# -ge 2 ]; then + case $2 in + production|development|test) NODE_ENV=$2 ;; + *) echo -e "${RED}未知环境:$2${NC}"; show_help; exit 1 ;; + esac + fi + + # 执行相应的命令 + case $COMMAND in + start) + check_node + check_pm2 + check_env + check_ecosystem + install_dependencies + create_directories + start_service + ;; + stop) + check_pm2 + stop_service + ;; + restart) + check_node + check_pm2 + check_env + install_dependencies + restart_service + ;; + status) + check_pm2 + status_service + ;; + logs) + check_pm2 + logs_service + ;; + install) + check_node + install_dependencies + ;; + setup) + check_pm2 + setup_autostart + ;; + help) + show_help + ;; + esac +} + +# 执行主函数 +handle_command "$@" \ No newline at end of file diff --git a/backend/status.sh b/backend/status.sh deleted file mode 100755 index 32f6cc0..0000000 --- a/backend/status.sh +++ /dev/null @@ -1,90 +0,0 @@ -#!/bin/bash - -# 结伴客后端服务状态检查脚本 - -# 设置颜色输出 -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# 检查服务状态 -check_status() { - echo -e "${BLUE}正在检查结伴客后端服务状态...${NC}" - - # 查找结伴客后端服务进程 - PROCESSES=$(ps aux | grep "node src/server.js" | grep -v grep) - PIDS=$(echo "$PROCESSES" | awk '{print $2}') - - if [ -z "$PIDS" ]; then - echo -e "${RED}状态: 未运行${NC}" - return 1 - else - echo -e "${GREEN}状态: 运行中${NC}" - echo -e "${BLUE}进程信息:${NC}" - echo "$PROCESSES" - return 0 - fi -} - -# 显示详细信息 -show_details() { - echo -e "${BLUE}========== 结伴客后端服务详细信息 ==========${NC}" - - # 显示进程信息 - echo -e "${BLUE}进程信息:${NC}" - ps aux | grep "node src/server.js" | grep -v grep || echo -e "${YELLOW}未找到相关进程${NC}" - - # 显示端口占用情况 - echo -e "${BLUE}端口占用情况:${NC}" - netstat -tlnp | grep :3000 || echo -e "${YELLOW}未检测到3000端口占用${NC}" - - # 显示工作目录 - echo -e "${BLUE}当前工作目录:${NC}" - echo "$(pwd)" - - # 显示Node.js版本 - echo -e "${BLUE}Node.js版本:${NC}" - node --version || echo -e "${YELLOW}未安装Node.js${NC}" - - # 显示npm版本 - echo -e "${BLUE}npm版本:${NC}" - npm --version || echo -e "${YELLOW}未安装npm${NC}" -} - -# 显示帮助信息 -show_help() { - echo "结伴客后端服务状态检查脚本" - echo "" - echo "使用方法:" - echo " ./status.sh - 检查服务状态" - echo " ./status.sh detail - 显示详细信息" - echo " ./status.sh help - 显示帮助信息" -} - -# 主逻辑 -main() { - echo -e "${GREEN}========== 结伴客后端服务状态检查 ==========${NC}" - - # 检查参数 - case "$1" in - "help"|"-h"|"--help") - show_help - ;; - "detail") - show_details - ;; - *) - if check_status; then - echo -e "${GREEN}结伴客后端服务正在正常运行${NC}" - else - echo -e "${RED}结伴客后端服务未运行${NC}" - exit 1 - fi - ;; - esac -} - -# 执行主逻辑 -main "$@" \ No newline at end of file diff --git a/backend/stop.sh b/backend/stop.sh deleted file mode 100755 index a95a48a..0000000 --- a/backend/stop.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash - -# 结伴客后端服务停止脚本 - -# 设置颜色输出 -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# 停止服务 -stop_server() { - echo -e "${GREEN}正在停止结伴客后端服务...${NC}" - - # 查找并停止结伴客后端服务进程 - PIDS=$(ps aux | grep "node src/server.js" | grep -v grep | awk '{print $2}') - - if [ -z "$PIDS" ]; then - echo -e "${YELLOW}未找到正在运行的结伴客后端服务进程${NC}" - return 0 - fi - - echo -e "${GREEN}找到以下结伴客后端服务进程: $PIDS${NC}" - - for PID in $PIDS; do - echo -e "${GREEN}正在停止进程 $PID...${NC}" - kill $PID - - # 等待进程结束 - COUNT=0 - while kill -0 $PID 2>/dev/null; do - sleep 1 - COUNT=$((COUNT + 1)) - if [ $COUNT -gt 10 ]; then - echo -e "${YELLOW}进程 $PID 未能正常停止,正在强制终止...${NC}" - kill -9 $PID - break - fi - done - - echo -e "${GREEN}进程 $PID 已停止${NC}" - done - - echo -e "${GREEN}结伴客后端服务已停止${NC}" -} - -# 显示帮助信息 -show_help() { - echo "结伴客后端服务停止脚本" - echo "" - echo "使用方法:" - echo " ./stop.sh - 停止所有结伴客后端服务进程" - echo " ./stop.sh help - 显示帮助信息" -} - -# 主逻辑 -main() { - echo -e "${GREEN}========== 结伴客后端服务停止脚本 ==========${NC}" - - # 检查参数 - case "$1" in - "help"|"-h"|"--help") - show_help - ;; - *) - stop_server - ;; - esac -} - -# 执行主逻辑 -main "$@" \ No newline at end of file diff --git a/backend/sync_to_server.sh b/backend/sync_to_server.sh new file mode 100755 index 0000000..7626318 --- /dev/null +++ b/backend/sync_to_server.sh @@ -0,0 +1,109 @@ +#!/bin/bash + +# 结伴客后端文件同步脚本 - 同步到CentOS服务器 +# 目标服务器: 使用命令行参数或环境变量指定 +# 目标目录: /data/nodejs/jiebanke + +# 设置颜色输出 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# 配置参数 - 可通过环境变量或命令行参数覆盖 +LOCAL_DIR="$(pwd)" +REMOTE_USER=${REMOTE_USER:-"root"} +REMOTE_HOST=${REMOTE_HOST:-"www.jiebanke.com"} +REMOTE_DIR=${REMOTE_DIR:-"/data/nodejs/jiebanke"} + +# 检查命令行参数 +if [ $# -ge 1 ]; then + REMOTE_HOST=$1 +fi + +# 检查必需参数 +if [ -z "$REMOTE_HOST" ]; then + echo -e "${RED}错误:未指定远程服务器地址!${NC}" + echo -e "${YELLOW}用法:$0 [remote_user]${NC}" + exit 1 +fi + +if [ $# -ge 2 ]; then + REMOTE_USER=$2 +fi + +# 检查本地目录是否存在 +if [ ! -d "$LOCAL_DIR" ]; then + echo -e "${RED}错误:本地目录 $LOCAL_DIR 不存在!${NC}" + exit 1 +fi + +# 检查rsync是否安装 +if ! command -v rsync &> /dev/null; then + echo -e "${RED}错误:rsync未安装!请先安装rsync。${NC}" + exit 1 +fi + +# 检查Node.js环境 +if ! command -v node &> /dev/null; then + echo -e "${YELLOW}警告:本地Node.js环境未安装,但仍会继续同步文件...${NC}" +else + # 安装生产依赖 + echo -e "${BLUE}安装生产依赖...${NC}" + npm install --production + if [ $? -ne 0 ]; then + echo -e "${RED}依赖安装失败!${NC}" + exit 1 + fi +fi + +# 确保.env文件存在 +if [ ! -f ".env" ]; then + if [ -f ".env.example" ]; then + echo -e "${YELLOW}未找到.env文件,正在从.env.example创建...${NC}" + cp .env.example .env + else + echo -e "${RED}错误:未找到.env和.env.example文件!${NC}" + exit 1 + fi +fi + +# 使用rsync上传文件到服务器 +echo -e "${BLUE}开始同步文件到远程服务器 ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}...${NC}" + +# 创建排除列表 +EXCLUDE_LIST=( + --exclude 'node_modules' + --exclude '.git' + --exclude '.gitignore' + --exclude '.env.example' # 不同步示例环境文件,保留服务器上的实际配置 + --exclude 'logs' + --exclude 'uploads' + --exclude 'docker-compose.yml' + --exclude 'test-*.js' + --exclude 'README*.md' +) + +# 执行同步 +rsync -avz --progress --delete \ + "${EXCLUDE_LIST[@]}" \ + "$LOCAL_DIR/" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR/" + +if [ $? -ne 0 ]; then + echo -e "${RED}文件同步失败!${NC}" + exit 1 +fi + +# 在服务器上创建必要的目录 +echo -e "${BLUE}在远程服务器上创建必要的目录...${NC}" +ssh "$REMOTE_USER@$REMOTE_HOST" "mkdir -p $REMOTE_DIR/logs $REMOTE_DIR/uploads" + +# 完成提示 +echo -e "${GREEN}文件同步完成!${NC}" + +# 提示如何在服务器上启动服务 +echo -e "${YELLOW}请在服务器上执行以下命令以启动服务:${NC}" +echo -e " ssh $REMOTE_USER@$REMOTE_HOST" +echo -e " cd $REMOTE_DIR" +echo -e " ./start_server.sh" \ No newline at end of file diff --git a/backend/test-api.js b/backend/test-api.js deleted file mode 100644 index 8559545..0000000 --- a/backend/test-api.js +++ /dev/null @@ -1,103 +0,0 @@ -const http = require('http'); - -// 测试健康检查接口 -function testHealthCheck() { - return new Promise((resolve, reject) => { - const options = { - hostname: 'webapi.jiebanke.com', - port: 3000, - path: '/health', - method: 'GET' - }; - - const req = http.request(options, (res) => { - let data = ''; - - res.on('data', (chunk) => { - data += chunk; - }); - - res.on('end', () => { - console.log('✅ 健康检查接口测试成功'); - console.log('状态码:', res.statusCode); - console.log('响应:', JSON.parse(data)); - resolve(); - }); - }); - - req.on('error', (error) => { - console.error('❌ 健康检查接口测试失败:', error.message); - reject(error); - }); - - req.end(); - }); -} - -// 测试认证接口 -function testAuthAPI() { - return new Promise((resolve, reject) => { - const postData = JSON.stringify({ - username: 'testuser', - password: 'testpass123' - }); - - const options = { - hostname: 'webapi.jiebanke.com', - port: 3000, - path: '/api/v1/auth/login', - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Content-Length': Buffer.byteLength(postData) - } - }; - - const req = http.request(options, (res) => { - let data = ''; - - res.on('data', (chunk) => { - data += chunk; - }); - - res.on('end', () => { - console.log('\n✅ 认证接口测试成功'); - console.log('状态码:', res.statusCode); - try { - const response = JSON.parse(data); - console.log('响应:', response); - } catch (e) { - console.log('原始响应:', data); - } - resolve(); - }); - }); - - req.on('error', (error) => { - console.error('❌ 认证接口测试失败:', error.message); - reject(error); - }); - - req.write(postData); - req.end(); - }); -} - -async function runTests() { - console.log('🚀 开始测试API接口...\n'); - - try { - await testHealthCheck(); - await testAuthAPI(); - console.log('\n🎉 所有测试完成!'); - } catch (error) { - console.error('\n❌ 测试失败:', error.message); - } -} - -// 如果直接运行此文件,则执行测试 -if (require.main === module) { - runTests(); -} - -module.exports = { runTests }; \ No newline at end of file diff --git a/backend/test-swagger.js b/backend/test-swagger.js deleted file mode 100644 index 41aaac9..0000000 --- a/backend/test-swagger.js +++ /dev/null @@ -1,30 +0,0 @@ -const http = require('http'); - -// 发送请求到Swagger UI -const options = { - hostname: 'admin.jiebanke.com', - port: 3001, - path: '/api-docs/', - method: 'GET' -}; - -const req = http.request(options, (res) => { - console.log(`状态码: ${res.statusCode}`); - - res.on('data', (chunk) => { - // 检查响应中是否包含Swagger UI的关键字 - if (chunk.toString().includes('Swagger UI')) { - console.log('Swagger UI 已成功启动并运行'); - } else { - console.log('收到响应,但可能不是Swagger UI页面'); - } - // 只输出前200个字符来检查内容 - console.log('响应前200字符:', chunk.toString().substring(0, 200)); - }); -}); - -req.on('error', (error) => { - console.error('请求出错:', error.message); -}); - -req.end(); \ No newline at end of file