后端版本服务器部署成功

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

@@ -1,6 +1,6 @@
# 服务器配置
NODE_ENV=development
PORT=3000
PORT=3200
HOST=0.0.0.0
# 数据库配置

View File

@@ -1,6 +1,6 @@
# 服务器配置
NODE_ENV=development
PORT=3000
PORT=3200
HOST=0.0.0.0
# 数据库配置

View File

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

View File

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

View File

@@ -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. 认证机制
系统使用JWTJSON 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

134
backend/cleanup_files.sh Executable file
View File

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

View File

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

View File

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

View File

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

View File

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

110
backend/jiebanke_nginx.conf Normal file
View File

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

View File

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

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
}

View File

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

278
backend/start_server.sh Executable file
View File

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

View File

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

View File

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

109
backend/sync_to_server.sh Executable file
View File

@@ -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_host> [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"

View File

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

View File

@@ -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();