添加服务端
This commit is contained in:
parent
55bee29a4d
commit
43bbd626c5
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -6,3 +6,4 @@ node-modules
|
||||||
/node_modules/
|
/node_modules/
|
||||||
**/dist/
|
**/dist/
|
||||||
**/node-modules/
|
**/node-modules/
|
||||||
|
server/admin-dist/*
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@ JWT_EXPIRES_IN=7d
|
||||||
JWT_REFRESH_EXPIRES_IN=30d
|
JWT_REFRESH_EXPIRES_IN=30d
|
||||||
|
|
||||||
# WeChat Configuration
|
# WeChat Configuration
|
||||||
WECHAT_APP_ID=your-wechat-app-id
|
WECHAT_APP_ID=wx5ab1c98df1ec13f3
|
||||||
WECHAT_APP_SECRET=your-wechat-app-secret
|
WECHAT_APP_SECRET=091adc2c6dc3c7fddf198d782b6ac240
|
||||||
|
|
||||||
# File Upload Configuration
|
# File Upload Configuration
|
||||||
UPLOAD_PATH=./uploads
|
UPLOAD_PATH=./uploads
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@ JWT_EXPIRES_IN=7d
|
||||||
JWT_REFRESH_EXPIRES_IN=30d
|
JWT_REFRESH_EXPIRES_IN=30d
|
||||||
|
|
||||||
# WeChat Configuration
|
# WeChat Configuration
|
||||||
WECHAT_APP_ID=your-wechat-app-id
|
WECHAT_APP_ID=wx5ab1c98df1ec13f3
|
||||||
WECHAT_APP_SECRET=your-wechat-app-secret
|
WECHAT_APP_SECRET=091adc2c6dc3c7fddf198d782b6ac240
|
||||||
|
|
||||||
# File Upload Configuration
|
# File Upload Configuration
|
||||||
UPLOAD_PATH=./uploads
|
UPLOAD_PATH=./uploads
|
||||||
|
|
|
||||||
53
server/.env
Normal file
53
server/.env
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
# ===========================================
|
||||||
|
# 生产环境配置模板
|
||||||
|
# 复制此文件为 .env 并填写实际配置
|
||||||
|
# ===========================================
|
||||||
|
|
||||||
|
# 应用端口(唯一对外暴露的端口)
|
||||||
|
APP_PORT=2701
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# 数据库配置 (外部 MySQL)
|
||||||
|
# ===========================================
|
||||||
|
DB_HOST=192.168.195.15
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_NAME=overseas_appointment
|
||||||
|
DB_USER=root
|
||||||
|
DB_PASSWORD=root123456
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Redis 配置 (外部 Redis)
|
||||||
|
# ===========================================
|
||||||
|
REDIS_HOST=192.168.195.15
|
||||||
|
REDIS_PORT=6379
|
||||||
|
REDIS_PASSWORD=
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# JWT 认证配置
|
||||||
|
# ===========================================
|
||||||
|
JWT_SECRET=your-super-secret-key-change-in-production
|
||||||
|
JWT_EXPIRES_IN=7d
|
||||||
|
JWT_REFRESH_EXPIRES_IN=30d
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# 微信小程序配置
|
||||||
|
# ===========================================
|
||||||
|
WECHAT_APP_ID=wx5ab1c98df1ec13f3
|
||||||
|
WECHAT_APP_SECRET=091adc2c6dc3c7fddf198d782b6ac240
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# 文件上传配置
|
||||||
|
# ===========================================
|
||||||
|
UPLOAD_PATH=./uploads
|
||||||
|
MAX_FILE_SIZE=5242880
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# 限流配置
|
||||||
|
# ===========================================
|
||||||
|
RATE_LIMIT_WINDOW=60000
|
||||||
|
RATE_LIMIT_MAX=100
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# 日志配置
|
||||||
|
# ===========================================
|
||||||
|
LOG_LEVEL=info
|
||||||
52
server/.env.example
Normal file
52
server/.env.example
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
# ===========================================
|
||||||
|
# 生产环境配置模板
|
||||||
|
# 复制此文件为 .env 并填写实际配置
|
||||||
|
# ===========================================
|
||||||
|
|
||||||
|
# 应用端口(唯一对外暴露的端口)
|
||||||
|
APP_PORT=2701
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# 数据库配置 (外部 MySQL)
|
||||||
|
# ===========================================
|
||||||
|
DB_HOST=192.168.195.15
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_NAME=overseas_appointment
|
||||||
|
DB_USER=root
|
||||||
|
DB_PASSWORD=your_password_here
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# Redis 配置 (外部 Redis)
|
||||||
|
# ===========================================
|
||||||
|
REDIS_HOST=192.168.195.15
|
||||||
|
REDIS_PORT=6379
|
||||||
|
REDIS_PASSWORD=
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# JWT 认证配置
|
||||||
|
# ===========================================
|
||||||
|
JWT_SECRET=your-super-secret-key-change-in-production
|
||||||
|
JWT_EXPIRES_IN=7d
|
||||||
|
JWT_REFRESH_EXPIRES_IN=30d
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# 微信小程序配置
|
||||||
|
# ===========================================
|
||||||
|
WECHAT_APP_ID=your-wechat-app-id
|
||||||
|
WECHAT_APP_SECRET=your-wechat-app-secret
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# 文件上传配置
|
||||||
|
# ===========================================
|
||||||
|
MAX_FILE_SIZE=5242880
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# 限流配置
|
||||||
|
# ===========================================
|
||||||
|
RATE_LIMIT_WINDOW=60000
|
||||||
|
RATE_LIMIT_MAX=100
|
||||||
|
|
||||||
|
# ===========================================
|
||||||
|
# 日志配置
|
||||||
|
# ===========================================
|
||||||
|
LOG_LEVEL=info
|
||||||
112
server/Makefile
Normal file
112
server/Makefile
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
# ============================================
|
||||||
|
# 海外预约系统 - Makefile
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
.PHONY: help deploy build start stop restart logs status clean
|
||||||
|
|
||||||
|
# 默认目标
|
||||||
|
.DEFAULT_GOAL := help
|
||||||
|
|
||||||
|
# 颜色定义
|
||||||
|
GREEN := \033[0;32m
|
||||||
|
YELLOW := \033[1;33m
|
||||||
|
BLUE := \033[0;34m
|
||||||
|
NC := \033[0m
|
||||||
|
|
||||||
|
# 检测 docker compose 命令
|
||||||
|
DOCKER_COMPOSE := $(shell if docker compose version > /dev/null 2>&1; then echo "docker compose"; else echo "docker-compose"; fi)
|
||||||
|
|
||||||
|
# 帮助信息
|
||||||
|
help:
|
||||||
|
@echo ""
|
||||||
|
@echo "$(BLUE)============================================$(NC)"
|
||||||
|
@echo "$(BLUE) 海外预约系统 - 部署命令$(NC)"
|
||||||
|
@echo "$(BLUE)============================================$(NC)"
|
||||||
|
@echo ""
|
||||||
|
@echo "$(GREEN)部署命令:$(NC)"
|
||||||
|
@echo " make deploy - 完整部署(构建 Admin + 启动服务)"
|
||||||
|
@echo " make build - 仅构建 Admin 前端"
|
||||||
|
@echo " make start - 启动服务(跳过构建)"
|
||||||
|
@echo ""
|
||||||
|
@echo "$(GREEN)管理命令:$(NC)"
|
||||||
|
@echo " make stop - 停止服务"
|
||||||
|
@echo " make restart - 重启服务"
|
||||||
|
@echo " make status - 查看服务状态"
|
||||||
|
@echo " make logs - 查看实时日志"
|
||||||
|
@echo " make logs-api - 查看 API 日志"
|
||||||
|
@echo " make logs-nginx - 查看 Nginx 日志"
|
||||||
|
@echo ""
|
||||||
|
@echo "$(GREEN)其他命令:$(NC)"
|
||||||
|
@echo " make clean - 清理构建产物"
|
||||||
|
@echo " make shell-api - 进入 API 容器"
|
||||||
|
@echo " make shell-nginx - 进入 Nginx 容器"
|
||||||
|
@echo " make init - 初始化环境配置"
|
||||||
|
@echo ""
|
||||||
|
|
||||||
|
# 完整部署
|
||||||
|
deploy:
|
||||||
|
@./deploy.sh
|
||||||
|
|
||||||
|
# 仅构建 Admin
|
||||||
|
build:
|
||||||
|
@echo "$(YELLOW)构建 Admin 前端...$(NC)"
|
||||||
|
@cd ../admin && npm install && npm run build
|
||||||
|
@rm -rf ./admin-dist
|
||||||
|
@cp -r ../admin/dist ./admin-dist
|
||||||
|
@echo "$(GREEN)✓ Admin 构建完成$(NC)"
|
||||||
|
|
||||||
|
# 启动服务(跳过构建)
|
||||||
|
start:
|
||||||
|
@./deploy.sh --skip-build
|
||||||
|
|
||||||
|
# 停止服务
|
||||||
|
stop:
|
||||||
|
@./deploy.sh --stop
|
||||||
|
|
||||||
|
# 重启服务
|
||||||
|
restart:
|
||||||
|
@./deploy.sh --restart
|
||||||
|
|
||||||
|
# 查看服务状态
|
||||||
|
status:
|
||||||
|
@$(DOCKER_COMPOSE) ps
|
||||||
|
|
||||||
|
# 查看所有日志
|
||||||
|
logs:
|
||||||
|
@$(DOCKER_COMPOSE) logs -f
|
||||||
|
|
||||||
|
# 查看 API 日志
|
||||||
|
logs-api:
|
||||||
|
@$(DOCKER_COMPOSE) logs -f api
|
||||||
|
|
||||||
|
# 查看 Nginx 日志
|
||||||
|
logs-nginx:
|
||||||
|
@$(DOCKER_COMPOSE) logs -f nginx
|
||||||
|
|
||||||
|
# 进入 API 容器
|
||||||
|
shell-api:
|
||||||
|
@docker exec -it overseas-appointment-api sh
|
||||||
|
|
||||||
|
# 进入 Nginx 容器
|
||||||
|
shell-nginx:
|
||||||
|
@docker exec -it overseas-appointment-nginx sh
|
||||||
|
|
||||||
|
# 初始化环境配置
|
||||||
|
init:
|
||||||
|
@if [ ! -f .env ]; then \
|
||||||
|
cp .env.example .env; \
|
||||||
|
echo "$(GREEN)✓ 已创建 .env 文件,请编辑配置$(NC)"; \
|
||||||
|
else \
|
||||||
|
echo "$(YELLOW).env 文件已存在$(NC)"; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 清理构建产物
|
||||||
|
clean:
|
||||||
|
@echo "$(YELLOW)清理构建产物...$(NC)"
|
||||||
|
@rm -rf ./admin-dist
|
||||||
|
@$(DOCKER_COMPOSE) down --rmi local 2>/dev/null || true
|
||||||
|
@echo "$(GREEN)✓ 清理完成$(NC)"
|
||||||
|
|
||||||
|
# 查看健康状态
|
||||||
|
health:
|
||||||
|
@curl -s http://localhost:2701/health | python3 -m json.tool 2>/dev/null || curl -s http://localhost:2701/health
|
||||||
193
server/README.md
Normal file
193
server/README.md
Normal file
|
|
@ -0,0 +1,193 @@
|
||||||
|
# 服务器部署配置
|
||||||
|
|
||||||
|
此目录包含生产环境的 Docker 部署配置,使用**外部 MySQL 和 Redis** 服务。
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
server/
|
||||||
|
├── docker-compose.yml # Docker Compose 配置
|
||||||
|
├── deploy.sh # 自动部署脚本
|
||||||
|
├── .env.example # 环境变量模板
|
||||||
|
├── .env # 实际环境变量(需手动创建)
|
||||||
|
├── nginx/ # Nginx 配置
|
||||||
|
│ ├── nginx.conf
|
||||||
|
│ └── conf.d/
|
||||||
|
│ └── default.conf
|
||||||
|
├── data/ # 数据目录(本地挂载,方便迁移)
|
||||||
|
│ ├── uploads/ # 用户上传的文件
|
||||||
|
│ └── logs/
|
||||||
|
│ ├── api/ # API 服务日志
|
||||||
|
│ └── nginx/ # Nginx 访问日志
|
||||||
|
├── admin-dist/ # Admin 打包后的静态文件(需手动放置)
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## 架构说明
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ 外部代理 (SSL) │
|
||||||
|
└──────────────┬──────────────────────┘
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
┌──────────────────────────────────────────────────────────────────┐
|
||||||
|
│ Docker Compose (单一端口 2701) │
|
||||||
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ Nginx │ │
|
||||||
|
│ │ /admin/* → 静态文件 (Admin 后台) │ │
|
||||||
|
│ │ /api/* → API 服务 │ │
|
||||||
|
│ │ /uploads → API 服务 │ │
|
||||||
|
│ └────────────────────────┬───────────────────────────────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ▼ │
|
||||||
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ API 服务 (:3000) │ │
|
||||||
|
│ └────────────────────────────────────────────────────────────┘ │
|
||||||
|
└──────────────────────────────────────────────────────────────────┘
|
||||||
|
│ │
|
||||||
|
▼ ▼
|
||||||
|
┌─────────────┐ ┌─────────────┐
|
||||||
|
│ MySQL │ │ Redis │
|
||||||
|
│ (外部服务) │ │ (外部服务) │
|
||||||
|
└─────────────┘ └─────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
### 1. 配置环境变量
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd server
|
||||||
|
cp .env.example .env
|
||||||
|
vim .env # 编辑配置
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 构建 Admin 前端
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ../admin
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# 将打包产物复制到 server 目录
|
||||||
|
cp -r dist ../server/admin-dist
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 启动服务
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd server
|
||||||
|
docker-compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 验证服务
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 检查服务状态
|
||||||
|
docker-compose ps
|
||||||
|
|
||||||
|
# 访问 Admin 后台
|
||||||
|
curl http://localhost:2701/admin
|
||||||
|
|
||||||
|
# 检查 API
|
||||||
|
curl http://localhost:2701/health
|
||||||
|
curl http://localhost:2701/api/v1/services
|
||||||
|
```
|
||||||
|
|
||||||
|
## 访问地址
|
||||||
|
|
||||||
|
| 路径 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `/admin` | Admin 管理后台 |
|
||||||
|
| `/api/*` | 后端 API 接口 |
|
||||||
|
| `/api-docs` | API 文档 |
|
||||||
|
| `/health` | 健康检查 |
|
||||||
|
| `/uploads/*` | 上传文件 |
|
||||||
|
|
||||||
|
## 常用命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 启动
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
# 停止
|
||||||
|
docker-compose down
|
||||||
|
|
||||||
|
# 重新构建并启动
|
||||||
|
docker-compose up -d --build
|
||||||
|
|
||||||
|
# 查看日志
|
||||||
|
docker-compose logs -f
|
||||||
|
docker-compose logs -f api
|
||||||
|
docker-compose logs -f nginx
|
||||||
|
|
||||||
|
# 重启服务
|
||||||
|
docker-compose restart api
|
||||||
|
|
||||||
|
# 进入容器
|
||||||
|
docker exec -it overseas-appointment-api sh
|
||||||
|
docker exec -it overseas-appointment-nginx sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## 更新 Admin 前端
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 重新构建 admin
|
||||||
|
cd ../admin
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# 2. 更新静态文件
|
||||||
|
rm -rf ../server/admin-dist
|
||||||
|
cp -r dist ../server/admin-dist
|
||||||
|
|
||||||
|
# 3. 重启 nginx
|
||||||
|
cd ../server
|
||||||
|
docker-compose restart nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
## 外部代理配置示例
|
||||||
|
|
||||||
|
### Nginx (外部)
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
server_name your-domain.com;
|
||||||
|
|
||||||
|
ssl_certificate /path/to/fullchain.pem;
|
||||||
|
ssl_certificate_key /path/to/privkey.pem;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:2701;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 故障排查
|
||||||
|
|
||||||
|
### Admin 页面 404
|
||||||
|
|
||||||
|
确认 `admin-dist` 目录存在且包含 `index.html`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls -la server/admin-dist/
|
||||||
|
```
|
||||||
|
|
||||||
|
### API 连接数据库失败
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 检查数据库连通性
|
||||||
|
docker exec -it overseas-appointment-api sh
|
||||||
|
node -e "require('./src/models').sequelize.authenticate().then(() => console.log('OK')).catch(console.error)"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 查看实时日志
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose logs -f --tail=100
|
||||||
|
```
|
||||||
0
server/data/logs/api/.gitkeep
Normal file
0
server/data/logs/api/.gitkeep
Normal file
0
server/data/logs/nginx/.gitkeep
Normal file
0
server/data/logs/nginx/.gitkeep
Normal file
0
server/data/uploads/.gitkeep
Normal file
0
server/data/uploads/.gitkeep
Normal file
189
server/deploy.sh
Executable file
189
server/deploy.sh
Executable file
|
|
@ -0,0 +1,189 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# 海外预约系统 - 自动部署脚本
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
set -e # 遇到错误立即退出
|
||||||
|
|
||||||
|
# 颜色定义
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# 项目根目录
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||||
|
|
||||||
|
# 检测 docker compose 命令
|
||||||
|
if docker compose version > /dev/null 2>&1; then
|
||||||
|
DOCKER_COMPOSE="docker compose"
|
||||||
|
elif docker-compose version > /dev/null 2>&1; then
|
||||||
|
DOCKER_COMPOSE="docker-compose"
|
||||||
|
else
|
||||||
|
echo -e "${RED}错误: 未找到 docker compose 命令${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${BLUE}============================================${NC}"
|
||||||
|
echo -e "${BLUE} 海外预约系统 - 自动部署脚本${NC}"
|
||||||
|
echo -e "${BLUE}============================================${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 检查 Docker 是否运行
|
||||||
|
check_docker() {
|
||||||
|
echo -e "${YELLOW}[1/5] 检查 Docker 环境...${NC}"
|
||||||
|
if ! docker info > /dev/null 2>&1; then
|
||||||
|
echo -e "${RED}错误: Docker 未运行,请先启动 Docker${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo -e "${GREEN}✓ Docker 运行正常 (使用 $DOCKER_COMPOSE)${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查环境变量文件
|
||||||
|
check_env() {
|
||||||
|
echo -e "${YELLOW}[2/5] 检查环境配置...${NC}"
|
||||||
|
if [ ! -f "$SCRIPT_DIR/.env" ]; then
|
||||||
|
echo -e "${YELLOW} .env 文件不存在,从模板创建...${NC}"
|
||||||
|
cp "$SCRIPT_DIR/.env.example" "$SCRIPT_DIR/.env"
|
||||||
|
echo -e "${YELLOW} 请编辑 $SCRIPT_DIR/.env 配置数据库等信息${NC}"
|
||||||
|
echo -e "${RED} 首次部署请先配置 .env 文件后重新运行脚本${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo -e "${GREEN}✓ 环境配置已就绪${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 构建 Admin 前端
|
||||||
|
build_admin() {
|
||||||
|
echo -e "${YELLOW}[3/5] 构建 Admin 前端...${NC}"
|
||||||
|
cd "$PROJECT_ROOT/admin"
|
||||||
|
|
||||||
|
# 检查 node_modules
|
||||||
|
if [ ! -d "node_modules" ]; then
|
||||||
|
echo -e "${YELLOW} 安装依赖...${NC}"
|
||||||
|
npm install
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${YELLOW} 执行构建...${NC}"
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# 复制构建产物
|
||||||
|
echo -e "${YELLOW} 复制构建产物到 server/admin-dist...${NC}"
|
||||||
|
rm -rf "$SCRIPT_DIR/admin-dist"
|
||||||
|
cp -r dist "$SCRIPT_DIR/admin-dist"
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓ Admin 构建完成${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 构建并启动 Docker 服务
|
||||||
|
deploy_docker() {
|
||||||
|
echo -e "${YELLOW}[4/5] 构建并启动 Docker 服务...${NC}"
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
|
||||||
|
# 停止旧容器(如果存在)
|
||||||
|
echo -e "${YELLOW} 停止旧容器...${NC}"
|
||||||
|
$DOCKER_COMPOSE down 2>/dev/null || true
|
||||||
|
|
||||||
|
# 构建并启动
|
||||||
|
echo -e "${YELLOW} 构建镜像并启动容器...${NC}"
|
||||||
|
$DOCKER_COMPOSE up -d --build
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓ Docker 服务已启动${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查服务状态
|
||||||
|
check_status() {
|
||||||
|
echo -e "${YELLOW}[5/5] 检查服务状态...${NC}"
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
# 检查容器状态
|
||||||
|
echo ""
|
||||||
|
$DOCKER_COMPOSE ps
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 等待服务就绪
|
||||||
|
echo -e "${YELLOW} 等待服务就绪...${NC}"
|
||||||
|
for i in {1..30}; do
|
||||||
|
if curl -s http://localhost:2701/health > /dev/null 2>&1; then
|
||||||
|
echo -e "${GREEN}✓ 服务已就绪${NC}"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if [ $i -eq 30 ]; then
|
||||||
|
echo -e "${YELLOW} 服务启动中,请稍后检查...${NC}"
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# 显示访问信息
|
||||||
|
show_info() {
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}============================================${NC}"
|
||||||
|
echo -e "${GREEN} 部署完成!${NC}"
|
||||||
|
echo -e "${GREEN}============================================${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " 访问地址:"
|
||||||
|
echo -e " Admin 后台: ${BLUE}http://localhost:2701/admin${NC}"
|
||||||
|
echo -e " API 接口: ${BLUE}http://localhost:2701/api/v1${NC}"
|
||||||
|
echo -e " 健康检查: ${BLUE}http://localhost:2701/health${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e " 常用命令:"
|
||||||
|
echo -e " 查看日志: ${YELLOW}cd server && $DOCKER_COMPOSE logs -f${NC}"
|
||||||
|
echo -e " 停止服务: ${YELLOW}cd server && $DOCKER_COMPOSE down${NC}"
|
||||||
|
echo -e " 重启服务: ${YELLOW}cd server && $DOCKER_COMPOSE restart${NC}"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# 主流程
|
||||||
|
main() {
|
||||||
|
check_docker
|
||||||
|
check_env
|
||||||
|
build_admin
|
||||||
|
deploy_docker
|
||||||
|
check_status
|
||||||
|
show_info
|
||||||
|
}
|
||||||
|
|
||||||
|
# 处理命令行参数
|
||||||
|
case "${1:-}" in
|
||||||
|
--skip-build)
|
||||||
|
echo -e "${YELLOW}跳过 Admin 构建${NC}"
|
||||||
|
check_docker
|
||||||
|
check_env
|
||||||
|
deploy_docker
|
||||||
|
check_status
|
||||||
|
show_info
|
||||||
|
;;
|
||||||
|
--restart)
|
||||||
|
echo -e "${YELLOW}重启服务${NC}"
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
$DOCKER_COMPOSE restart
|
||||||
|
check_status
|
||||||
|
;;
|
||||||
|
--stop)
|
||||||
|
echo -e "${YELLOW}停止服务${NC}"
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
$DOCKER_COMPOSE down
|
||||||
|
echo -e "${GREEN}✓ 服务已停止${NC}"
|
||||||
|
;;
|
||||||
|
--logs)
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
$DOCKER_COMPOSE logs -f
|
||||||
|
;;
|
||||||
|
--help)
|
||||||
|
echo "用法: ./deploy.sh [选项]"
|
||||||
|
echo ""
|
||||||
|
echo "选项:"
|
||||||
|
echo " (无参数) 完整部署(构建 Admin + 启动服务)"
|
||||||
|
echo " --skip-build 跳过 Admin 构建,直接部署"
|
||||||
|
echo " --restart 重启服务"
|
||||||
|
echo " --stop 停止服务"
|
||||||
|
echo " --logs 查看日志"
|
||||||
|
echo " --help 显示帮助"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
main
|
||||||
|
;;
|
||||||
|
esac
|
||||||
67
server/docker-compose.yml
Normal file
67
server/docker-compose.yml
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
# 生产环境 Docker Compose 配置
|
||||||
|
# 使用外部 MySQL 和 Redis 服务
|
||||||
|
# 只暴露一个端口,SSL 由外部代理处理
|
||||||
|
|
||||||
|
services:
|
||||||
|
# Backend API Service
|
||||||
|
api:
|
||||||
|
build:
|
||||||
|
context: ../backend
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: overseas-appointment-api
|
||||||
|
restart: unless-stopped
|
||||||
|
expose:
|
||||||
|
- "3000"
|
||||||
|
environment:
|
||||||
|
- NODE_ENV=production
|
||||||
|
- PORT=3000
|
||||||
|
- DB_HOST=${DB_HOST}
|
||||||
|
- DB_PORT=${DB_PORT:-3306}
|
||||||
|
- DB_NAME=${DB_NAME}
|
||||||
|
- DB_USER=${DB_USER}
|
||||||
|
- DB_PASSWORD=${DB_PASSWORD}
|
||||||
|
- REDIS_HOST=${REDIS_HOST}
|
||||||
|
- REDIS_PORT=${REDIS_PORT:-6379}
|
||||||
|
- REDIS_PASSWORD=${REDIS_PASSWORD:-}
|
||||||
|
- JWT_SECRET=${JWT_SECRET}
|
||||||
|
- JWT_EXPIRES_IN=${JWT_EXPIRES_IN:-7d}
|
||||||
|
- JWT_REFRESH_EXPIRES_IN=${JWT_REFRESH_EXPIRES_IN:-30d}
|
||||||
|
- WECHAT_APP_ID=${WECHAT_APP_ID:-}
|
||||||
|
- WECHAT_APP_SECRET=${WECHAT_APP_SECRET:-}
|
||||||
|
- UPLOAD_PATH=/app/uploads
|
||||||
|
- MAX_FILE_SIZE=${MAX_FILE_SIZE:-5242880}
|
||||||
|
- RATE_LIMIT_WINDOW=${RATE_LIMIT_WINDOW:-60000}
|
||||||
|
- RATE_LIMIT_MAX=${RATE_LIMIT_MAX:-100}
|
||||||
|
- LOG_LEVEL=${LOG_LEVEL:-info}
|
||||||
|
volumes:
|
||||||
|
- ./data/uploads:/app/uploads
|
||||||
|
- ./data/logs/api:/app/logs
|
||||||
|
networks:
|
||||||
|
- app-network
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
|
# Nginx - 托管 Admin 静态文件 + 代理 API
|
||||||
|
nginx:
|
||||||
|
image: nginx:alpine
|
||||||
|
container_name: overseas-appointment-nginx
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "${APP_PORT:-2701}:80"
|
||||||
|
volumes:
|
||||||
|
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
|
- ./nginx/conf.d:/etc/nginx/conf.d:ro
|
||||||
|
- ./admin-dist:/usr/share/nginx/html/admin:ro
|
||||||
|
- ./data/logs/nginx:/var/log/nginx
|
||||||
|
depends_on:
|
||||||
|
- api
|
||||||
|
networks:
|
||||||
|
- app-network
|
||||||
|
|
||||||
|
networks:
|
||||||
|
app-network:
|
||||||
|
driver: bridge
|
||||||
93
server/nginx/conf.d/default.conf
Normal file
93
server/nginx/conf.d/default.conf
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name localhost;
|
||||||
|
|
||||||
|
# Client body size limit
|
||||||
|
client_max_body_size 10M;
|
||||||
|
|
||||||
|
# Admin 后台静态文件
|
||||||
|
location /admin {
|
||||||
|
alias /usr/share/nginx/html/admin;
|
||||||
|
index index.html;
|
||||||
|
try_files $uri $uri/ /admin/index.html;
|
||||||
|
|
||||||
|
# 缓存静态资源
|
||||||
|
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# API 接口代理
|
||||||
|
location /api/ {
|
||||||
|
limit_req zone=api_limit burst=20 nodelay;
|
||||||
|
limit_conn conn_limit 10;
|
||||||
|
|
||||||
|
proxy_pass http://api_servers;
|
||||||
|
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_connect_timeout 60s;
|
||||||
|
proxy_send_timeout 60s;
|
||||||
|
proxy_read_timeout 60s;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
location /health {
|
||||||
|
proxy_pass http://api_servers;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
# API 文档
|
||||||
|
location /api-docs {
|
||||||
|
proxy_pass http://api_servers;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 上传文件访问
|
||||||
|
location /uploads/ {
|
||||||
|
proxy_pass http://api_servers;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
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_valid 200 1d;
|
||||||
|
expires 1d;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
# 根路径重定向到 admin
|
||||||
|
location = / {
|
||||||
|
return 302 /admin;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 禁止访问隐藏文件
|
||||||
|
location ~ /\. {
|
||||||
|
deny all;
|
||||||
|
access_log off;
|
||||||
|
log_not_found off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 错误页面
|
||||||
|
error_page 500 502 503 504 /50x.html;
|
||||||
|
location = /50x.html {
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
internal;
|
||||||
|
}
|
||||||
|
}
|
||||||
51
server/nginx/nginx.conf
Normal file
51
server/nginx/nginx.conf
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
user nginx;
|
||||||
|
worker_processes auto;
|
||||||
|
error_log /var/log/nginx/error.log warn;
|
||||||
|
pid /var/run/nginx.pid;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
use epoll;
|
||||||
|
multi_accept on;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
# Logging format
|
||||||
|
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||||
|
'$status $body_bytes_sent "$http_referer" '
|
||||||
|
'"$http_user_agent" "$http_x_forwarded_for" '
|
||||||
|
'rt=$request_time';
|
||||||
|
|
||||||
|
access_log /var/log/nginx/access.log main;
|
||||||
|
|
||||||
|
# Performance optimizations
|
||||||
|
sendfile on;
|
||||||
|
tcp_nopush on;
|
||||||
|
tcp_nodelay on;
|
||||||
|
keepalive_timeout 65;
|
||||||
|
types_hash_max_size 2048;
|
||||||
|
|
||||||
|
# Gzip compression
|
||||||
|
gzip on;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_proxied any;
|
||||||
|
gzip_comp_level 6;
|
||||||
|
gzip_types text/plain text/css text/xml application/json application/javascript
|
||||||
|
application/xml application/xml+rss text/javascript;
|
||||||
|
|
||||||
|
# Rate limiting zones
|
||||||
|
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
|
||||||
|
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
|
||||||
|
|
||||||
|
# Upstream API server
|
||||||
|
upstream api_servers {
|
||||||
|
least_conn;
|
||||||
|
server api:3000 weight=1 max_fails=3 fail_timeout=30s;
|
||||||
|
keepalive 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
include /etc/nginx/conf.d/*.conf;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user