添加服务端
This commit is contained in:
parent
55bee29a4d
commit
43bbd626c5
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -6,3 +6,4 @@ node-modules
|
|||
/node_modules/
|
||||
**/dist/
|
||||
**/node-modules/
|
||||
server/admin-dist/*
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ JWT_EXPIRES_IN=7d
|
|||
JWT_REFRESH_EXPIRES_IN=30d
|
||||
|
||||
# WeChat Configuration
|
||||
WECHAT_APP_ID=your-wechat-app-id
|
||||
WECHAT_APP_SECRET=your-wechat-app-secret
|
||||
WECHAT_APP_ID=wx5ab1c98df1ec13f3
|
||||
WECHAT_APP_SECRET=091adc2c6dc3c7fddf198d782b6ac240
|
||||
|
||||
# File Upload Configuration
|
||||
UPLOAD_PATH=./uploads
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ JWT_EXPIRES_IN=7d
|
|||
JWT_REFRESH_EXPIRES_IN=30d
|
||||
|
||||
# WeChat Configuration
|
||||
WECHAT_APP_ID=your-wechat-app-id
|
||||
WECHAT_APP_SECRET=your-wechat-app-secret
|
||||
WECHAT_APP_ID=wx5ab1c98df1ec13f3
|
||||
WECHAT_APP_SECRET=091adc2c6dc3c7fddf198d782b6ac240
|
||||
|
||||
# File Upload Configuration
|
||||
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