From 43bbd626c5e92db91b6d0dfebd061fd31d43a8cd Mon Sep 17 00:00:00 2001 From: "code@server" Date: Sat, 20 Dec 2025 18:04:10 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=9C=8D=E5=8A=A1=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + backend/.env | 4 +- backend/.env.example | 4 +- server/.env | 53 +++++++++ server/.env.example | 52 +++++++++ server/Makefile | 112 ++++++++++++++++++ server/README.md | 193 +++++++++++++++++++++++++++++++ server/data/logs/api/.gitkeep | 0 server/data/logs/nginx/.gitkeep | 0 server/data/uploads/.gitkeep | 0 server/deploy.sh | 189 ++++++++++++++++++++++++++++++ server/docker-compose.yml | 67 +++++++++++ server/nginx/conf.d/default.conf | 93 +++++++++++++++ server/nginx/nginx.conf | 51 ++++++++ 14 files changed, 815 insertions(+), 4 deletions(-) create mode 100644 server/.env create mode 100644 server/.env.example create mode 100644 server/Makefile create mode 100644 server/README.md create mode 100644 server/data/logs/api/.gitkeep create mode 100644 server/data/logs/nginx/.gitkeep create mode 100644 server/data/uploads/.gitkeep create mode 100755 server/deploy.sh create mode 100644 server/docker-compose.yml create mode 100644 server/nginx/conf.d/default.conf create mode 100644 server/nginx/nginx.conf diff --git a/.gitignore b/.gitignore index db5710a..6f0653d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ node-modules /node_modules/ **/dist/ **/node-modules/ +server/admin-dist/* diff --git a/backend/.env b/backend/.env index 923ef10..0af476c 100644 --- a/backend/.env +++ b/backend/.env @@ -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 diff --git a/backend/.env.example b/backend/.env.example index 923ef10..0af476c 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -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 diff --git a/server/.env b/server/.env new file mode 100644 index 0000000..e982b98 --- /dev/null +++ b/server/.env @@ -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 diff --git a/server/.env.example b/server/.env.example new file mode 100644 index 0000000..3d3bd27 --- /dev/null +++ b/server/.env.example @@ -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 diff --git a/server/Makefile b/server/Makefile new file mode 100644 index 0000000..a0f1add --- /dev/null +++ b/server/Makefile @@ -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 diff --git a/server/README.md b/server/README.md new file mode 100644 index 0000000..af89560 --- /dev/null +++ b/server/README.md @@ -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 +``` diff --git a/server/data/logs/api/.gitkeep b/server/data/logs/api/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/server/data/logs/nginx/.gitkeep b/server/data/logs/nginx/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/server/data/uploads/.gitkeep b/server/data/uploads/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/server/deploy.sh b/server/deploy.sh new file mode 100755 index 0000000..8b90574 --- /dev/null +++ b/server/deploy.sh @@ -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 diff --git a/server/docker-compose.yml b/server/docker-compose.yml new file mode 100644 index 0000000..8917d48 --- /dev/null +++ b/server/docker-compose.yml @@ -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 diff --git a/server/nginx/conf.d/default.conf b/server/nginx/conf.d/default.conf new file mode 100644 index 0000000..22bb05b --- /dev/null +++ b/server/nginx/conf.d/default.conf @@ -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; + } +} diff --git a/server/nginx/nginx.conf b/server/nginx/nginx.conf new file mode 100644 index 0000000..5471b76 --- /dev/null +++ b/server/nginx/nginx.conf @@ -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; +}