feat: 初始化考培练系统项目
- 从服务器拉取完整代码 - 按框架规范整理项目结构 - 配置 Drone CI 测试环境部署 - 包含后端(FastAPI)、前端(Vue3)、管理端 技术栈: Vue3 + TypeScript + FastAPI + MySQL
This commit is contained in:
115
deploy/docker/docker-compose.admin.yml
Normal file
115
deploy/docker/docker-compose.admin.yml
Normal file
@@ -0,0 +1,115 @@
|
||||
# 考培练系统 SaaS 超级管理后台 Docker Compose 配置
|
||||
#
|
||||
# 启动命令:
|
||||
# cd /root/aiedu && docker compose -f docker-compose.admin.yml up -d
|
||||
#
|
||||
# 重新构建后端:
|
||||
# docker compose -f docker-compose.admin.yml build --no-cache kaopeilian-admin-backend
|
||||
#
|
||||
# 服务说明:
|
||||
# - kaopeilian-admin-frontend: 管理后台前端 (端口 3030)
|
||||
# - kaopeilian-admin-backend: 管理后台后端 (端口 8030)
|
||||
#
|
||||
# 域名:admin.kpl.ireborn.com.cn
|
||||
#
|
||||
# 注意:敏感配置从 .env.admin 文件读取
|
||||
|
||||
name: kaopeilian-admin
|
||||
|
||||
services:
|
||||
# ============================================
|
||||
# 管理后台前端
|
||||
# ============================================
|
||||
kaopeilian-admin-frontend:
|
||||
build:
|
||||
context: ./kaopeilian-admin-frontend
|
||||
dockerfile: Dockerfile
|
||||
image: kaopeilian-admin-frontend:1.0.0
|
||||
container_name: kaopeilian-admin-frontend
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
ports:
|
||||
- "127.0.0.1:3030:80" # 仅本地访问,通过 Nginx 反向代理
|
||||
volumes:
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
networks:
|
||||
- kaopeilian-network
|
||||
depends_on:
|
||||
kaopeilian-admin-backend:
|
||||
condition: service_started
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:80/"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 256M
|
||||
reservations:
|
||||
cpus: '0.1'
|
||||
memory: 64M
|
||||
|
||||
# ============================================
|
||||
# 管理后台后端(开发环境 - 热重载)
|
||||
# ============================================
|
||||
kaopeilian-admin-backend:
|
||||
build:
|
||||
context: ./kaopeilian-backend
|
||||
dockerfile: Dockerfile.admin
|
||||
image: kaopeilian-admin-backend:1.0.0
|
||||
container_name: kaopeilian-admin-backend
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env.admin
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- PYTHONPATH=/app
|
||||
ports:
|
||||
- "127.0.0.1:8030:8000" # 仅本地访问,通过 Nginx 反向代理
|
||||
volumes:
|
||||
# 代码热重载 - 挂载整个 app 目录
|
||||
- ./kaopeilian-backend/app:/app/app:rw
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
# 开发模式命令 - 启用热重载
|
||||
command: ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload", "--reload-dir", "/app/app"]
|
||||
networks:
|
||||
- kaopeilian-network
|
||||
- prod-network
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 60s
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1'
|
||||
memory: 1G
|
||||
reservations:
|
||||
cpus: '0.25'
|
||||
memory: 256M
|
||||
|
||||
networks:
|
||||
kaopeilian-network:
|
||||
external: true
|
||||
name: kaopeilian-network
|
||||
prod-network:
|
||||
external: true
|
||||
name: prod-network
|
||||
186
deploy/docker/docker-compose.dev.yml
Normal file
186
deploy/docker/docker-compose.dev.yml
Normal file
@@ -0,0 +1,186 @@
|
||||
# 考培练系统开发环境 Docker Compose 配置
|
||||
name: kaopeilian-dev
|
||||
|
||||
services:
|
||||
# 前端开发服务
|
||||
frontend-dev:
|
||||
build:
|
||||
context: ./kaopeilian-frontend
|
||||
dockerfile: Dockerfile.dev
|
||||
container_name: kaopeilian-frontend-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3001:3001"
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
- VITE_APP_ENV=development
|
||||
- VITE_API_BASE_URL=""
|
||||
- VITE_WS_BASE_URL=""
|
||||
- VITE_USE_MOCK_DATA=false
|
||||
- VITE_ENABLE_DEVTOOLS=true
|
||||
- VITE_ENABLE_REQUEST_LOG=true
|
||||
- CHOKIDAR_USEPOLLING=true # 支持Docker中的文件监听
|
||||
- WATCHPACK_POLLING=true # 支持热重载
|
||||
volumes:
|
||||
# 挂载源代码实现热重载(关键配置)
|
||||
- ./kaopeilian-frontend:/app
|
||||
- kaopeilian_frontend_node_modules:/app/node_modules # 使用命名卷
|
||||
- ./kaopeilian-frontend/logs:/app/logs
|
||||
networks:
|
||||
- kaopeilian-dev-network
|
||||
depends_on:
|
||||
backend-dev:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3001/"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
|
||||
# 后端开发服务
|
||||
backend-dev:
|
||||
build:
|
||||
context: ./kaopeilian-backend
|
||||
dockerfile: Dockerfile.dev
|
||||
container_name: kaopeilian-backend-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8000:8000"
|
||||
environment:
|
||||
- PYTHONPATH=/app
|
||||
- DEBUG=true
|
||||
- ENV=development
|
||||
- "DATABASE_URL=mysql+aiomysql://root:nj861021@mysql-dev:3306/kaopeilian?charset=utf8mb4"
|
||||
- REDIS_URL=redis://redis-dev:6379/0
|
||||
- MYSQL_HOST=mysql-dev
|
||||
- MYSQL_PORT=3306
|
||||
- MYSQL_USER=root
|
||||
- "MYSQL_PASSWORD=nj861021"
|
||||
- MYSQL_DATABASE=kaopeilian
|
||||
- REDIS_HOST=redis-dev
|
||||
- REDIS_PORT=6379
|
||||
- REDIS_DB=0
|
||||
- PYTHONUNBUFFERED=1 # 确保日志实时输出
|
||||
- CORS_ORIGINS=["http://localhost:3000","http://localhost:3001","http://localhost:5173","http://127.0.0.1:3000","http://127.0.0.1:3001","http://127.0.0.1:5173"]
|
||||
# 完全禁用代理(覆盖 Docker Desktop 的代理配置)
|
||||
- HTTP_PROXY=
|
||||
- HTTPS_PROXY=
|
||||
- http_proxy=
|
||||
- https_proxy=
|
||||
- NO_PROXY=
|
||||
- no_proxy=
|
||||
volumes:
|
||||
# 挂载源代码实现热重载(关键配置)
|
||||
- ./kaopeilian-backend:/app:rw
|
||||
- ./kaopeilian-backend/uploads:/app/uploads:rw
|
||||
- ./kaopeilian-backend/logs:/app/logs:rw
|
||||
# 排除虚拟环境目录,避免冲突
|
||||
- /app/venv
|
||||
networks:
|
||||
- kaopeilian-dev-network
|
||||
depends_on:
|
||||
mysql-dev:
|
||||
condition: service_healthy
|
||||
redis-dev:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
|
||||
# 开发数据库
|
||||
mysql-dev:
|
||||
image: mysql:8.0.43
|
||||
container_name: kaopeilian-mysql-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3306:3306"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: nj861021
|
||||
MYSQL_DATABASE: kaopeilian
|
||||
MYSQL_CHARACTER_SET_SERVER: utf8mb4
|
||||
MYSQL_COLLATION_SERVER: utf8mb4_unicode_ci
|
||||
volumes:
|
||||
- mysql_dev_data:/var/lib/mysql
|
||||
- ./kaopeilian-backend/scripts/init_database_unified.sql:/docker-entrypoint-initdb.d/init.sql:ro
|
||||
- ./kaopeilian-backend/mysql.cnf:/etc/mysql/conf.d/mysql.cnf:ro
|
||||
command: --default-authentication-plugin=mysql_native_password
|
||||
networks:
|
||||
- kaopeilian-dev-network
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-pnj861021"]
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
start_period: 30s
|
||||
|
||||
# 开发 Redis 缓存
|
||||
redis-dev:
|
||||
image: redis:7.2-alpine
|
||||
container_name: kaopeilian-redis-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "6379:6379"
|
||||
volumes:
|
||||
- redis_dev_data:/data
|
||||
networks:
|
||||
- kaopeilian-dev-network
|
||||
command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
|
||||
# phpMyAdmin 数据库管理界面(可选)
|
||||
phpmyadmin:
|
||||
image: phpmyadmin/phpmyadmin:latest
|
||||
container_name: kaopeilian-phpmyadmin-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:80"
|
||||
environment:
|
||||
PMA_HOST: mysql-dev
|
||||
PMA_PORT: 3306
|
||||
PMA_USER: root
|
||||
PMA_PASSWORD: nj861021
|
||||
networks:
|
||||
- kaopeilian-dev-network
|
||||
depends_on:
|
||||
mysql-dev:
|
||||
condition: service_healthy
|
||||
profiles:
|
||||
- admin # 使用 profile 控制是否启动
|
||||
|
||||
# 邮件测试服务(开发环境用于测试邮件发送)
|
||||
mailhog:
|
||||
image: mailhog/mailhog:latest
|
||||
container_name: kaopeilian-mailhog-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "1025:1025" # SMTP 端口
|
||||
- "8025:8025" # Web UI 端口
|
||||
networks:
|
||||
- kaopeilian-dev-network
|
||||
profiles:
|
||||
- mail # 使用 profile 控制是否启动
|
||||
|
||||
# 开发网络
|
||||
networks:
|
||||
kaopeilian-dev-network:
|
||||
driver: bridge
|
||||
name: kaopeilian-dev-network
|
||||
|
||||
# 开发数据卷
|
||||
volumes:
|
||||
mysql_dev_data:
|
||||
driver: local
|
||||
name: kaopeilian-mysql-dev-data
|
||||
redis_dev_data:
|
||||
driver: local
|
||||
name: kaopeilian-redis-dev-data
|
||||
kaopeilian_frontend_node_modules:
|
||||
driver: local
|
||||
name: kaopeilian-frontend-node-modules
|
||||
207
deploy/docker/docker-compose.kpl.yml
Normal file
207
deploy/docker/docker-compose.kpl.yml
Normal file
@@ -0,0 +1,207 @@
|
||||
# 瑞小美团队开发环境 Docker Compose 配置
|
||||
# 域名:kpl.ireborn.com.cn
|
||||
# 支持热重载,与演示系统完全隔离
|
||||
#
|
||||
# ⚠️ 敏感配置(API Key 等)请在 .env.kpl 文件中设置
|
||||
# 启动命令:docker compose --env-file .env.kpl -f docker-compose.kpl.yml up -d
|
||||
|
||||
name: kpl-dev
|
||||
|
||||
services:
|
||||
# 前端开发服务
|
||||
kpl-frontend-dev:
|
||||
build:
|
||||
context: ./kaopeilian-frontend
|
||||
dockerfile: Dockerfile.dev
|
||||
container_name: kpl-frontend-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3002:3001"
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
- VITE_APP_ENV=development
|
||||
- VITE_API_BASE_URL=https://kpl.ireborn.com.cn
|
||||
- VITE_WS_BASE_URL=wss://kpl.ireborn.com.cn
|
||||
- VITE_USE_MOCK_DATA=false
|
||||
- VITE_ENABLE_DEVTOOLS=true
|
||||
- VITE_ENABLE_REQUEST_LOG=true
|
||||
- CHOKIDAR_USEPOLLING=true # 支持Docker中的文件监听
|
||||
- WATCHPACK_POLLING=true # 支持热重载
|
||||
volumes:
|
||||
# 挂载源代码实现热重载(关键配置)
|
||||
- ./kaopeilian-frontend:/app
|
||||
- kpl_frontend_node_modules:/app/node_modules # 使用命名卷
|
||||
- ./kaopeilian-frontend/logs:/app/logs
|
||||
networks:
|
||||
- kpl-dev-network
|
||||
depends_on:
|
||||
kpl-backend-dev:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3001/"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
|
||||
# 后端开发服务
|
||||
kpl-backend-dev:
|
||||
build:
|
||||
context: ./kaopeilian-backend
|
||||
dockerfile: Dockerfile.dev
|
||||
container_name: kpl-backend-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8001:8000"
|
||||
environment:
|
||||
- PYTHONPATH=/app
|
||||
- DEBUG=true
|
||||
- ENV=development
|
||||
- "DATABASE_URL=mysql+aiomysql://root:nj861021@kpl-mysql-dev:3306/kaopeilian?charset=utf8mb4"
|
||||
- REDIS_URL=redis://kpl-redis-dev:6379/0
|
||||
- MYSQL_HOST=kpl-mysql-dev
|
||||
- MYSQL_PORT=3306
|
||||
- MYSQL_USER=root
|
||||
- "MYSQL_PASSWORD=nj861021"
|
||||
- MYSQL_DATABASE=kaopeilian
|
||||
- REDIS_HOST=kpl-redis-dev
|
||||
- REDIS_PORT=6379
|
||||
- REDIS_DB=0
|
||||
- PYTHONUNBUFFERED=1 # 确保日志实时输出
|
||||
- CORS_ORIGINS=["https://kpl.ireborn.com.cn","http://localhost:3002","http://127.0.0.1:3002"]
|
||||
# Coze OAuth配置
|
||||
- COZE_OAUTH_CLIENT_ID=1114009328887
|
||||
- COZE_OAUTH_PUBLIC_KEY_ID=GGs9pw0BDHx2k9vGGehUyRgKV-PyUWLBncDs-YNNN_I
|
||||
- COZE_OAUTH_PRIVATE_KEY_PATH=/app/secrets/coze_private_key.pem
|
||||
- COZE_PRACTICE_BOT_ID=7560643598174683145
|
||||
# Coze 播课工作流配置(瑞小美专用)
|
||||
- COZE_BROADCAST_WORKFLOW_ID=7561161554420482088
|
||||
- COZE_BROADCAST_SPACE_ID=7474971491470688296
|
||||
- COZE_BROADCAST_BOT_ID=7560643598174683145
|
||||
# 完全禁用代理(覆盖 Docker Desktop 的代理配置)
|
||||
- HTTP_PROXY=
|
||||
- HTTPS_PROXY=
|
||||
- http_proxy=
|
||||
- https_proxy=
|
||||
- NO_PROXY=
|
||||
- no_proxy=
|
||||
# AI 服务配置(遵循瑞小美 AI 接入规范)
|
||||
# 注意:API Key 应从 .env 文件或密钥管理服务获取
|
||||
- AI_PRIMARY_API_KEY=${AI_PRIMARY_API_KEY:-}
|
||||
- AI_ANTHROPIC_API_KEY=${AI_ANTHROPIC_API_KEY:-}
|
||||
- AI_PRIMARY_BASE_URL=https://4sapi.com/v1
|
||||
- AI_FALLBACK_API_KEY=${AI_FALLBACK_API_KEY:-}
|
||||
- AI_FALLBACK_BASE_URL=https://openrouter.ai/api/v1
|
||||
# 默认模型:遵循"优先最强"原则,使用 Claude Opus 4.5
|
||||
- AI_DEFAULT_MODEL=claude-opus-4-5-20251101-thinking
|
||||
- AI_TIMEOUT=120
|
||||
volumes:
|
||||
# 挂载源代码实现热重载(关键配置)
|
||||
- ./kaopeilian-backend:/app:rw
|
||||
- /data/kaopeilian/uploads/kpl:/app/uploads:rw # 迁移到数据盘
|
||||
- ./kaopeilian-backend/logs:/app/logs:rw
|
||||
- ./kaopeilian-backend/secrets:/app/secrets:ro
|
||||
# 排除虚拟环境目录,避免冲突
|
||||
- /app/venv
|
||||
networks:
|
||||
- kpl-dev-network
|
||||
depends_on:
|
||||
kpl-mysql-dev:
|
||||
condition: service_healthy
|
||||
kpl-redis-dev:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
|
||||
# 开发数据库
|
||||
kpl-mysql-dev:
|
||||
image: mysql:8.0.43
|
||||
container_name: kpl-mysql-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3308:3306"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: nj861021
|
||||
MYSQL_DATABASE: kaopeilian
|
||||
MYSQL_CHARACTER_SET_SERVER: utf8mb4
|
||||
MYSQL_COLLATION_SERVER: utf8mb4_unicode_ci
|
||||
TZ: Asia/Shanghai
|
||||
volumes:
|
||||
- kpl_mysql_dev_data:/var/lib/mysql
|
||||
- ./kaopeilian-backend/scripts/init_database_unified.sql:/docker-entrypoint-initdb.d/init.sql:ro
|
||||
- ./kaopeilian-backend/mysql.cnf:/etc/mysql/conf.d/mysql.cnf:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
command: --default-authentication-plugin=mysql_native_password
|
||||
networks:
|
||||
- kpl-dev-network
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-pnj861021"]
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
start_period: 30s
|
||||
|
||||
# 开发 Redis 缓存
|
||||
kpl-redis-dev:
|
||||
image: redis:7.2-alpine
|
||||
container_name: kpl-redis-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "6380:6379"
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
volumes:
|
||||
- kpl_redis_dev_data:/data
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
networks:
|
||||
- kpl-dev-network
|
||||
command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
|
||||
# phpMyAdmin 数据库管理界面(可选)
|
||||
kpl-phpmyadmin:
|
||||
image: phpmyadmin/phpmyadmin:latest
|
||||
container_name: kpl-phpmyadmin-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8081:80"
|
||||
environment:
|
||||
PMA_HOST: kpl-mysql-dev
|
||||
PMA_PORT: 3306
|
||||
PMA_USER: root
|
||||
PMA_PASSWORD: nj861021
|
||||
networks:
|
||||
- kpl-dev-network
|
||||
depends_on:
|
||||
kpl-mysql-dev:
|
||||
condition: service_healthy
|
||||
profiles:
|
||||
- admin # 使用 profile 控制是否启动
|
||||
|
||||
# 开发网络
|
||||
networks:
|
||||
kpl-dev-network:
|
||||
external: true
|
||||
name: kpl-dev-network
|
||||
|
||||
# 开发数据卷
|
||||
volumes:
|
||||
kpl_mysql_dev_data:
|
||||
driver: local
|
||||
name: kpl-mysql-dev-data
|
||||
kpl_redis_dev_data:
|
||||
driver: local
|
||||
name: kpl-redis-dev-data
|
||||
kpl_frontend_node_modules:
|
||||
driver: local
|
||||
name: kpl-frontend-node-modules
|
||||
|
||||
482
deploy/docker/docker-compose.prod-multi.yml
Normal file
482
deploy/docker/docker-compose.prod-multi.yml
Normal file
@@ -0,0 +1,482 @@
|
||||
# 多客户生产环境 Docker Compose 配置
|
||||
# 共享MySQL实例,独立前端/后端/Redis容器
|
||||
#
|
||||
# 重要说明:
|
||||
# - 所有租户前端共享同一个 dist 目录: /root/aiedu/kaopeilian-frontend/dist
|
||||
# - 编译一次前端,所有租户自动更新(无需重新构建镜像)
|
||||
# - 更新前端步骤:cd /root/aiedu/kaopeilian-frontend && npm run build
|
||||
# - 后端API地址由nginx反向代理根据域名自动路由,前端无需区分
|
||||
#
|
||||
name: prod-multi
|
||||
|
||||
services:
|
||||
# ============================================
|
||||
# 共享MySQL数据库服务
|
||||
# ============================================
|
||||
prod-mysql:
|
||||
image: mysql:8.0.43
|
||||
container_name: prod-mysql
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
MYSQL_ROOT_PASSWORD: ProdMySQL2025!@#
|
||||
MYSQL_CHARACTER_SET_SERVER: utf8mb4
|
||||
MYSQL_COLLATION_SERVER: utf8mb4_unicode_ci
|
||||
ports:
|
||||
- "3309:3306"
|
||||
volumes:
|
||||
- /data/mysql-data:/var/lib/mysql
|
||||
- ./kaopeilian-backend/mysql.cnf:/etc/mysql/conf.d/mysql.cnf:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
command: --default-authentication-plugin=mysql_native_password
|
||||
networks:
|
||||
- prod-network
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-pProdMySQL2025!@#"]
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
start_period: 30s
|
||||
|
||||
# ============================================
|
||||
# 华尔倍丽 (hua.ireborn.com.cn)
|
||||
# ============================================
|
||||
hua-frontend:
|
||||
# 使用共享的前端镜像,挂载统一的dist目录
|
||||
image: kaopeilian-frontend:shared
|
||||
container_name: hua-frontend
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
ports:
|
||||
- "3010:80"
|
||||
volumes:
|
||||
- /root/aiedu/kaopeilian-frontend/dist:/usr/share/nginx/html:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
networks:
|
||||
- prod-network
|
||||
- kaopeilian-network
|
||||
depends_on:
|
||||
- hua-backend
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:80/"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
hua-backend:
|
||||
build:
|
||||
context: ./kaopeilian-backend
|
||||
dockerfile: Dockerfile
|
||||
image: prod-multi-hua-backend:latest
|
||||
container_name: hua-backend
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- ./kaopeilian-backend/.env.hua
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- PYTHONPATH=/app
|
||||
ports:
|
||||
- "8010:8000"
|
||||
volumes:
|
||||
- ./kaopeilian-backend/app:/app/app # 代码热重载
|
||||
- /data/prod-envs/uploads-hua:/app/uploads
|
||||
- /data/prod-envs/logs-hua:/app/logs
|
||||
- /data/prod-envs/secrets:/app/secrets:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
command: ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
|
||||
networks:
|
||||
- prod-network
|
||||
- kaopeilian-network
|
||||
depends_on:
|
||||
prod-mysql:
|
||||
condition: service_healthy
|
||||
hua-redis:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
|
||||
hua-redis:
|
||||
image: redis:7.2-alpine
|
||||
container_name: hua-redis
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
ports:
|
||||
- "6390:6379"
|
||||
volumes:
|
||||
- /data/redis-data/hua:/data
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
networks:
|
||||
- prod-network
|
||||
command: redis-server --appendonly yes --maxmemory 128mb --maxmemory-policy allkeys-lru
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
|
||||
# ============================================
|
||||
# 杨扬宠物 (yy.ireborn.com.cn)
|
||||
# ============================================
|
||||
yy-frontend:
|
||||
# 使用共享的前端镜像,挂载统一的dist目录
|
||||
image: kaopeilian-frontend:shared
|
||||
container_name: yy-frontend
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
ports:
|
||||
- "3011:80"
|
||||
volumes:
|
||||
- /root/aiedu/kaopeilian-frontend/dist:/usr/share/nginx/html:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
networks:
|
||||
- prod-network
|
||||
- kaopeilian-network
|
||||
depends_on:
|
||||
- yy-backend
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:80/"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
yy-backend:
|
||||
build:
|
||||
context: ./kaopeilian-backend
|
||||
dockerfile: Dockerfile
|
||||
image: prod-multi-yy-backend:latest
|
||||
container_name: yy-backend
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- ./kaopeilian-backend/.env.yy
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- PYTHONPATH=/app
|
||||
ports:
|
||||
- "8011:8000"
|
||||
volumes:
|
||||
- ./kaopeilian-backend/app:/app/app # 代码热重载
|
||||
- /data/prod-envs/uploads-yy:/app/uploads
|
||||
- /data/prod-envs/logs-yy:/app/logs
|
||||
- /data/prod-envs/secrets:/app/secrets:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
command: ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
|
||||
networks:
|
||||
- prod-network
|
||||
- kaopeilian-network
|
||||
depends_on:
|
||||
prod-mysql:
|
||||
condition: service_healthy
|
||||
yy-redis:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
|
||||
yy-redis:
|
||||
image: redis:7.2-alpine
|
||||
container_name: yy-redis
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
ports:
|
||||
- "6391:6379"
|
||||
volumes:
|
||||
- /data/redis-data/yy:/data
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
networks:
|
||||
- prod-network
|
||||
command: redis-server --appendonly yes --maxmemory 128mb --maxmemory-policy allkeys-lru
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
|
||||
# ============================================
|
||||
# 武汉禾丽 (hl.ireborn.com.cn)
|
||||
# ============================================
|
||||
hl-frontend:
|
||||
# 使用共享的前端镜像,挂载统一的dist目录
|
||||
image: kaopeilian-frontend:shared
|
||||
container_name: hl-frontend
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
ports:
|
||||
- "3012:80"
|
||||
volumes:
|
||||
- /root/aiedu/kaopeilian-frontend/dist:/usr/share/nginx/html:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
networks:
|
||||
- prod-network
|
||||
- kaopeilian-network
|
||||
depends_on:
|
||||
- hl-backend
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:80/"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
hl-backend:
|
||||
build:
|
||||
context: ./kaopeilian-backend
|
||||
dockerfile: Dockerfile
|
||||
image: prod-multi-hl-backend:latest
|
||||
container_name: hl-backend
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- ./kaopeilian-backend/.env.hl
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- PYTHONPATH=/app
|
||||
ports:
|
||||
- "8012:8000"
|
||||
volumes:
|
||||
- ./kaopeilian-backend/app:/app/app # 代码热重载
|
||||
- /data/prod-envs/uploads-hl:/app/uploads
|
||||
- /data/prod-envs/logs-hl:/app/logs
|
||||
- /data/prod-envs/secrets:/app/secrets:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
command: ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
|
||||
networks:
|
||||
- prod-network
|
||||
- kaopeilian-network
|
||||
depends_on:
|
||||
prod-mysql:
|
||||
condition: service_healthy
|
||||
hl-redis:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
|
||||
hl-redis:
|
||||
image: redis:7.2-alpine
|
||||
container_name: hl-redis
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
ports:
|
||||
- "6392:6379"
|
||||
volumes:
|
||||
- /data/redis-data/hl:/data
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
networks:
|
||||
- prod-network
|
||||
command: redis-server --appendonly yes --maxmemory 128mb --maxmemory-policy allkeys-lru
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
|
||||
# ============================================
|
||||
# 芯颜定制 (xy.ireborn.com.cn)
|
||||
# ============================================
|
||||
xy-frontend:
|
||||
# 使用共享的前端镜像,挂载统一的dist目录
|
||||
image: kaopeilian-frontend:shared
|
||||
container_name: xy-frontend
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
ports:
|
||||
- "3013:80"
|
||||
volumes:
|
||||
- /root/aiedu/kaopeilian-frontend/dist:/usr/share/nginx/html:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
networks:
|
||||
- prod-network
|
||||
- kaopeilian-network
|
||||
depends_on:
|
||||
- xy-backend
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:80/"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
xy-backend:
|
||||
build:
|
||||
context: ./kaopeilian-backend
|
||||
dockerfile: Dockerfile
|
||||
image: prod-multi-xy-backend:latest
|
||||
container_name: xy-backend
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- ./kaopeilian-backend/.env.xy
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- PYTHONPATH=/app
|
||||
ports:
|
||||
- "8013:8000"
|
||||
volumes:
|
||||
- ./kaopeilian-backend/app:/app/app # 代码热重载
|
||||
- /data/prod-envs/uploads-xy:/app/uploads
|
||||
- /data/prod-envs/logs-xy:/app/logs
|
||||
- /data/prod-envs/secrets:/app/secrets:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
command: ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
|
||||
networks:
|
||||
- prod-network
|
||||
- kaopeilian-network
|
||||
depends_on:
|
||||
prod-mysql:
|
||||
condition: service_healthy
|
||||
xy-redis:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
|
||||
xy-redis:
|
||||
image: redis:7.2-alpine
|
||||
container_name: xy-redis
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
ports:
|
||||
- "6393:6379"
|
||||
volumes:
|
||||
- /data/redis-data/xy:/data
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
networks:
|
||||
- prod-network
|
||||
command: redis-server --appendonly yes --maxmemory 128mb --maxmemory-policy allkeys-lru
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
|
||||
# ============================================
|
||||
# 飞沃 (fw.ireborn.com.cn)
|
||||
# ============================================
|
||||
fw-frontend:
|
||||
# 使用共享的前端镜像,挂载统一的dist目录
|
||||
# 这样只需编译一次前端,所有租户自动更新
|
||||
image: kaopeilian-frontend:shared
|
||||
container_name: fw-frontend
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
ports:
|
||||
- "3014:80"
|
||||
volumes:
|
||||
- /root/aiedu/kaopeilian-frontend/dist:/usr/share/nginx/html:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
networks:
|
||||
- prod-network
|
||||
- kaopeilian-network
|
||||
depends_on:
|
||||
- fw-backend
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:80/"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
fw-backend:
|
||||
build:
|
||||
context: ./kaopeilian-backend
|
||||
dockerfile: Dockerfile
|
||||
image: prod-multi-fw-backend:latest
|
||||
container_name: fw-backend
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- ./kaopeilian-backend/.env.fw
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- PYTHONPATH=/app
|
||||
ports:
|
||||
- "8014:8000"
|
||||
volumes:
|
||||
- ./kaopeilian-backend/app:/app/app # 代码热重载
|
||||
- /data/prod-envs/uploads-fw:/app/uploads
|
||||
- /data/prod-envs/logs-fw:/app/logs
|
||||
- /data/prod-envs/secrets:/app/secrets:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
command: ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
|
||||
networks:
|
||||
- prod-network
|
||||
- kaopeilian-network
|
||||
depends_on:
|
||||
prod-mysql:
|
||||
condition: service_healthy
|
||||
fw-redis:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
|
||||
fw-redis:
|
||||
image: redis:7.2-alpine
|
||||
container_name: fw-redis
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
ports:
|
||||
- "6394:6379"
|
||||
volumes:
|
||||
- /data/redis-data/fw:/data
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
networks:
|
||||
- prod-network
|
||||
command: redis-server --appendonly yes --maxmemory 128mb --maxmemory-policy allkeys-lru
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
|
||||
# 网络配置
|
||||
networks:
|
||||
prod-network:
|
||||
driver: bridge
|
||||
name: prod-network
|
||||
kaopeilian-network:
|
||||
external: true
|
||||
name: kaopeilian-network
|
||||
|
||||
|
||||
|
||||
178
deploy/docker/docker-compose.yml
Normal file
178
deploy/docker/docker-compose.yml
Normal file
@@ -0,0 +1,178 @@
|
||||
# 考培练系统完整部署配置
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
# MySQL数据库服务
|
||||
mysql:
|
||||
image: mysql:8.0.43
|
||||
container_name: kaopeilian-mysql
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
MYSQL_ROOT_PASSWORD: Kaopeilian2025!@#
|
||||
MYSQL_DATABASE: kaopeilian
|
||||
MYSQL_CHARACTER_SET_SERVER: utf8mb4
|
||||
MYSQL_COLLATION_SERVER: utf8mb4_unicode_ci
|
||||
ports:
|
||||
- "3307:3306"
|
||||
volumes:
|
||||
- mysql_data:/var/lib/mysql
|
||||
- ./考培练系统规划/数据库-里程碑备份/7-完成数据分析模块-20251016_075159.sql:/docker-entrypoint-initdb.d/init.sql
|
||||
- ./kaopeilian-backend/mysql.cnf:/etc/mysql/conf.d/mysql.cnf
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
command: --default-authentication-plugin=mysql_native_password
|
||||
networks:
|
||||
- kaopeilian-network
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
|
||||
# 后端API服务
|
||||
backend:
|
||||
build:
|
||||
context: ./kaopeilian-backend
|
||||
dockerfile: Dockerfile
|
||||
container_name: kaopeilian-backend
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- ./kaopeilian-backend/.env.production
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
- PYTHONPATH=/app
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
- ./kaopeilian-backend/app:/app/app # 代码热重载
|
||||
- /data/kaopeilian/uploads/demo:/app/uploads # 迁移到数据盘
|
||||
- ./kaopeilian-backend/logs:/app/logs
|
||||
- ./kaopeilian-backend/secrets:/app/secrets:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
command: ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
|
||||
networks:
|
||||
- kaopeilian-network
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
|
||||
# 前端服务
|
||||
frontend:
|
||||
build:
|
||||
context: ./kaopeilian-frontend
|
||||
dockerfile: Dockerfile
|
||||
target: production
|
||||
args:
|
||||
- NODE_ENV=production
|
||||
- VITE_API_BASE_URL=https://aiedu.ireborn.com.cn
|
||||
- VITE_WS_BASE_URL=wss://aiedu.ireborn.com.cn
|
||||
- VITE_USE_MOCK_DATA=false
|
||||
container_name: kaopeilian-frontend
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
volumes:
|
||||
- ./kaopeilian-frontend/docker/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./kaopeilian-frontend/docker/default.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
networks:
|
||||
- kaopeilian-network
|
||||
depends_on:
|
||||
- backend
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:80/"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
# Redis缓存服务
|
||||
redis:
|
||||
image: redis:7.2-alpine
|
||||
container_name: kaopeilian-redis
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
ports:
|
||||
- "6379:6379"
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
networks:
|
||||
- kaopeilian-network
|
||||
command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
|
||||
# Nginx反向代理和SSL终止
|
||||
nginx:
|
||||
image: nginx:1.25-alpine
|
||||
container_name: kaopeilian-nginx
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Asia/Shanghai
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./nginx/conf.d:/etc/nginx/conf.d:ro
|
||||
- /etc/letsencrypt:/etc/letsencrypt:ro
|
||||
- /var/www/certbot:/var/www/certbot:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
networks:
|
||||
- kaopeilian-network
|
||||
- kpl-dev-network
|
||||
depends_on:
|
||||
- frontend
|
||||
- backend
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:80/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
# SSL证书续期服务
|
||||
certbot:
|
||||
image: certbot/certbot:latest
|
||||
container_name: kaopeilian-certbot
|
||||
restart: "no"
|
||||
volumes:
|
||||
- /etc/letsencrypt:/etc/letsencrypt
|
||||
- /var/www/certbot:/var/www/certbot
|
||||
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
|
||||
profiles:
|
||||
- ssl
|
||||
|
||||
# 网络配置
|
||||
networks:
|
||||
kaopeilian-network:
|
||||
driver: bridge
|
||||
name: kaopeilian-network
|
||||
kpl-dev-network:
|
||||
external: true
|
||||
name: kpl-dev-network
|
||||
|
||||
# 数据卷配置
|
||||
volumes:
|
||||
redis_data:
|
||||
driver: local
|
||||
name: kaopeilian-redis-data
|
||||
mysql_data:
|
||||
driver: local
|
||||
name: kaopeilian-mysql-data
|
||||
91
deploy/nginx/conf.d/admin.conf
Normal file
91
deploy/nginx/conf.d/admin.conf
Normal file
@@ -0,0 +1,91 @@
|
||||
# 考培练系统 SaaS 超级管理后台
|
||||
# 域名:admin.kpl.ireborn.com.cn
|
||||
|
||||
# HTTP 重定向到 HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name admin.kpl.ireborn.com.cn;
|
||||
|
||||
# Let's Encrypt 证书验证
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS
|
||||
server {
|
||||
listen 443 ssl;
|
||||
http2 on;
|
||||
server_name admin.kpl.ireborn.com.cn;
|
||||
|
||||
# SSL 证书
|
||||
ssl_certificate /etc/letsencrypt/live/admin.kpl.ireborn.com.cn/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/admin.kpl.ireborn.com.cn/privkey.pem;
|
||||
|
||||
# SSL 配置
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:AdminSSL:10m;
|
||||
ssl_session_tickets off;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_prefer_server_ciphers off;
|
||||
|
||||
# 安全头
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
# Docker DNS resolver
|
||||
resolver 127.0.0.11 valid=30s;
|
||||
|
||||
# 动态 upstream 变量
|
||||
set $admin_frontend kaopeilian-admin-frontend;
|
||||
set $admin_backend kaopeilian-admin-backend;
|
||||
|
||||
# API 路由 - 使用 rewrite 确保正确传递 URI
|
||||
location /api/ {
|
||||
rewrite ^/api/(.*)$ /api/$1 break;
|
||||
proxy_pass http://$admin_backend:8000;
|
||||
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 300s;
|
||||
proxy_connect_timeout 75s;
|
||||
client_max_body_size 50M;
|
||||
}
|
||||
|
||||
# 健康检查
|
||||
location = /health {
|
||||
proxy_pass http://$admin_backend:8000/health;
|
||||
proxy_set_header Host $host;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# 前端静态资源
|
||||
location / {
|
||||
proxy_pass http://$admin_frontend:80;
|
||||
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;
|
||||
|
||||
# HTML 不缓存
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
expires 0;
|
||||
}
|
||||
}
|
||||
102
deploy/nginx/conf.d/ex.conf
Normal file
102
deploy/nginx/conf.d/ex.conf
Normal file
@@ -0,0 +1,102 @@
|
||||
# 恩喜成都总院 (ex.ireborn.com.cn) Nginx配置
|
||||
# 支持 HTTP 和 HTTPS 访问
|
||||
|
||||
# HTTP 重定向到 HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name ex.ireborn.com.cn;
|
||||
|
||||
# Let's Encrypt 验证路径
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
# 其他请求重定向到 HTTPS
|
||||
location / {
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS 配置
|
||||
server {
|
||||
listen 443 ssl;
|
||||
http2 on;
|
||||
server_name ex.ireborn.com.cn;
|
||||
|
||||
# SSL 证书配置
|
||||
ssl_certificate /etc/letsencrypt/live/ex.ireborn.com.cn/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/ex.ireborn.com.cn/privkey.pem;
|
||||
|
||||
# SSL 安全配置
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384;
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
|
||||
# 安全头
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header X-Frame-Options DENY always;
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
# 前端静态资源(带哈希,长期缓存)
|
||||
location /assets/ {
|
||||
proxy_pass http://ex-frontend:80;
|
||||
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;
|
||||
|
||||
# 带哈希的文件可以长期缓存
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
expires 1y;
|
||||
}
|
||||
|
||||
# 前端服务(HTML 不缓存)
|
||||
location / {
|
||||
proxy_pass http://ex-frontend:80;
|
||||
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_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
# HTML 文件不缓存,确保用户获取最新版本
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
expires 0;
|
||||
}
|
||||
|
||||
# 后端API
|
||||
location /api/ {
|
||||
proxy_pass http://ex-backend:8000;
|
||||
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_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
|
||||
# 健康检查
|
||||
location /health {
|
||||
proxy_pass http://ex-backend:8000;
|
||||
proxy_set_header Host $host;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# 静态文件上传
|
||||
location /static/uploads/ {
|
||||
proxy_pass http://ex-backend:8000;
|
||||
proxy_set_header Host $host;
|
||||
expires 1y;
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
}
|
||||
101
deploy/nginx/conf.d/fw.conf
Normal file
101
deploy/nginx/conf.d/fw.conf
Normal file
@@ -0,0 +1,101 @@
|
||||
# 飞沃 (fw.ireborn.com.cn) Nginx配置
|
||||
# 支持 HTTP 和 HTTPS 访问
|
||||
|
||||
# HTTP 重定向到 HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name fw.ireborn.com.cn;
|
||||
|
||||
# Let's Encrypt 验证路径
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
# 其他请求重定向到 HTTPS
|
||||
location / {
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS 配置
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name fw.ireborn.com.cn;
|
||||
|
||||
# SSL 证书配置
|
||||
ssl_certificate /etc/letsencrypt/live/fw.ireborn.com.cn/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/fw.ireborn.com.cn/privkey.pem;
|
||||
|
||||
# SSL 安全配置
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384;
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
|
||||
# 安全头
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header X-Frame-Options DENY always;
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
# 前端静态资源(带哈希,长期缓存)
|
||||
location /assets/ {
|
||||
proxy_pass http://fw-frontend:80;
|
||||
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;
|
||||
|
||||
# 带哈希的文件可以长期缓存
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
expires 1y;
|
||||
}
|
||||
|
||||
# 前端服务(HTML 不缓存)
|
||||
location / {
|
||||
proxy_pass http://fw-frontend:80;
|
||||
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_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
# HTML 文件不缓存,确保用户获取最新版本
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
expires 0;
|
||||
}
|
||||
|
||||
# 后端API
|
||||
location /api/ {
|
||||
proxy_pass http://fw-backend:8000;
|
||||
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_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
|
||||
# 健康检查
|
||||
location /health {
|
||||
proxy_pass http://fw-backend:8000;
|
||||
proxy_set_header Host $host;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# 静态文件上传
|
||||
location /static/uploads/ {
|
||||
proxy_pass http://fw-backend:8000;
|
||||
proxy_set_header Host $host;
|
||||
expires 1y;
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
}
|
||||
101
deploy/nginx/conf.d/hl.conf
Normal file
101
deploy/nginx/conf.d/hl.conf
Normal file
@@ -0,0 +1,101 @@
|
||||
# 武汉禾丽 (hl.ireborn.com.cn) Nginx配置
|
||||
# 支持 HTTP 和 HTTPS 访问
|
||||
|
||||
# HTTP 重定向到 HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name hl.ireborn.com.cn;
|
||||
|
||||
# Let's Encrypt 验证路径
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
# 其他请求重定向到 HTTPS
|
||||
location / {
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS 配置
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name hl.ireborn.com.cn;
|
||||
|
||||
# SSL 证书配置
|
||||
ssl_certificate /etc/letsencrypt/live/hl.ireborn.com.cn/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/hl.ireborn.com.cn/privkey.pem;
|
||||
|
||||
# SSL 安全配置
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384;
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
|
||||
# 安全头
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header X-Frame-Options DENY always;
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
# 前端静态资源(带哈希,长期缓存)
|
||||
location /assets/ {
|
||||
proxy_pass http://hl-frontend:80;
|
||||
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;
|
||||
|
||||
# 带哈希的文件可以长期缓存
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
expires 1y;
|
||||
}
|
||||
|
||||
# 前端服务(HTML 不缓存)
|
||||
location / {
|
||||
proxy_pass http://hl-frontend:80;
|
||||
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_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
# HTML 文件不缓存,确保用户获取最新版本
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
expires 0;
|
||||
}
|
||||
|
||||
# 后端API
|
||||
location /api/ {
|
||||
proxy_pass http://hl-backend:8000;
|
||||
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_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
|
||||
# 健康检查
|
||||
location /health {
|
||||
proxy_pass http://hl-backend:8000;
|
||||
proxy_set_header Host $host;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# 静态文件上传
|
||||
location /static/uploads/ {
|
||||
proxy_pass http://hl-backend:8000;
|
||||
proxy_set_header Host $host;
|
||||
expires 1y;
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
}
|
||||
101
deploy/nginx/conf.d/hua.conf
Normal file
101
deploy/nginx/conf.d/hua.conf
Normal file
@@ -0,0 +1,101 @@
|
||||
# 华尔倍丽 (hua.ireborn.com.cn) Nginx配置
|
||||
# 支持 HTTP 和 HTTPS 访问
|
||||
|
||||
# HTTP 重定向到 HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name hua.ireborn.com.cn;
|
||||
|
||||
# Let's Encrypt 验证路径
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
# 其他请求重定向到 HTTPS
|
||||
location / {
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS 配置
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name hua.ireborn.com.cn;
|
||||
|
||||
# SSL 证书配置
|
||||
ssl_certificate /etc/letsencrypt/live/hua.ireborn.com.cn/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/hua.ireborn.com.cn/privkey.pem;
|
||||
|
||||
# SSL 安全配置
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384;
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
|
||||
# 安全头
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header X-Frame-Options DENY always;
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
# 前端静态资源(带哈希,长期缓存)
|
||||
location /assets/ {
|
||||
proxy_pass http://hua-frontend:80;
|
||||
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;
|
||||
|
||||
# 带哈希的文件可以长期缓存
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
expires 1y;
|
||||
}
|
||||
|
||||
# 前端服务(HTML 不缓存)
|
||||
location / {
|
||||
proxy_pass http://hua-frontend:80;
|
||||
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_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
# HTML 文件不缓存,确保用户获取最新版本
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
expires 0;
|
||||
}
|
||||
|
||||
# 后端API
|
||||
location /api/ {
|
||||
proxy_pass http://hua-backend:8000;
|
||||
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_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
|
||||
# 健康检查
|
||||
location /health {
|
||||
proxy_pass http://hua-backend:8000;
|
||||
proxy_set_header Host $host;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# 静态文件上传
|
||||
location /static/uploads/ {
|
||||
proxy_pass http://hua-backend:8000;
|
||||
proxy_set_header Host $host;
|
||||
expires 1y;
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
}
|
||||
150
deploy/nginx/conf.d/kaopeilian.conf
Normal file
150
deploy/nginx/conf.d/kaopeilian.conf
Normal file
@@ -0,0 +1,150 @@
|
||||
# 考陪练系统 Nginx 配置
|
||||
# 支持 HTTP 和 HTTPS 访问
|
||||
|
||||
# HTTP 重定向到 HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name aiedu.ireborn.com.cn;
|
||||
|
||||
# Let's Encrypt 验证路径
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
# 其他请求重定向到 HTTPS
|
||||
location / {
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS 配置
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name aiedu.ireborn.com.cn;
|
||||
|
||||
# SSL 证书配置
|
||||
ssl_certificate /etc/letsencrypt/live/aiedu.ireborn.com.cn/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/aiedu.ireborn.com.cn/privkey.pem;
|
||||
|
||||
# SSL 安全配置
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384;
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
|
||||
# 安全头
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header X-Frame-Options DENY always;
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
# 前端静态资源(带哈希,长期缓存)
|
||||
location /assets/ {
|
||||
proxy_pass http://kaopeilian-frontend:80;
|
||||
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;
|
||||
|
||||
# 带哈希的文件可以长期缓存
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
expires 1y;
|
||||
}
|
||||
|
||||
# 前端生产服务器(HTML 不缓存)
|
||||
location / {
|
||||
proxy_pass http://kaopeilian-frontend:80;
|
||||
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_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
# HTML 文件不缓存,确保用户获取最新版本
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
expires 0;
|
||||
}
|
||||
|
||||
# 修复前端localhost:8000请求的特殊处理
|
||||
location ~* ^/localhost:8000/(.*)$ {
|
||||
rewrite ^/localhost:8000/(.*)$ /$1 break;
|
||||
proxy_pass http://kaopeilian-backend:8000;
|
||||
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_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
|
||||
# 后端生产服务器 API
|
||||
location /api/ {
|
||||
proxy_pass http://kaopeilian-backend:8000;
|
||||
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";
|
||||
|
||||
# 超时配置 - 增加到10分钟以支持AI试题生成等长时间处理
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
|
||||
# 后端健康检查
|
||||
location /health {
|
||||
proxy_pass http://kaopeilian-backend:8000;
|
||||
proxy_set_header Host $host;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# 静态文件上传
|
||||
location /static/uploads/ {
|
||||
proxy_pass http://kaopeilian-backend:8000;
|
||||
proxy_set_header Host $host;
|
||||
expires 1y;
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
|
||||
# GitHub Webhook处理
|
||||
location /webhook {
|
||||
proxy_pass http://172.18.0.1:9000;
|
||||
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;
|
||||
|
||||
# Webhook专用配置
|
||||
proxy_read_timeout 60s;
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
}
|
||||
|
||||
# Webhook状态检查
|
||||
location /webhook/health {
|
||||
proxy_pass http://172.18.0.1:9000/health;
|
||||
proxy_set_header Host $host;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
location /webhook/status {
|
||||
proxy_pass http://172.18.0.1:9000/status;
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
118
deploy/nginx/conf.d/kpl.conf
Normal file
118
deploy/nginx/conf.d/kpl.conf
Normal file
@@ -0,0 +1,118 @@
|
||||
# 瑞小美团队开发环境 Nginx 配置
|
||||
# 域名:kpl.ireborn.com.cn
|
||||
# 支持 HTTP 和 HTTPS 访问,热重载
|
||||
|
||||
# HTTP 重定向到 HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name kpl.ireborn.com.cn;
|
||||
|
||||
# Let's Encrypt 验证路径
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
# 其他请求重定向到 HTTPS
|
||||
location / {
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS 配置
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name kpl.ireborn.com.cn;
|
||||
|
||||
# SSL 证书配置
|
||||
ssl_certificate /etc/letsencrypt/live/kpl.ireborn.com.cn/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/kpl.ireborn.com.cn/privkey.pem;
|
||||
|
||||
# SSL 安全配置
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384;
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
|
||||
# 安全头
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header X-Frame-Options DENY always;
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
# 前端服务(共享 dist 方案)
|
||||
location / {
|
||||
proxy_pass http://kpl-frontend-dev:80;
|
||||
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_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
# HTML 不缓存
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
expires 0;
|
||||
}
|
||||
|
||||
# JS/CSS 静态资源(带 hash 可长期缓存)
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
proxy_pass http://kpl-frontend-dev:80;
|
||||
proxy_set_header Host $host;
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
expires 1y;
|
||||
}
|
||||
|
||||
# 修复前端localhost:8000请求的特殊处理
|
||||
location ~* ^/localhost:8000/(.*)$ {
|
||||
rewrite ^/localhost:8000/(.*)$ /$1 break;
|
||||
proxy_pass http://kpl-backend-dev:8000;
|
||||
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_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
|
||||
# 后端开发服务器 API
|
||||
location /api/ {
|
||||
proxy_pass http://kpl-backend-dev:8000;
|
||||
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";
|
||||
|
||||
# 超时配置 - 增加到10分钟以支持AI试题生成等长时间处理
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
|
||||
# 后端健康检查
|
||||
location /health {
|
||||
proxy_pass http://kpl-backend-dev:8000;
|
||||
proxy_set_header Host $host;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# 静态文件上传
|
||||
location /static/uploads/ {
|
||||
proxy_pass http://kpl-backend-dev:8000;
|
||||
proxy_set_header Host $host;
|
||||
expires 1y;
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
}
|
||||
|
||||
73
deploy/nginx/conf.d/pl.conf
Normal file
73
deploy/nginx/conf.d/pl.conf
Normal file
@@ -0,0 +1,73 @@
|
||||
# 陪练试用版 Nginx配置
|
||||
# 域名: pl.ireborn.com.cn
|
||||
|
||||
upstream pl_backend {
|
||||
server 172.18.0.1:8020;
|
||||
}
|
||||
|
||||
upstream pl_frontend {
|
||||
server 172.18.0.1:3020;
|
||||
}
|
||||
|
||||
# HTTP -> HTTPS 重定向
|
||||
server {
|
||||
listen 80;
|
||||
server_name pl.ireborn.com.cn;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS 服务
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name pl.ireborn.com.cn;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/pl.ireborn.com.cn/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/pl.ireborn.com.cn/privkey.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
|
||||
access_log /var/log/nginx/pl_access.log;
|
||||
error_log /var/log/nginx/pl_error.log;
|
||||
|
||||
location / {
|
||||
proxy_pass http://pl_frontend;
|
||||
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;
|
||||
|
||||
# 禁用缓存
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
add_header Expires "0";
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_pass http://pl_backend;
|
||||
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_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
|
||||
location /@vite/ {
|
||||
proxy_pass http://pl_frontend;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
}
|
||||
99
deploy/nginx/conf.d/pl.conf.disabled
Normal file
99
deploy/nginx/conf.d/pl.conf.disabled
Normal file
@@ -0,0 +1,99 @@
|
||||
# 陪练试用版 Nginx配置
|
||||
# 域名: pl.ireborn.com.cn
|
||||
# 后端: peilian-backend:8000 (通过外部网络访问 host.docker.internal:8020)
|
||||
# 前端: peilian-frontend:3000 (通过外部网络访问 host.docker.internal:3020)
|
||||
|
||||
upstream pl_backend {
|
||||
server host.docker.internal:8020;
|
||||
}
|
||||
|
||||
upstream pl_frontend {
|
||||
server host.docker.internal:3020;
|
||||
}
|
||||
|
||||
# HTTP -> HTTPS 重定向
|
||||
server {
|
||||
listen 80;
|
||||
server_name pl.ireborn.com.cn;
|
||||
|
||||
# Let's Encrypt 验证路径
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS 服务
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name pl.ireborn.com.cn;
|
||||
|
||||
# SSL证书
|
||||
ssl_certificate /etc/letsencrypt/live/pl.ireborn.com.cn/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/pl.ireborn.com.cn/privkey.pem;
|
||||
|
||||
# SSL配置
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:SSL:50m;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
|
||||
ssl_prefer_server_ciphers off;
|
||||
|
||||
# 安全头
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
# 日志
|
||||
access_log /var/log/nginx/pl_access.log;
|
||||
error_log /var/log/nginx/pl_error.log;
|
||||
|
||||
# 前端页面
|
||||
location / {
|
||||
proxy_pass http://pl_frontend;
|
||||
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;
|
||||
}
|
||||
|
||||
# API代理
|
||||
location /api/ {
|
||||
proxy_pass http://pl_backend;
|
||||
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_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
|
||||
# WebSocket代理(用于HMR)
|
||||
location /_hmr {
|
||||
proxy_pass http://pl_frontend;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
|
||||
# Vite HMR WebSocket
|
||||
location /@vite/ {
|
||||
proxy_pass http://pl_frontend;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
}
|
||||
}
|
||||
96
deploy/nginx/conf.d/xy.conf
Normal file
96
deploy/nginx/conf.d/xy.conf
Normal file
@@ -0,0 +1,96 @@
|
||||
# 芯颜定制 (xy.ireborn.com.cn) Nginx配置
|
||||
# 支持 HTTP 和 HTTPS 访问
|
||||
|
||||
# HTTP 重定向到 HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name xy.ireborn.com.cn;
|
||||
|
||||
# Let's Encrypt 验证路径
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
# 其他请求重定向到 HTTPS
|
||||
location / {
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS 配置
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name xy.ireborn.com.cn;
|
||||
|
||||
# SSL 证书配置
|
||||
ssl_certificate /etc/letsencrypt/live/xy.ireborn.com.cn/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/xy.ireborn.com.cn/privkey.pem;
|
||||
|
||||
# SSL 安全配置
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384;
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
|
||||
# 安全头
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header X-Frame-Options DENY always;
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
# 前端服务 - HTML文件不缓存
|
||||
location / {
|
||||
proxy_pass http://xy-frontend:80;
|
||||
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_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
# HTML文件不缓存,确保获取最新版本
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
expires 0;
|
||||
}
|
||||
|
||||
# JS/CSS等静态资源(带hash的文件可长期缓存)
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
proxy_pass http://xy-frontend:80;
|
||||
proxy_set_header Host $host;
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
expires 1y;
|
||||
}
|
||||
|
||||
# 后端API
|
||||
location /api/ {
|
||||
proxy_pass http://xy-backend:8000;
|
||||
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_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
|
||||
# 健康检查
|
||||
location /health {
|
||||
proxy_pass http://xy-backend:8000;
|
||||
proxy_set_header Host $host;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# 静态文件上传
|
||||
location /static/uploads/ {
|
||||
proxy_pass http://xy-backend:8000;
|
||||
proxy_set_header Host $host;
|
||||
expires 1y;
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
}
|
||||
101
deploy/nginx/conf.d/yy.conf
Normal file
101
deploy/nginx/conf.d/yy.conf
Normal file
@@ -0,0 +1,101 @@
|
||||
# 杨扬宠物 (yy.ireborn.com.cn) Nginx配置
|
||||
# 支持 HTTP 和 HTTPS 访问
|
||||
|
||||
# HTTP 重定向到 HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name yy.ireborn.com.cn;
|
||||
|
||||
# Let's Encrypt 验证路径
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
# 其他请求重定向到 HTTPS
|
||||
location / {
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS 配置
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name yy.ireborn.com.cn;
|
||||
|
||||
# SSL 证书配置
|
||||
ssl_certificate /etc/letsencrypt/live/yy.ireborn.com.cn/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/yy.ireborn.com.cn/privkey.pem;
|
||||
|
||||
# SSL 安全配置
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384;
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
|
||||
# 安全头
|
||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||
add_header X-Frame-Options DENY always;
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
# 前端静态资源(带哈希,长期缓存)
|
||||
location /assets/ {
|
||||
proxy_pass http://yy-frontend:80;
|
||||
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;
|
||||
|
||||
# 带哈希的文件可以长期缓存
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
expires 1y;
|
||||
}
|
||||
|
||||
# 前端服务(HTML 不缓存)
|
||||
location / {
|
||||
proxy_pass http://yy-frontend:80;
|
||||
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_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
# HTML 文件不缓存,确保用户获取最新版本
|
||||
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
add_header Pragma "no-cache";
|
||||
expires 0;
|
||||
}
|
||||
|
||||
# 后端API
|
||||
location /api/ {
|
||||
proxy_pass http://yy-backend:8000;
|
||||
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_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_connect_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
}
|
||||
|
||||
# 健康检查
|
||||
location /health {
|
||||
proxy_pass http://yy-backend:8000;
|
||||
proxy_set_header Host $host;
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# 静态文件上传
|
||||
location /static/uploads/ {
|
||||
proxy_pass http://yy-backend:8000;
|
||||
proxy_set_header Host $host;
|
||||
expires 1y;
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
}
|
||||
53
deploy/nginx/nginx.conf
Normal file
53
deploy/nginx/nginx.conf
Normal file
@@ -0,0 +1,53 @@
|
||||
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;
|
||||
|
||||
# 日志格式
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
# 基础配置
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 65;
|
||||
types_hash_max_size 2048;
|
||||
client_max_body_size 50M;
|
||||
|
||||
# Gzip压缩
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_types
|
||||
text/plain
|
||||
text/css
|
||||
text/xml
|
||||
text/javascript
|
||||
application/json
|
||||
application/javascript
|
||||
application/xml+rss
|
||||
application/atom+xml
|
||||
image/svg+xml;
|
||||
|
||||
# 包含站点配置
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
}
|
||||
|
||||
|
||||
|
||||
152
deploy/scripts/auto_update.sh
Executable file
152
deploy/scripts/auto_update.sh
Executable file
@@ -0,0 +1,152 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 考培练系统自动更新脚本
|
||||
# 作者: AI Assistant
|
||||
# 日期: 2025-09-25
|
||||
|
||||
set -e
|
||||
|
||||
# 配置变量
|
||||
PROJECT_DIR="/root/aiedu"
|
||||
LOG_FILE="/var/log/kaopeilian_update.log"
|
||||
BACKUP_DIR="/root/aiedu/backups/updates"
|
||||
|
||||
# 日志函数
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
# 创建备份目录
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
log "=== 开始检查代码更新 ==="
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# 获取当前提交哈希
|
||||
LOCAL_COMMIT=$(git rev-parse HEAD)
|
||||
log "本地提交: $LOCAL_COMMIT"
|
||||
|
||||
# 拉取最新代码
|
||||
git fetch origin production
|
||||
|
||||
# 获取远程提交哈希
|
||||
REMOTE_COMMIT=$(git rev-parse origin/production)
|
||||
log "远程提交: $REMOTE_COMMIT"
|
||||
|
||||
# 检查是否有更新
|
||||
if [ "$LOCAL_COMMIT" = "$REMOTE_COMMIT" ]; then
|
||||
log "代码已是最新版本,无需更新"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "发现代码更新,开始部署流程..."
|
||||
|
||||
# 创建备份
|
||||
BACKUP_NAME="backup_$(date '+%Y%m%d_%H%M%S')"
|
||||
log "创建备份: $BACKUP_NAME"
|
||||
|
||||
# 备份当前代码
|
||||
git stash push -m "Auto backup before update $BACKUP_NAME"
|
||||
|
||||
# 备份数据库(如果MySQL容器在运行)
|
||||
if docker ps | grep -q kaopeilian-mysql; then
|
||||
docker exec kaopeilian-mysql mysqldump -u root -p'Kaopeilian2025!@#' kaopeilian > "$BACKUP_DIR/${BACKUP_NAME}_database.sql"
|
||||
log "数据库备份完成"
|
||||
fi
|
||||
|
||||
# 拉取最新代码
|
||||
log "拉取最新代码..."
|
||||
git pull origin production
|
||||
|
||||
# 检查是否需要重新构建
|
||||
NEED_REBUILD=false
|
||||
|
||||
# 检查Docker文件变化
|
||||
if git diff --name-only "$LOCAL_COMMIT" "$REMOTE_COMMIT" | grep -E "(Dockerfile|docker-compose\.yml|requirements\.txt|package\.json)"; then
|
||||
NEED_REBUILD=true
|
||||
log "检测到构建文件变化,需要重新构建镜像"
|
||||
fi
|
||||
|
||||
# 检查前端文件变化并构建
|
||||
if git diff --name-only "$LOCAL_COMMIT" "$REMOTE_COMMIT" | grep -E "kaopeilian-frontend/(src/|package\.json|vite\.config\.ts)"; then
|
||||
log "检测到前端代码变化,开始构建前端..."
|
||||
|
||||
cd "$PROJECT_DIR/kaopeilian-frontend"
|
||||
|
||||
# 确保依赖已安装
|
||||
if [ ! -d "node_modules" ]; then
|
||||
log "安装前端依赖..."
|
||||
npm install
|
||||
fi
|
||||
|
||||
# 构建前端
|
||||
log "构建前端应用..."
|
||||
npm run build
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log "前端构建成功"
|
||||
NEED_REBUILD=true
|
||||
else
|
||||
log "⚠️ 前端构建失败,但继续部署流程"
|
||||
fi
|
||||
|
||||
cd "$PROJECT_DIR"
|
||||
fi
|
||||
|
||||
# 停止服务
|
||||
log "停止当前服务..."
|
||||
docker compose down
|
||||
|
||||
# 重新构建(如果需要)
|
||||
if [ "$NEED_REBUILD" = true ]; then
|
||||
log "重新构建Docker镜像..."
|
||||
docker compose build --no-cache
|
||||
else
|
||||
log "使用现有镜像启动服务..."
|
||||
fi
|
||||
|
||||
# 启动服务
|
||||
log "启动更新后的服务..."
|
||||
docker compose up -d
|
||||
|
||||
# 等待服务启动
|
||||
sleep 60
|
||||
|
||||
# 健康检查
|
||||
log "执行健康检查..."
|
||||
HEALTH_CHECK_URL="https://aiedu.ireborn.com.cn/health"
|
||||
|
||||
# 尝试多次健康检查
|
||||
RETRY_COUNT=0
|
||||
MAX_RETRIES=5
|
||||
|
||||
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
|
||||
if curl -f -s -k "$HEALTH_CHECK_URL" > /dev/null; then
|
||||
log "✅ 健康检查通过,更新成功完成"
|
||||
|
||||
# 清理旧的备份(保留最近5个)
|
||||
cd "$BACKUP_DIR"
|
||||
ls -t backup_*_database.sql 2>/dev/null | tail -n +6 | xargs rm -f 2>/dev/null || true
|
||||
|
||||
log "=== 自动更新完成 ==="
|
||||
exit 0
|
||||
else
|
||||
RETRY_COUNT=$((RETRY_COUNT + 1))
|
||||
log "健康检查失败,重试 $RETRY_COUNT/$MAX_RETRIES"
|
||||
sleep 10
|
||||
fi
|
||||
done
|
||||
|
||||
log "❌ 健康检查失败,开始回滚..."
|
||||
|
||||
# 回滚代码
|
||||
cd "$PROJECT_DIR"
|
||||
git reset --hard "$LOCAL_COMMIT"
|
||||
|
||||
# 重新启动服务
|
||||
docker compose down
|
||||
docker compose up -d
|
||||
|
||||
log "回滚完成,请检查服务状态"
|
||||
exit 1
|
||||
223
deploy/scripts/check-config.sh
Executable file
223
deploy/scripts/check-config.sh
Executable file
@@ -0,0 +1,223 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 配置一致性检查脚本
|
||||
# 用于快速验证系统各组件配置是否一致
|
||||
|
||||
echo "🔧 配置一致性检查脚本"
|
||||
echo "========================"
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 检查结果统计
|
||||
PASS_COUNT=0
|
||||
FAIL_COUNT=0
|
||||
|
||||
# 检查函数
|
||||
check_service() {
|
||||
local service_name=$1
|
||||
local url=$2
|
||||
local expected_response=$3
|
||||
|
||||
echo -n "检查 $service_name ... "
|
||||
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
response=$(curl -s --connect-timeout 5 "$url" 2>/dev/null)
|
||||
if [[ $? -eq 0 && "$response" == *"$expected_response"* ]]; then
|
||||
echo -e "${GREEN}✅ 通过${NC}"
|
||||
((PASS_COUNT++))
|
||||
else
|
||||
echo -e "${RED}❌ 失败${NC}"
|
||||
echo " 期望包含: $expected_response"
|
||||
echo " 实际响应: $response"
|
||||
((FAIL_COUNT++))
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ 跳过 (curl 未安装)${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查Docker服务
|
||||
check_docker_service() {
|
||||
local service_name=$1
|
||||
echo -n "检查 Docker 服务 $service_name ... "
|
||||
|
||||
if command -v docker-compose >/dev/null 2>&1; then
|
||||
if docker-compose ps | grep -q "$service_name.*Up"; then
|
||||
echo -e "${GREEN}✅ 运行中${NC}"
|
||||
((PASS_COUNT++))
|
||||
else
|
||||
echo -e "${RED}❌ 未运行${NC}"
|
||||
((FAIL_COUNT++))
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ 跳过 (docker-compose 未安装)${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查配置文件
|
||||
check_config_file() {
|
||||
local file_path=$1
|
||||
local description=$2
|
||||
|
||||
echo -n "检查 $description ... "
|
||||
|
||||
if [[ -f "$file_path" ]]; then
|
||||
echo -e "${GREEN}✅ 存在${NC}"
|
||||
((PASS_COUNT++))
|
||||
else
|
||||
echo -e "${RED}❌ 缺失${NC}"
|
||||
echo " 文件路径: $file_path"
|
||||
((FAIL_COUNT++))
|
||||
fi
|
||||
}
|
||||
|
||||
# 检查端口占用
|
||||
check_port() {
|
||||
local port=$1
|
||||
local service_name=$2
|
||||
|
||||
echo -n "检查端口 $port ($service_name) ... "
|
||||
|
||||
if command -v lsof >/dev/null 2>&1; then
|
||||
if lsof -i :$port >/dev/null 2>&1; then
|
||||
echo -e "${GREEN}✅ 已占用${NC}"
|
||||
((PASS_COUNT++))
|
||||
else
|
||||
echo -e "${RED}❌ 未占用${NC}"
|
||||
((FAIL_COUNT++))
|
||||
fi
|
||||
elif command -v netstat >/dev/null 2>&1; then
|
||||
if netstat -an | grep -q ":$port "; then
|
||||
echo -e "${GREEN}✅ 已占用${NC}"
|
||||
((PASS_COUNT++))
|
||||
else
|
||||
echo -e "${RED}❌ 未占用${NC}"
|
||||
((FAIL_COUNT++))
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ 跳过 (lsof/netstat 未安装)${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
echo "1. 检查基础服务状态"
|
||||
echo "-------------------"
|
||||
|
||||
# 检查Docker服务
|
||||
check_docker_service "mysql"
|
||||
check_docker_service "redis"
|
||||
|
||||
# 检查端口占用
|
||||
check_port 3306 "MySQL"
|
||||
check_port 6379 "Redis"
|
||||
check_port 8000 "后端API"
|
||||
check_port 3001 "前端开发服务器"
|
||||
|
||||
echo ""
|
||||
echo "2. 检查服务健康状态"
|
||||
echo "-------------------"
|
||||
|
||||
# 检查后端健康状态
|
||||
check_service "后端API健康检查" "http://localhost:8000/health" "healthy"
|
||||
|
||||
# 检查前端服务
|
||||
check_service "前端服务" "http://localhost:3001" "考培练系统"
|
||||
|
||||
echo ""
|
||||
echo "3. 检查配置文件"
|
||||
echo "---------------"
|
||||
|
||||
# 检查关键配置文件
|
||||
check_config_file "kaopeilian-backend/app/config/settings.py" "后端配置文件"
|
||||
check_config_file "kaopeilian-frontend/src/api/config.ts" "前端API配置"
|
||||
check_config_file "docker-compose.yml" "Docker配置文件"
|
||||
check_config_file "配置一致性检查清单.md" "配置检查清单"
|
||||
|
||||
echo ""
|
||||
echo "4. 检查认证功能"
|
||||
echo "---------------"
|
||||
|
||||
# 检查登录API
|
||||
echo -n "检查登录API ... "
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
login_response=$(curl -s -X POST http://localhost:8000/api/v1/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username": "testuser", "password": "TestPass123!"}' 2>/dev/null)
|
||||
|
||||
if [[ "$login_response" == *"access_token"* ]]; then
|
||||
echo -e "${GREEN}✅ 正常工作${NC}"
|
||||
((PASS_COUNT++))
|
||||
|
||||
# 提取token测试认证
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
token=$(echo "$login_response" | jq -r '.data.access_token' 2>/dev/null)
|
||||
if [[ "$token" != "null" && "$token" != "" ]]; then
|
||||
echo -n "检查token认证 ... "
|
||||
auth_response=$(curl -s -H "Authorization: Bearer $token" \
|
||||
http://localhost:8000/api/v1/auth/me 2>/dev/null)
|
||||
|
||||
if [[ "$auth_response" == *"testuser"* ]]; then
|
||||
echo -e "${GREEN}✅ 正常工作${NC}"
|
||||
((PASS_COUNT++))
|
||||
else
|
||||
echo -e "${RED}❌ 失败${NC}"
|
||||
((FAIL_COUNT++))
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo " (跳过token测试 - jq未安装)"
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}❌ 失败${NC}"
|
||||
echo " 响应: $login_response"
|
||||
((FAIL_COUNT++))
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ 跳过 (curl 未安装)${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "5. 检查CORS配置"
|
||||
echo "---------------"
|
||||
|
||||
echo -n "检查CORS预检请求 ... "
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
cors_response=$(curl -s -X OPTIONS http://localhost:8000/api/v1/auth/login \
|
||||
-H "Origin: http://localhost:3001" \
|
||||
-H "Access-Control-Request-Method: POST" \
|
||||
-H "Access-Control-Request-Headers: Content-Type" 2>/dev/null)
|
||||
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo -e "${GREEN}✅ 正常响应${NC}"
|
||||
((PASS_COUNT++))
|
||||
else
|
||||
echo -e "${RED}❌ 失败${NC}"
|
||||
((FAIL_COUNT++))
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ 跳过 (curl 未安装)${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "========================"
|
||||
echo "📊 检查结果统计"
|
||||
echo "------------------------"
|
||||
echo -e "通过: ${GREEN}$PASS_COUNT${NC}"
|
||||
echo -e "失败: ${RED}$FAIL_COUNT${NC}"
|
||||
|
||||
if [[ $FAIL_COUNT -eq 0 ]]; then
|
||||
echo -e "\n🎉 ${GREEN}所有检查都通过了!系统配置正常。${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "\n⚠️ ${YELLOW}发现 $FAIL_COUNT 个问题,请检查配置。${NC}"
|
||||
echo ""
|
||||
echo "💡 解决建议:"
|
||||
echo "1. 确保Docker服务已启动: docker-compose up -d"
|
||||
echo "2. 检查端口是否被占用或服务未启动"
|
||||
echo "3. 参考 '配置一致性检查清单.md' 核对配置"
|
||||
echo "4. 确保数据库密码、CORS域名等配置一致"
|
||||
exit 1
|
||||
fi
|
||||
91
deploy/scripts/check_environment.sh
Normal file
91
deploy/scripts/check_environment.sh
Normal file
@@ -0,0 +1,91 @@
|
||||
#!/bin/bash
|
||||
# 环境状态检查脚本
|
||||
|
||||
echo "=== 考培练系统环境状态检查 ==="
|
||||
echo "检查时间: $(date)"
|
||||
echo ""
|
||||
|
||||
# 检查前端环境
|
||||
echo "🌐 前端环境检查:"
|
||||
if curl -s -f http://localhost:3001 > /dev/null; then
|
||||
echo "✅ 前端服务运行正常 (http://localhost:3001)"
|
||||
|
||||
# 尝试获取环境信息
|
||||
if command -v jq &> /dev/null; then
|
||||
echo "📊 前端环境信息:"
|
||||
curl -s http://localhost:3001/api/env 2>/dev/null | jq . || echo "无法获取环境信息"
|
||||
fi
|
||||
else
|
||||
echo "❌ 前端服务不可访问"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 检查后端环境
|
||||
echo "🚀 后端环境检查:"
|
||||
if curl -s -f http://localhost:8000/health > /dev/null; then
|
||||
echo "✅ 后端服务运行正常 (http://localhost:8000)"
|
||||
|
||||
# 获取健康检查信息
|
||||
if command -v jq &> /dev/null; then
|
||||
echo "📊 后端环境信息:"
|
||||
curl -s http://localhost:8000/health | jq .
|
||||
else
|
||||
echo "健康检查响应:"
|
||||
curl -s http://localhost:8000/health
|
||||
fi
|
||||
else
|
||||
echo "❌ 后端服务不可访问"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 检查数据库连接
|
||||
echo "🗄️ 数据库连接检查:"
|
||||
if curl -s -f http://localhost:8000/health/db > /dev/null 2>&1; then
|
||||
echo "✅ 数据库连接正常"
|
||||
if command -v jq &> /dev/null; then
|
||||
curl -s http://localhost:8000/health/db | jq .
|
||||
fi
|
||||
else
|
||||
echo "❌ 数据库连接异常"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 检查Redis连接
|
||||
echo "🔴 Redis连接检查:"
|
||||
if curl -s -f http://localhost:8000/health/redis > /dev/null 2>&1; then
|
||||
echo "✅ Redis连接正常"
|
||||
if command -v jq &> /dev/null; then
|
||||
curl -s http://localhost:8000/health/redis | jq .
|
||||
fi
|
||||
else
|
||||
echo "❌ Redis连接异常"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 检查Docker容器状态
|
||||
echo "🐳 Docker容器状态:"
|
||||
if command -v docker &> /dev/null; then
|
||||
echo "开发环境容器:"
|
||||
docker-compose -f docker-compose.dev.yml ps 2>/dev/null || echo "无法获取开发环境容器状态"
|
||||
echo ""
|
||||
echo "生产环境容器:"
|
||||
docker-compose ps 2>/dev/null || echo "无法获取生产环境容器状态"
|
||||
else
|
||||
echo "Docker未安装或不可访问"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# 检查端口占用
|
||||
echo "🔌 端口占用检查:"
|
||||
ports=(3001 8000 3306 6379)
|
||||
for port in "${ports[@]}"; do
|
||||
if lsof -i :$port > /dev/null 2>&1; then
|
||||
echo "✅ 端口 $port 已占用"
|
||||
lsof -i :$port | head -2 | tail -1 | awk '{print " 进程:", $2, "命令:", $1}'
|
||||
else
|
||||
echo "❌ 端口 $port 未占用"
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
|
||||
echo "=== 环境检查完成 ==="
|
||||
62
deploy/scripts/cleanup_docker.sh
Executable file
62
deploy/scripts/cleanup_docker.sh
Executable file
@@ -0,0 +1,62 @@
|
||||
#!/bin/bash
|
||||
# Docker 清理脚本
|
||||
# 用于清理未使用的Docker资源
|
||||
|
||||
echo "🧹 开始清理Docker资源..."
|
||||
|
||||
# 1. 清理停止的容器
|
||||
echo "📋 清理停止的容器..."
|
||||
stopped_containers=$(docker ps -a --filter "status=exited" -q)
|
||||
if [ ! -z "$stopped_containers" ]; then
|
||||
echo "发现停止的容器: $stopped_containers"
|
||||
docker rm $stopped_containers
|
||||
echo "✅ 已清理停止的容器"
|
||||
else
|
||||
echo "✅ 没有停止的容器需要清理"
|
||||
fi
|
||||
|
||||
# 2. 清理悬空镜像
|
||||
echo "📋 清理悬空镜像..."
|
||||
dangling_images=$(docker images --filter "dangling=true" -q)
|
||||
if [ ! -z "$dangling_images" ]; then
|
||||
echo "发现悬空镜像: $dangling_images"
|
||||
docker rmi $dangling_images
|
||||
echo "✅ 已清理悬空镜像"
|
||||
else
|
||||
echo "✅ 没有悬空镜像需要清理"
|
||||
fi
|
||||
|
||||
# 3. 清理未使用的卷
|
||||
echo "📋 清理未使用的卷..."
|
||||
unused_volumes=$(docker volume ls --filter "dangling=true" -q)
|
||||
if [ ! -z "$unused_volumes" ]; then
|
||||
echo "发现未使用的卷: $unused_volumes"
|
||||
docker volume rm $unused_volumes
|
||||
echo "✅ 已清理未使用的卷"
|
||||
else
|
||||
echo "✅ 没有未使用的卷需要清理"
|
||||
fi
|
||||
|
||||
# 4. 清理未使用的网络
|
||||
echo "📋 清理未使用的网络..."
|
||||
unused_networks=$(docker network ls --filter "dangling=true" -q)
|
||||
if [ ! -z "$unused_networks" ]; then
|
||||
echo "发现未使用的网络: $unused_networks"
|
||||
docker network rm $unused_networks
|
||||
echo "✅ 已清理未使用的网络"
|
||||
else
|
||||
echo "✅ 没有未使用的网络需要清理"
|
||||
fi
|
||||
|
||||
# 5. 显示当前状态
|
||||
echo "📊 当前Docker状态:"
|
||||
echo "运行中的容器:"
|
||||
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"
|
||||
|
||||
echo -e "\nDocker卷:"
|
||||
docker volume ls
|
||||
|
||||
echo -e "\nDocker网络:"
|
||||
docker network ls
|
||||
|
||||
echo "🎉 Docker清理完成!"
|
||||
113
deploy/scripts/deploy.sh
Executable file
113
deploy/scripts/deploy.sh
Executable file
@@ -0,0 +1,113 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 考陪练系统部署脚本
|
||||
# 用于部署前端、后端服务并申请SSL证书
|
||||
|
||||
set -e
|
||||
|
||||
echo "=== 考陪练系统部署开始 ==="
|
||||
|
||||
# 检查Docker是否运行
|
||||
if ! docker info > /dev/null 2>&1; then
|
||||
echo "❌ Docker未运行,请先启动Docker服务"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查Docker Compose是否安装
|
||||
if ! command -v docker-compose > /dev/null 2>&1; then
|
||||
echo "❌ Docker Compose未安装"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Docker环境检查通过"
|
||||
|
||||
# 创建必要的目录
|
||||
echo "📁 创建必要的目录..."
|
||||
mkdir -p /root/aiedu/kaopeilian-backend/logs
|
||||
mkdir -p /root/aiedu/kaopeilian-backend/uploads
|
||||
mkdir -p /root/aiedu/nginx/conf.d
|
||||
|
||||
# 停止现有容器
|
||||
echo "🛑 停止现有容器..."
|
||||
docker-compose down || true
|
||||
|
||||
# 构建镜像
|
||||
echo "🔨 构建Docker镜像..."
|
||||
docker-compose build --no-cache
|
||||
|
||||
# 启动服务(除了nginx,因为还没有SSL证书)
|
||||
echo "🚀 启动后端和前端服务..."
|
||||
docker-compose up -d kaopeilian-redis kaopeilian-backend kaopeilian-frontend
|
||||
|
||||
# 等待服务启动
|
||||
echo "⏳ 等待服务启动..."
|
||||
sleep 30
|
||||
|
||||
# 检查服务健康状态
|
||||
echo "🔍 检查服务健康状态..."
|
||||
if curl -f http://localhost:8000/health > /dev/null 2>&1; then
|
||||
echo "✅ 后端服务健康检查通过"
|
||||
else
|
||||
echo "❌ 后端服务健康检查失败"
|
||||
docker-compose logs kaopeilian-backend
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if curl -f http://localhost:3001/ > /dev/null 2>&1; then
|
||||
echo "✅ 前端服务健康检查通过"
|
||||
else
|
||||
echo "❌ 前端服务健康检查失败"
|
||||
docker-compose logs kaopeilian-frontend
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "=== 基础服务部署完成 ==="
|
||||
echo "前端访问地址: http://localhost:3001"
|
||||
echo "后端API地址: http://localhost:8000"
|
||||
echo ""
|
||||
echo "下一步将申请SSL证书..."
|
||||
|
||||
# 申请SSL证书
|
||||
echo "🔐 申请SSL证书..."
|
||||
if command -v certbot > /dev/null 2>&1; then
|
||||
# 停止nginx容器(如果运行)
|
||||
docker-compose stop kaopeilian-nginx || true
|
||||
|
||||
# 使用standalone模式申请证书
|
||||
certbot certonly \
|
||||
--standalone \
|
||||
--non-interactive \
|
||||
--agree-tos \
|
||||
--email admin@ireborn.com.cn \
|
||||
--domains aiedu.ireborn.com.cn \
|
||||
--pre-hook "docker-compose stop kaopeilian-nginx" \
|
||||
--post-hook "docker-compose up -d kaopeilian-nginx"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ SSL证书申请成功"
|
||||
|
||||
# 启动nginx服务
|
||||
echo "🚀 启动Nginx反向代理服务..."
|
||||
docker-compose up -d kaopeilian-nginx
|
||||
|
||||
echo "=== 部署完成 ==="
|
||||
echo "HTTPS访问地址: https://aiedu.ireborn.com.cn"
|
||||
echo "前端访问地址: https://aiedu.ireborn.com.cn"
|
||||
echo "后端API地址: https://aiedu.ireborn.com.cn/api"
|
||||
else
|
||||
echo "❌ SSL证书申请失败,请检查域名配置"
|
||||
echo "HTTP访问地址: http://aiedu.ireborn.com.cn"
|
||||
fi
|
||||
else
|
||||
echo "⚠️ certbot未安装,跳过SSL证书申请"
|
||||
echo "请手动安装certbot并申请SSL证书"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== 部署完成 ==="
|
||||
echo "查看服务状态: docker-compose ps"
|
||||
echo "查看日志: docker-compose logs [服务名]"
|
||||
echo "停止服务: docker-compose down"
|
||||
|
||||
|
||||
|
||||
34
deploy/scripts/diagnose.sh
Normal file
34
deploy/scripts/diagnose.sh
Normal file
@@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
# 系统诊断脚本
|
||||
|
||||
echo "=== 系统诊断开始 ==="
|
||||
echo "时间: $(date)"
|
||||
echo ""
|
||||
|
||||
echo "=== Docker服务状态 ==="
|
||||
systemctl is-active docker || echo "Docker服务未运行"
|
||||
echo ""
|
||||
|
||||
echo "=== Docker版本 ==="
|
||||
docker --version 2>/dev/null || echo "Docker命令不可用"
|
||||
echo ""
|
||||
|
||||
echo "=== 容器状态 ==="
|
||||
docker ps -a 2>/dev/null || echo "无法获取容器状态"
|
||||
echo ""
|
||||
|
||||
echo "=== 网络连接测试 ==="
|
||||
curl -I --connect-timeout 3 http://localhost 2>/dev/null || echo "本地80端口不可访问"
|
||||
curl -I --connect-timeout 3 https://aiedu.ireborn.com.cn 2>/dev/null || echo "HTTPS不可访问"
|
||||
echo ""
|
||||
|
||||
echo "=== 端口占用检查 ==="
|
||||
netstat -tlnp | grep -E ":(80|443|8000|3306|6379|9000)" || echo "关键端口未监听"
|
||||
echo ""
|
||||
|
||||
echo "=== 服务状态 ==="
|
||||
systemctl is-active kaopeilian.service || echo "kaopeilian服务未运行"
|
||||
systemctl is-active kaopeilian-webhook.service || echo "webhook服务未运行"
|
||||
echo ""
|
||||
|
||||
echo "=== 诊断完成 ==="
|
||||
59
deploy/scripts/diagnose_dify_network.sh
Executable file
59
deploy/scripts/diagnose_dify_network.sh
Executable file
@@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
# Dify服务器网络诊断脚本
|
||||
# 请在Dify服务器(47.112.29.0)上运行此脚本
|
||||
|
||||
echo "=== Dify服务器网络诊断 ==="
|
||||
echo ""
|
||||
|
||||
# 1. DNS解析测试
|
||||
echo "1. DNS解析测试:"
|
||||
echo " hl.ireborn.com.cn: $(nslookup hl.ireborn.com.cn 2>/dev/null | grep Address | tail -1 || echo '解析失败')"
|
||||
echo " yy.ireborn.com.cn: $(nslookup yy.ireborn.com.cn 2>/dev/null | grep Address | tail -1 || echo '解析失败')"
|
||||
echo ""
|
||||
|
||||
# 2. 网络连通性测试
|
||||
echo "2. 网络连通性测试 (ping):"
|
||||
ping -c 3 120.79.247.16 2>&1 | tail -3
|
||||
echo ""
|
||||
|
||||
# 3. 端口连通性测试
|
||||
echo "3. 端口连通性测试:"
|
||||
echo " HTTP (80): $(timeout 5 bash -c 'echo >/dev/tcp/120.79.247.16/80' 2>&1 && echo '可连接' || echo '不可连接')"
|
||||
echo " HTTPS (443): $(timeout 5 bash -c 'echo >/dev/tcp/120.79.247.16/443' 2>&1 && echo '可连接' || echo '不可连接')"
|
||||
echo " API (8000): $(timeout 5 bash -c 'echo >/dev/tcp/120.79.247.16/8000' 2>&1 && echo '可连接' || echo '不可连接')"
|
||||
echo ""
|
||||
|
||||
# 4. HTTPS请求测试
|
||||
echo "4. HTTPS请求测试:"
|
||||
echo " 使用域名:"
|
||||
curl -s -o /dev/null -w "HTTP状态码: %{http_code}, 连接时间: %{time_connect}s\n" \
|
||||
--connect-timeout 10 \
|
||||
https://hl.ireborn.com.cn/health 2>&1 || echo " 请求失败"
|
||||
|
||||
echo " 使用IP地址:"
|
||||
curl -s -o /dev/null -w "HTTP状态码: %{http_code}, 连接时间: %{time_connect}s\n" \
|
||||
--connect-timeout 10 \
|
||||
-H "Host: hl.ireborn.com.cn" \
|
||||
https://120.79.247.16/health 2>&1 || echo " 请求失败"
|
||||
echo ""
|
||||
|
||||
# 5. SQL执行器API测试
|
||||
echo "5. SQL执行器API测试:"
|
||||
curl -s -X POST https://hl.ireborn.com.cn/api/v1/sql/execute-simple \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: dify-2025-kaopeilian" \
|
||||
-d '{"sql":"SELECT 1 as test"}' \
|
||||
--connect-timeout 10 2>&1 | head -1 || echo " 请求失败"
|
||||
echo ""
|
||||
|
||||
echo "=== 诊断完成 ==="
|
||||
echo ""
|
||||
echo "如果以上测试有失败项,请检查:"
|
||||
echo "1. 阿里云安全组是否允许来自47.112.29.0的入站流量"
|
||||
echo "2. 服务器防火墙规则"
|
||||
echo "3. VPC网络配置"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
50
deploy/scripts/force_restart.sh
Normal file
50
deploy/scripts/force_restart.sh
Normal file
@@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
# 强制重启所有服务
|
||||
|
||||
echo "=== 强制重启考培练系统服务 ==="
|
||||
echo "时间: $(date)"
|
||||
|
||||
# 1. 停止所有相关进程
|
||||
echo "1. 停止相关进程..."
|
||||
pkill -f "docker-compose"
|
||||
pkill -f "webhook_handler"
|
||||
|
||||
# 2. 清理Docker
|
||||
echo "2. 清理Docker容器和网络..."
|
||||
cd /root/aiedu
|
||||
docker compose down --remove-orphans 2>/dev/null || true
|
||||
docker system prune -f 2>/dev/null || true
|
||||
|
||||
# 3. 重启Docker服务
|
||||
echo "3. 重启Docker服务..."
|
||||
systemctl restart docker
|
||||
sleep 15
|
||||
|
||||
# 4. 启动服务
|
||||
echo "4. 启动考培练系统..."
|
||||
cd /root/aiedu
|
||||
docker compose up -d
|
||||
|
||||
# 5. 启动webhook服务
|
||||
echo "5. 启动Webhook服务..."
|
||||
systemctl restart kaopeilian-webhook.service
|
||||
|
||||
# 6. 等待服务启动
|
||||
echo "6. 等待服务启动..."
|
||||
sleep 30
|
||||
|
||||
# 7. 检查状态
|
||||
echo "7. 检查服务状态..."
|
||||
echo "Docker容器:"
|
||||
docker ps --format "table {{.Names}}\t{{.Status}}" 2>/dev/null || echo "Docker命令失败"
|
||||
|
||||
echo ""
|
||||
echo "端口监听:"
|
||||
netstat -tlnp | grep -E ":(80|443|8000|3306|6379|9000)" || echo "端口检查失败"
|
||||
|
||||
echo ""
|
||||
echo "网站测试:"
|
||||
curl -I --connect-timeout 5 https://aiedu.ireborn.com.cn 2>/dev/null || echo "网站访问失败"
|
||||
|
||||
echo ""
|
||||
echo "=== 重启完成 ==="
|
||||
60
deploy/scripts/quick_test_practice.sh
Executable file
60
deploy/scripts/quick_test_practice.sh
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/bin/bash
|
||||
# 快速测试陪练功能API
|
||||
|
||||
echo "============================================================"
|
||||
echo "陪练功能快速测试"
|
||||
echo "============================================================"
|
||||
|
||||
# 获取token
|
||||
echo -e "\n1. 登录获取token..."
|
||||
TOKEN=$(curl -s -X POST "http://localhost:8000/api/v1/auth/login" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"admin","password":"admin123"}' \
|
||||
| python3 -c "import sys,json; data=json.load(sys.stdin); print(data['data']['token']['access_token'] if data['code']==200 else '')")
|
||||
|
||||
if [ -z "$TOKEN" ]; then
|
||||
echo "❌ 登录失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ 登录成功"
|
||||
|
||||
# 测试场景列表
|
||||
echo -e "\n2. 测试场景列表..."
|
||||
SCENES=$(curl -s "http://localhost:8000/api/v1/practice/scenes" \
|
||||
-H "Authorization: Bearer $TOKEN")
|
||||
|
||||
COUNT=$(echo $SCENES | python3 -c "import sys,json; data=json.load(sys.stdin); print(data['data']['total'] if data['code']==200 else 0)")
|
||||
|
||||
if [ "$COUNT" -gt 0 ]; then
|
||||
echo "✅ 成功获取 $COUNT 个场景"
|
||||
else
|
||||
echo "❌ 获取场景失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 测试场景详情
|
||||
echo -e "\n3. 测试场景详情..."
|
||||
DETAIL=$(curl -s "http://localhost:8000/api/v1/practice/scenes/1" \
|
||||
-H "Authorization: Bearer $TOKEN")
|
||||
|
||||
NAME=$(echo $DETAIL | python3 -c "import sys,json; data=json.load(sys.stdin); print(data['data']['name'] if data['code']==200 else '')")
|
||||
|
||||
if [ -n "$NAME" ]; then
|
||||
echo "✅ 成功获取场景: $NAME"
|
||||
else
|
||||
echo "❌ 获取场景详情失败"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "\n============================================================"
|
||||
echo "✅ 陪练功能API测试通过"
|
||||
echo "============================================================"
|
||||
echo ""
|
||||
echo "📌 提示:"
|
||||
echo " - 场景列表: http://localhost:3001/trainee/ai-practice-center"
|
||||
echo " - 后端API: http://localhost:8000/docs"
|
||||
echo " - 运行完整测试: python3 test_practice_api.py"
|
||||
echo ""
|
||||
|
||||
|
||||
84
deploy/scripts/quick_test_score_mistakes.sh
Executable file
84
deploy/scripts/quick_test_score_mistakes.sh
Executable file
@@ -0,0 +1,84 @@
|
||||
#!/bin/bash
|
||||
# 快速测试成绩报告和错题本功能
|
||||
# 使用方法:./quick_test_score_mistakes.sh
|
||||
|
||||
set -e
|
||||
|
||||
echo "=========================================="
|
||||
echo "成绩报告与错题本功能快速测试"
|
||||
echo "=========================================="
|
||||
|
||||
# 颜色定义
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 1. 检查MySQL容器
|
||||
echo -e "\n${YELLOW}1. 检查MySQL容器状态...${NC}"
|
||||
if docker ps | grep -q kaopeilian-mysql-dev; then
|
||||
echo -e "${GREEN}✓ MySQL容器正在运行${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ MySQL容器未运行,请先启动${NC}"
|
||||
echo "启动命令: docker-compose -f docker-compose.dev.yml up -d mysql-dev"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2. 检查数据库字段
|
||||
echo -e "\n${YELLOW}2. 验证数据库字段...${NC}"
|
||||
echo "检查exams表的round字段..."
|
||||
docker exec -i kaopeilian-mysql-dev mysql -u root -pnj861021 kaopeilian -e "
|
||||
SELECT COLUMN_NAME, COLUMN_TYPE, COLUMN_COMMENT
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA='kaopeilian'
|
||||
AND TABLE_NAME='exams'
|
||||
AND COLUMN_NAME LIKE 'round%';" 2>/dev/null | grep -v "Warning"
|
||||
|
||||
echo "检查exam_mistakes表的question_type字段..."
|
||||
docker exec -i kaopeilian-mysql-dev mysql -u root -pnj861021 kaopeilian -e "
|
||||
SELECT COLUMN_NAME, COLUMN_TYPE, COLUMN_COMMENT
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA='kaopeilian'
|
||||
AND TABLE_NAME='exam_mistakes'
|
||||
AND COLUMN_NAME='question_type';" 2>/dev/null | grep -v "Warning"
|
||||
|
||||
# 3. 检查后端服务
|
||||
echo -e "\n${YELLOW}3. 检查后端服务状态...${NC}"
|
||||
if curl -s http://localhost:8000/health > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓ 后端服务正在运行${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ 后端服务未运行${NC}"
|
||||
echo "请在另一个终端启动后端:"
|
||||
echo " cd kaopeilian-backend"
|
||||
echo " uvicorn app.main:app --reload --host 0.0.0.0 --port 8000"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 4. 测试API(需要token)
|
||||
echo -e "\n${YELLOW}4. 测试API接口...${NC}"
|
||||
echo "提示:需要先登录获取token"
|
||||
echo "运行Python测试脚本:"
|
||||
echo " python3 test_score_report_api.py"
|
||||
|
||||
# 5. 前端服务检查
|
||||
echo -e "\n${YELLOW}5. 检查前端服务状态...${NC}"
|
||||
if curl -s http://localhost:3001 > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓ 前端服务正在运行${NC}"
|
||||
echo ""
|
||||
echo "📊 成绩报告页面:"
|
||||
echo " http://localhost:3001/trainee/score-report"
|
||||
echo ""
|
||||
echo "📝 错题本页面:"
|
||||
echo " http://localhost:3001/trainee/mistakes"
|
||||
else
|
||||
echo -e "${RED}✗ 前端服务未运行${NC}"
|
||||
echo "请在另一个终端启动前端:"
|
||||
echo " cd kaopeilian-frontend"
|
||||
echo " npm run dev"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "检查完成!请根据上述提示进行测试。"
|
||||
echo "=========================================="
|
||||
|
||||
68
deploy/scripts/robust_start.sh
Executable file
68
deploy/scripts/robust_start.sh
Executable file
@@ -0,0 +1,68 @@
|
||||
#!/bin/bash
|
||||
# 健壮的服务启动脚本
|
||||
|
||||
set -e
|
||||
|
||||
LOG_FILE="/var/log/kaopeilian_start.log"
|
||||
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
cd /root/aiedu
|
||||
|
||||
log "=== 开始启动考培练系统服务 ==="
|
||||
|
||||
# 1. 检查Docker服务
|
||||
log "1. 检查Docker服务状态..."
|
||||
if ! systemctl is-active --quiet docker; then
|
||||
log "启动Docker服务..."
|
||||
systemctl start docker
|
||||
sleep 10
|
||||
fi
|
||||
|
||||
# 2. 清理可能的问题容器
|
||||
log "2. 清理问题容器..."
|
||||
docker compose down --remove-orphans 2>/dev/null || true
|
||||
|
||||
# 3. 分步启动服务(避免前端构建失败影响其他服务)
|
||||
log "3. 启动基础服务..."
|
||||
docker compose up -d mysql redis
|
||||
sleep 30
|
||||
|
||||
log "4. 启动后端服务..."
|
||||
docker compose up -d backend
|
||||
sleep 20
|
||||
|
||||
log "5. 尝试构建前端..."
|
||||
if docker compose build frontend; then
|
||||
log "前端构建成功,启动前端服务..."
|
||||
docker compose up -d frontend
|
||||
else
|
||||
log "前端构建失败,使用旧镜像启动..."
|
||||
docker compose up -d frontend || log "前端启动失败,跳过前端服务"
|
||||
fi
|
||||
|
||||
log "6. 启动Nginx..."
|
||||
docker compose up -d nginx
|
||||
|
||||
# 7. 检查服务状态
|
||||
log "7. 检查服务状态..."
|
||||
sleep 20
|
||||
docker compose ps
|
||||
|
||||
# 8. 健康检查
|
||||
log "8. 执行健康检查..."
|
||||
if curl -f -s https://aiedu.ireborn.com.cn/health > /dev/null; then
|
||||
log "✅ 后端服务正常"
|
||||
else
|
||||
log "❌ 后端服务异常"
|
||||
fi
|
||||
|
||||
if curl -f -s -I https://aiedu.ireborn.com.cn > /dev/null; then
|
||||
log "✅ 前端服务正常"
|
||||
else
|
||||
log "❌ 前端服务异常"
|
||||
fi
|
||||
|
||||
log "=== 服务启动完成 ==="
|
||||
246
deploy/scripts/setup_environment.sh
Normal file
246
deploy/scripts/setup_environment.sh
Normal file
@@ -0,0 +1,246 @@
|
||||
#!/bin/bash
|
||||
# 环境设置脚本
|
||||
|
||||
set -e
|
||||
|
||||
ENV_TYPE=${1:-development}
|
||||
FORCE_SETUP=${2:-false}
|
||||
|
||||
echo "=== 考培练系统环境设置 ==="
|
||||
echo "环境类型: $ENV_TYPE"
|
||||
echo "设置时间: $(date)"
|
||||
echo ""
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 打印带颜色的消息
|
||||
print_message() {
|
||||
local color=$1
|
||||
local message=$2
|
||||
echo -e "${color}${message}${NC}"
|
||||
}
|
||||
|
||||
# 检查必要的工具
|
||||
check_requirements() {
|
||||
print_message $BLUE "检查系统要求..."
|
||||
|
||||
# 检查Docker
|
||||
if ! command -v docker &> /dev/null; then
|
||||
print_message $RED "❌ Docker未安装,请先安装Docker"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查Docker Compose
|
||||
if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then
|
||||
print_message $RED "❌ Docker Compose未安装,请先安装Docker Compose"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查Node.js (如果需要本地开发)
|
||||
if [ "$ENV_TYPE" = "development" ] && ! command -v node &> /dev/null; then
|
||||
print_message $YELLOW "⚠️ Node.js未安装,建议安装以支持本地开发"
|
||||
fi
|
||||
|
||||
# 检查Python (如果需要本地开发)
|
||||
if [ "$ENV_TYPE" = "development" ] && ! command -v python3 &> /dev/null; then
|
||||
print_message $YELLOW "⚠️ Python3未安装,建议安装以支持本地开发"
|
||||
fi
|
||||
|
||||
print_message $GREEN "✅ 系统要求检查完成"
|
||||
}
|
||||
|
||||
# 设置前端环境
|
||||
setup_frontend_env() {
|
||||
print_message $BLUE "设置前端环境配置..."
|
||||
|
||||
local env_file="kaopeilian-frontend/.env.$ENV_TYPE"
|
||||
local example_file="kaopeilian-frontend/.env.example"
|
||||
|
||||
if [ ! -f "$env_file" ] || [ "$FORCE_SETUP" = "true" ]; then
|
||||
if [ -f "$example_file" ]; then
|
||||
cp "$example_file" "$env_file"
|
||||
print_message $GREEN "✅ 创建前端环境配置: $env_file"
|
||||
else
|
||||
print_message $YELLOW "⚠️ 前端配置模板不存在: $example_file"
|
||||
fi
|
||||
else
|
||||
print_message $GREEN "✅ 前端环境配置已存在: $env_file"
|
||||
fi
|
||||
|
||||
# 根据环境类型更新配置
|
||||
if [ -f "$env_file" ]; then
|
||||
case $ENV_TYPE in
|
||||
development)
|
||||
sed -i.bak 's/VITE_APP_TITLE=.*/VITE_APP_TITLE=考培练系统(开发)/' "$env_file"
|
||||
sed -i.bak 's/VITE_APP_ENV=.*/VITE_APP_ENV=development/' "$env_file"
|
||||
sed -i.bak 's/VITE_API_BASE_URL=.*/VITE_API_BASE_URL=http:\/\/localhost:8000/' "$env_file"
|
||||
sed -i.bak 's/VITE_ENABLE_DEVTOOLS=.*/VITE_ENABLE_DEVTOOLS=true/' "$env_file"
|
||||
rm -f "$env_file.bak"
|
||||
;;
|
||||
production)
|
||||
sed -i.bak 's/VITE_APP_TITLE=.*/VITE_APP_TITLE=考培练系统/' "$env_file"
|
||||
sed -i.bak 's/VITE_APP_ENV=.*/VITE_APP_ENV=production/' "$env_file"
|
||||
sed -i.bak 's/VITE_API_BASE_URL=.*/VITE_API_BASE_URL=https:\/\/aiedu.ireborn.com.cn/' "$env_file"
|
||||
sed -i.bak 's/VITE_ENABLE_DEVTOOLS=.*/VITE_ENABLE_DEVTOOLS=false/' "$env_file"
|
||||
rm -f "$env_file.bak"
|
||||
;;
|
||||
esac
|
||||
print_message $GREEN "✅ 前端环境配置已更新"
|
||||
fi
|
||||
}
|
||||
|
||||
# 设置后端环境
|
||||
setup_backend_env() {
|
||||
print_message $BLUE "设置后端环境配置..."
|
||||
|
||||
local env_file="kaopeilian-backend/.env.$ENV_TYPE"
|
||||
local example_file="kaopeilian-backend/.env.example"
|
||||
|
||||
if [ ! -f "$env_file" ] || [ "$FORCE_SETUP" = "true" ]; then
|
||||
if [ -f "$example_file" ]; then
|
||||
cp "$example_file" "$env_file"
|
||||
print_message $GREEN "✅ 创建后端环境配置: $env_file"
|
||||
else
|
||||
print_message $YELLOW "⚠️ 后端配置模板不存在: $example_file"
|
||||
fi
|
||||
else
|
||||
print_message $GREEN "✅ 后端环境配置已存在: $env_file"
|
||||
fi
|
||||
|
||||
# 根据环境类型更新配置
|
||||
if [ -f "$env_file" ]; then
|
||||
case $ENV_TYPE in
|
||||
development)
|
||||
sed -i.bak 's/ENV=.*/ENV=development/' "$env_file"
|
||||
sed -i.bak 's/DEBUG=.*/DEBUG=true/' "$env_file"
|
||||
sed -i.bak 's/DATABASE_URL=.*/DATABASE_URL=mysql+aiomysql:\/\/root:Kaopeilian2025!@#@localhost:3306\/kaopeilian?charset=utf8mb4/' "$env_file"
|
||||
sed -i.bak 's/MYSQL_HOST=.*/MYSQL_HOST=localhost/' "$env_file"
|
||||
rm -f "$env_file.bak"
|
||||
;;
|
||||
production)
|
||||
sed -i.bak 's/ENV=.*/ENV=production/' "$env_file"
|
||||
sed -i.bak 's/DEBUG=.*/DEBUG=false/' "$env_file"
|
||||
sed -i.bak 's/DATABASE_URL=.*/DATABASE_URL=mysql+aiomysql:\/\/root:Kaopeilian2025!@#@mysql:3306\/kaopeilian?charset=utf8mb4/' "$env_file"
|
||||
sed -i.bak 's/MYSQL_HOST=.*/MYSQL_HOST=mysql/' "$env_file"
|
||||
rm -f "$env_file.bak"
|
||||
;;
|
||||
esac
|
||||
print_message $GREEN "✅ 后端环境配置已更新"
|
||||
fi
|
||||
}
|
||||
|
||||
# 设置Docker环境
|
||||
setup_docker_env() {
|
||||
print_message $BLUE "设置Docker环境..."
|
||||
|
||||
case $ENV_TYPE in
|
||||
development)
|
||||
if [ ! -f "docker-compose.dev.yml" ]; then
|
||||
print_message $RED "❌ 开发环境Docker配置不存在: docker-compose.dev.yml"
|
||||
return 1
|
||||
fi
|
||||
print_message $GREEN "✅ 开发环境Docker配置已就绪"
|
||||
;;
|
||||
production)
|
||||
if [ ! -f "docker-compose.yml" ]; then
|
||||
print_message $RED "❌ 生产环境Docker配置不存在: docker-compose.yml"
|
||||
return 1
|
||||
fi
|
||||
print_message $GREEN "✅ 生产环境Docker配置已就绪"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 启动环境
|
||||
start_environment() {
|
||||
print_message $BLUE "启动$ENV_TYPE环境..."
|
||||
|
||||
case $ENV_TYPE in
|
||||
development)
|
||||
if command -v docker-compose &> /dev/null; then
|
||||
docker-compose -f docker-compose.dev.yml up -d
|
||||
else
|
||||
docker compose -f docker-compose.dev.yml up -d
|
||||
fi
|
||||
;;
|
||||
production)
|
||||
if command -v docker-compose &> /dev/null; then
|
||||
docker-compose up -d
|
||||
else
|
||||
docker compose up -d
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
print_message $GREEN "✅ 环境启动成功"
|
||||
|
||||
# 等待服务启动
|
||||
print_message $BLUE "等待服务启动..."
|
||||
sleep 10
|
||||
|
||||
# 检查服务状态
|
||||
./scripts/check_environment.sh
|
||||
else
|
||||
print_message $RED "❌ 环境启动失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示使用说明
|
||||
show_usage() {
|
||||
echo "用法: $0 [环境类型] [强制设置]"
|
||||
echo ""
|
||||
echo "环境类型:"
|
||||
echo " development - 开发环境 (默认)"
|
||||
echo " production - 生产环境"
|
||||
echo ""
|
||||
echo "强制设置:"
|
||||
echo " true - 强制重新创建配置文件"
|
||||
echo " false - 保留已存在的配置文件 (默认)"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 # 设置开发环境"
|
||||
echo " $0 development # 设置开发环境"
|
||||
echo " $0 production # 设置生产环境"
|
||||
echo " $0 development true # 强制重新设置开发环境"
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
|
||||
show_usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$ENV_TYPE" != "development" ] && [ "$ENV_TYPE" != "production" ]; then
|
||||
print_message $RED "❌ 无效的环境类型: $ENV_TYPE"
|
||||
show_usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
check_requirements
|
||||
setup_frontend_env
|
||||
setup_backend_env
|
||||
setup_docker_env
|
||||
|
||||
# 询问是否启动环境
|
||||
read -p "是否启动$ENV_TYPE环境? [y/N]: " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
start_environment
|
||||
fi
|
||||
|
||||
print_message $GREEN "=== 环境设置完成 ==="
|
||||
print_message $BLUE "环境类型: $ENV_TYPE"
|
||||
print_message $BLUE "前端地址: http://localhost:3001"
|
||||
print_message $BLUE "后端地址: http://localhost:8000"
|
||||
print_message $BLUE "API文档: http://localhost:8000/docs"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
59
deploy/scripts/setup_git_strategy.sh
Normal file
59
deploy/scripts/setup_git_strategy.sh
Normal file
@@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
# Git分支策略配置脚本
|
||||
|
||||
echo "=== 配置Git分支策略 ==="
|
||||
|
||||
cd /root/aiedu
|
||||
|
||||
# 1. 创建production分支(如果不存在)
|
||||
if ! git branch | grep -q production; then
|
||||
echo "创建production分支..."
|
||||
git checkout -b production
|
||||
git push origin production
|
||||
echo "production分支已创建"
|
||||
else
|
||||
echo "production分支已存在"
|
||||
fi
|
||||
|
||||
# 2. 切换到production分支
|
||||
git checkout production
|
||||
|
||||
# 3. 更新webhook脚本,监听production分支
|
||||
echo "更新webhook配置..."
|
||||
sed -i 's/refs\/heads\/main/refs\/heads\/production/g' /root/aiedu/scripts/webhook_handler.py
|
||||
|
||||
# 4. 重启webhook服务
|
||||
systemctl restart kaopeilian-webhook.service
|
||||
|
||||
# 5. 创建.gitignore规则
|
||||
echo "更新.gitignore..."
|
||||
cat >> /root/aiedu/.gitignore << 'EOF'
|
||||
|
||||
# 生产环境配置文件(不提交)
|
||||
kaopeilian-backend/.env.production
|
||||
docker-compose.override.yml
|
||||
|
||||
# 服务器运行时文件
|
||||
scripts/force_restart.sh
|
||||
scripts/diagnose.sh
|
||||
/var/log/kaopeilian_*.log
|
||||
EOF
|
||||
|
||||
# 6. 提交配置变更
|
||||
echo "提交配置变更到production分支..."
|
||||
git add .gitignore scripts/webhook_handler.py
|
||||
git commit -m "配置生产环境分支策略"
|
||||
git push origin production
|
||||
|
||||
echo ""
|
||||
echo "=== Git分支策略配置完成 ==="
|
||||
echo ""
|
||||
echo "使用说明:"
|
||||
echo "1. 开发者在main分支开发"
|
||||
echo "2. 生产环境使用production分支"
|
||||
echo "3. 发布流程:"
|
||||
echo " git checkout production"
|
||||
echo " git merge main"
|
||||
echo " git push origin production"
|
||||
echo ""
|
||||
echo "4. 服务器自动更新监听production分支"
|
||||
208
deploy/scripts/start-dev.sh
Executable file
208
deploy/scripts/start-dev.sh
Executable file
@@ -0,0 +1,208 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 考培练系统开发环境启动脚本
|
||||
# 使用方法:
|
||||
# ./start-dev.sh # 启动基础服务
|
||||
# ./start-dev.sh --with-admin # 启动服务 + phpMyAdmin
|
||||
# ./start-dev.sh --with-mail # 启动服务 + 邮件测试
|
||||
# ./start-dev.sh --full # 启动所有服务
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查Docker是否运行
|
||||
check_docker() {
|
||||
if ! docker info >/dev/null 2>&1; then
|
||||
log_error "Docker 未运行,请先启动 Docker"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 清理旧容器
|
||||
cleanup() {
|
||||
log_info "清理旧容器..."
|
||||
docker-compose -f docker-compose.dev.yml down --remove-orphans 2>/dev/null || true
|
||||
|
||||
# 清理孤立的容器
|
||||
docker ps -a --filter "name=kaopeilian-" --format "{{.ID}}" | xargs -r docker rm -f 2>/dev/null || true
|
||||
}
|
||||
|
||||
# 构建镜像
|
||||
build_images() {
|
||||
log_info "构建开发环境镜像..."
|
||||
docker-compose -f docker-compose.dev.yml build --no-cache
|
||||
}
|
||||
|
||||
# 启动服务
|
||||
start_services() {
|
||||
local profiles=""
|
||||
|
||||
# 解析命令行参数
|
||||
case "${1:-}" in
|
||||
--with-admin)
|
||||
profiles="--profile admin"
|
||||
log_info "启动完全Docker化服务(包含 phpMyAdmin)..."
|
||||
;;
|
||||
--with-mail)
|
||||
profiles="--profile mail"
|
||||
log_info "启动完全Docker化服务(包含邮件测试)..."
|
||||
;;
|
||||
--full)
|
||||
profiles="--profile admin --profile mail"
|
||||
log_info "启动所有Docker化服务..."
|
||||
;;
|
||||
*)
|
||||
log_info "启动完全Docker化基础服务(前后端+数据库)..."
|
||||
;;
|
||||
esac
|
||||
|
||||
# 启动所有服务(包括前后端)
|
||||
docker-compose -f docker-compose.dev.yml up -d frontend-dev backend-dev mysql-dev redis-dev $profiles
|
||||
}
|
||||
|
||||
# 等待服务就绪
|
||||
wait_for_services() {
|
||||
log_info "等待服务启动..."
|
||||
|
||||
# 等待数据库
|
||||
log_info "等待 MySQL 数据库启动..."
|
||||
timeout=60
|
||||
while ! docker exec kaopeilian-mysql-dev mysqladmin ping -h"localhost" --silent 2>/dev/null; do
|
||||
timeout=$((timeout - 1))
|
||||
if [ $timeout -eq 0 ]; then
|
||||
log_error "MySQL 启动超时"
|
||||
return 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
log_success "MySQL 数据库已就绪"
|
||||
|
||||
# 等待 Redis
|
||||
log_info "等待 Redis 缓存启动..."
|
||||
timeout=30
|
||||
while ! docker exec kaopeilian-redis-dev redis-cli ping 2>/dev/null | grep -q PONG; do
|
||||
timeout=$((timeout - 1))
|
||||
if [ $timeout -eq 0 ]; then
|
||||
log_error "Redis 启动超时"
|
||||
return 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
log_success "Redis 缓存已就绪"
|
||||
|
||||
# 等待后端服务
|
||||
log_info "等待后端服务启动..."
|
||||
timeout=60
|
||||
while ! curl -s http://localhost:8000/health >/dev/null 2>&1; do
|
||||
timeout=$((timeout - 1))
|
||||
if [ $timeout -eq 0 ]; then
|
||||
log_error "后端服务启动超时"
|
||||
return 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
log_success "后端服务已就绪"
|
||||
|
||||
# 等待前端服务
|
||||
log_info "等待前端服务启动..."
|
||||
timeout=60
|
||||
while ! curl -s http://localhost:3001/ >/dev/null 2>&1; do
|
||||
timeout=$((timeout - 1))
|
||||
if [ $timeout -eq 0 ]; then
|
||||
log_error "前端服务启动超时"
|
||||
return 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
log_success "前端服务已就绪"
|
||||
}
|
||||
|
||||
# 显示服务状态
|
||||
show_status() {
|
||||
echo ""
|
||||
log_success "🎉 考培练系统开发环境启动成功!"
|
||||
echo ""
|
||||
echo "📋 服务访问地址:"
|
||||
echo " 🌐 前端开发服务器: http://localhost:3001 (Docker容器)"
|
||||
echo " 🔧 后端API服务: http://localhost:8000 (Docker容器)"
|
||||
echo " 📚 API文档: http://localhost:8000/docs"
|
||||
echo " 🗄️ MySQL数据库: localhost:3306 (Docker容器)"
|
||||
echo " 🔄 Redis缓存: localhost:6379 (Docker容器)"
|
||||
|
||||
if docker ps --filter "name=kaopeilian-phpmyadmin-dev" --format "{{.Names}}" | grep -q phpmyadmin; then
|
||||
echo " 🛠️ phpMyAdmin: http://localhost:8080"
|
||||
fi
|
||||
|
||||
if docker ps --filter "name=kaopeilian-mailhog-dev" --format "{{.Names}}" | grep -q mailhog; then
|
||||
echo " 📧 邮件测试界面: http://localhost:8025"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🔧 常用命令:"
|
||||
echo " 查看日志: docker-compose -f docker-compose.dev.yml logs -f [service_name]"
|
||||
echo " 停止服务: docker-compose -f docker-compose.dev.yml down"
|
||||
echo " 重启服务: docker-compose -f docker-compose.dev.yml restart [service_name]"
|
||||
echo ""
|
||||
echo "💡 开发提示:"
|
||||
echo " - 🔥 代码修改会自动重载(Docker热重载已启用)"
|
||||
echo " - 🎨 前端: 修改 kaopeilian-frontend/src/ 目录下的文件"
|
||||
echo " - ⚙️ 后端: 修改 kaopeilian-backend/app/ 目录下的文件"
|
||||
echo " - 🐳 所有服务均运行在Docker容器中,环境完全一致"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
echo "🚀 考培练系统开发环境启动器"
|
||||
echo "================================"
|
||||
|
||||
# 检查 Docker
|
||||
check_docker
|
||||
|
||||
# 清理旧环境
|
||||
cleanup
|
||||
|
||||
# 构建镜像
|
||||
build_images
|
||||
|
||||
# 启动服务
|
||||
start_services "$@"
|
||||
|
||||
# 等待服务就绪
|
||||
if wait_for_services; then
|
||||
show_status
|
||||
exit 0
|
||||
else
|
||||
log_error "服务启动失败,请检查日志"
|
||||
docker-compose -f docker-compose.dev.yml logs --tail=50
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 如果直接运行此脚本
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
main "$@"
|
||||
fi
|
||||
186
deploy/scripts/start-kpl.sh
Executable file
186
deploy/scripts/start-kpl.sh
Executable file
@@ -0,0 +1,186 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 瑞小美团队开发环境启动脚本
|
||||
# 域名:kpl.ireborn.com.cn
|
||||
# 使用方法:
|
||||
# ./start-kpl.sh # 启动基础服务
|
||||
# ./start-kpl.sh --with-admin # 启动服务 + phpMyAdmin
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 检查Docker是否运行
|
||||
check_docker() {
|
||||
if ! docker info >/dev/null 2>&1; then
|
||||
log_error "Docker 未运行,请先启动 Docker"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 构建镜像
|
||||
build_images() {
|
||||
log_info "构建KPL开发环境镜像..."
|
||||
docker compose -f docker-compose.kpl.yml build --no-cache
|
||||
}
|
||||
|
||||
# 启动服务
|
||||
start_services() {
|
||||
local profiles=""
|
||||
|
||||
# 解析命令行参数
|
||||
case "${1:-}" in
|
||||
--with-admin)
|
||||
profiles="--profile admin"
|
||||
log_info "启动KPL服务(包含 phpMyAdmin)..."
|
||||
;;
|
||||
*)
|
||||
log_info "启动KPL基础服务(前后端+数据库)..."
|
||||
;;
|
||||
esac
|
||||
|
||||
# 启动所有服务
|
||||
docker compose -f docker-compose.kpl.yml up -d $profiles
|
||||
}
|
||||
|
||||
# 等待服务就绪
|
||||
wait_for_services() {
|
||||
log_info "等待服务启动..."
|
||||
|
||||
# 等待数据库
|
||||
log_info "等待 MySQL 数据库启动..."
|
||||
timeout=60
|
||||
while ! docker exec kpl-mysql-dev mysqladmin ping -h"localhost" --silent 2>/dev/null; do
|
||||
timeout=$((timeout - 1))
|
||||
if [ $timeout -eq 0 ]; then
|
||||
log_error "MySQL 启动超时"
|
||||
return 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
log_success "MySQL 数据库已就绪"
|
||||
|
||||
# 等待 Redis
|
||||
log_info "等待 Redis 缓存启动..."
|
||||
timeout=30
|
||||
while ! docker exec kpl-redis-dev redis-cli ping 2>/dev/null | grep -q PONG; do
|
||||
timeout=$((timeout - 1))
|
||||
if [ $timeout -eq 0 ]; then
|
||||
log_error "Redis 启动超时"
|
||||
return 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
log_success "Redis 缓存已就绪"
|
||||
|
||||
# 等待后端服务
|
||||
log_info "等待后端服务启动..."
|
||||
timeout=60
|
||||
while ! curl -s http://localhost:8001/health >/dev/null 2>&1; do
|
||||
timeout=$((timeout - 1))
|
||||
if [ $timeout -eq 0 ]; then
|
||||
log_error "后端服务启动超时"
|
||||
return 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
log_success "后端服务已就绪"
|
||||
|
||||
# 等待前端服务
|
||||
log_info "等待前端服务启动..."
|
||||
timeout=60
|
||||
while ! curl -s http://localhost:3002/ >/dev/null 2>&1; do
|
||||
timeout=$((timeout - 1))
|
||||
if [ $timeout -eq 0 ]; then
|
||||
log_error "前端服务启动超时"
|
||||
return 1
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
log_success "前端服务已就绪"
|
||||
}
|
||||
|
||||
# 显示服务状态
|
||||
show_status() {
|
||||
echo ""
|
||||
log_success "🎉 瑞小美团队开发环境启动成功!"
|
||||
echo ""
|
||||
echo "📋 服务访问地址:"
|
||||
echo " 🌐 正式域名访问: https://kpl.ireborn.com.cn"
|
||||
echo " 🖥️ 本地前端服务: http://localhost:3002"
|
||||
echo " 🔧 本地后端API: http://localhost:8001"
|
||||
echo " 📚 API文档: http://localhost:8001/docs"
|
||||
echo " 🗄️ MySQL数据库: localhost:3308"
|
||||
echo " 🔄 Redis缓存: localhost:6380"
|
||||
|
||||
if docker ps --filter "name=kpl-phpmyadmin-dev" --format "{{.Names}}" | grep -q phpmyadmin; then
|
||||
echo " 🛠️ phpMyAdmin: http://localhost:8081"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🔧 常用命令:"
|
||||
echo " 查看日志: docker compose -f docker-compose.kpl.yml logs -f [service_name]"
|
||||
echo " 停止服务: ./stop-kpl.sh 或 docker compose -f docker-compose.kpl.yml down"
|
||||
echo " 重启服务: docker compose -f docker-compose.kpl.yml restart [service_name]"
|
||||
echo ""
|
||||
echo "💡 开发提示:"
|
||||
echo " - 🔥 代码修改会自动重载(热重载已启用)"
|
||||
echo " - 🎨 前端: 修改 kaopeilian-frontend/src/ 目录下的文件"
|
||||
echo " - ⚙️ 后端: 修改 kaopeilian-backend/app/ 目录下的文件"
|
||||
echo " - 🔒 已配置HTTPS访问,使用域名访问更安全"
|
||||
echo ""
|
||||
echo "📌 注意:"
|
||||
echo " - 此环境与演示系统(aiedu.ireborn.com.cn)完全隔离"
|
||||
echo " - 拥有独立的数据库和Redis实例"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
echo "🚀 瑞小美团队开发环境启动器"
|
||||
echo "================================"
|
||||
|
||||
# 检查 Docker
|
||||
check_docker
|
||||
|
||||
# 启动服务(不重新构建)
|
||||
start_services "$@"
|
||||
|
||||
# 等待服务就绪
|
||||
if wait_for_services; then
|
||||
show_status
|
||||
exit 0
|
||||
else
|
||||
log_error "服务启动失败,请检查日志"
|
||||
docker compose -f docker-compose.kpl.yml logs --tail=50
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 如果直接运行此脚本
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
main "$@"
|
||||
fi
|
||||
|
||||
86
deploy/scripts/start.sh
Executable file
86
deploy/scripts/start.sh
Executable file
@@ -0,0 +1,86 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Coze智能体聊天系统启动脚本
|
||||
|
||||
echo "🚀 启动Coze智能体聊天系统..."
|
||||
|
||||
# 检查是否安装了必要工具
|
||||
check_command() {
|
||||
if ! command -v $1 &> /dev/null; then
|
||||
echo "❌ $1 未安装,请先安装 $1"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
echo "🔍 检查环境..."
|
||||
check_command python3
|
||||
check_command node
|
||||
check_command npm
|
||||
|
||||
# 关闭可能影响 coze.cn 访问的系统代理,并设置直连白名单
|
||||
echo "🛡️ 配置网络直连(禁用代理,放行 *.coze.cn)..."
|
||||
unset http_proxy https_proxy all_proxy HTTP_PROXY HTTPS_PROXY ALL_PROXY || true
|
||||
export NO_PROXY=localhost,127.0.0.1,api.coze.cn,.coze.cn
|
||||
|
||||
# 启动后端
|
||||
echo "🐍 启动Python后端..."
|
||||
cd coze-chat-backend
|
||||
|
||||
# 检查虚拟环境
|
||||
if [ ! -d "venv" ]; then
|
||||
echo "📦 创建Python虚拟环境..."
|
||||
python3 -m venv venv
|
||||
fi
|
||||
|
||||
# 激活虚拟环境
|
||||
source venv/bin/activate
|
||||
|
||||
# 安装依赖
|
||||
echo "📦 安装Python依赖..."
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 检查配置文件
|
||||
if [ ! -f "local_config.py" ]; then
|
||||
echo "⚠️ local_config.py文件不存在,请先配置API认证"
|
||||
echo "📋 可以参考 local_config.py.example 创建配置文件"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 后台启动Python服务
|
||||
echo "🚀 启动后端服务..."
|
||||
python main.py &
|
||||
BACKEND_PID=$!
|
||||
|
||||
cd ..
|
||||
|
||||
# 启动前端
|
||||
echo "⚛️ 启动React前端..."
|
||||
cd coze-chat-frontend
|
||||
|
||||
# 安装依赖
|
||||
echo "📦 安装前端依赖..."
|
||||
npm install
|
||||
|
||||
# 启动前端开发服务器
|
||||
echo "🚀 启动前端服务..."
|
||||
npm run dev &
|
||||
FRONTEND_PID=$!
|
||||
|
||||
# 显示启动信息
|
||||
echo ""
|
||||
echo "✅ 系统启动完成!"
|
||||
echo "🔗 前端地址: http://localhost:3001"
|
||||
echo "🔗 后端地址: http://localhost:8010"
|
||||
echo "📖 API文档: http://localhost:8010/docs"
|
||||
echo ""
|
||||
echo "按 Ctrl+C 停止所有服务"
|
||||
|
||||
# 等待用户中断
|
||||
wait
|
||||
|
||||
# 清理进程
|
||||
echo "🧹 清理进程..."
|
||||
kill $BACKEND_PID 2>/dev/null
|
||||
kill $FRONTEND_PID 2>/dev/null
|
||||
|
||||
echo "👋 系统已停止"
|
||||
50
deploy/scripts/stop-dev.sh
Executable file
50
deploy/scripts/stop-dev.sh
Executable file
@@ -0,0 +1,50 @@
|
||||
#!/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
|
||||
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
main() {
|
||||
echo "🛑 停止考培练系统开发环境"
|
||||
echo "=========================="
|
||||
|
||||
log_info "停止所有开发服务..."
|
||||
docker-compose -f docker-compose.dev.yml down --remove-orphans
|
||||
|
||||
# 可选:清理数据卷(谨慎使用)
|
||||
if [[ "$1" == "--clean-data" ]]; then
|
||||
log_warning "清理开发数据卷..."
|
||||
docker volume rm kaopeilian-mysql-dev-data kaopeilian-redis-dev-data 2>/dev/null || true
|
||||
log_success "数据卷已清理"
|
||||
fi
|
||||
|
||||
# 可选:清理镜像
|
||||
if [[ "$1" == "--clean-all" ]]; then
|
||||
log_warning "清理开发镜像..."
|
||||
docker images --filter "reference=*kaopeilian*dev*" -q | xargs -r docker rmi -f
|
||||
log_success "开发镜像已清理"
|
||||
fi
|
||||
|
||||
log_success "✅ 开发环境已停止"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
74
deploy/scripts/stop-kpl.sh
Executable file
74
deploy/scripts/stop-kpl.sh
Executable file
@@ -0,0 +1,74 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 瑞小美团队开发环境停止脚本
|
||||
# 使用方法:
|
||||
# ./stop-kpl.sh # 停止所有KPL服务
|
||||
# ./stop-kpl.sh --keep-data # 停止服务但保留数据卷
|
||||
|
||||
set -e
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
# 停止服务
|
||||
stop_services() {
|
||||
log_info "停止KPL开发环境服务..."
|
||||
|
||||
if [ "$1" = "--keep-data" ]; then
|
||||
docker compose -f docker-compose.kpl.yml down
|
||||
log_info "服务已停止,数据卷已保留"
|
||||
else
|
||||
docker compose -f docker-compose.kpl.yml down -v
|
||||
log_warning "服务已停止,数据卷已删除"
|
||||
fi
|
||||
}
|
||||
|
||||
# 显示状态
|
||||
show_status() {
|
||||
echo ""
|
||||
log_success "✅ KPL开发环境已停止"
|
||||
echo ""
|
||||
echo "💡 提示:"
|
||||
echo " - 重新启动: ./start-kpl.sh"
|
||||
echo " - 查看演示系统: docker ps | grep kaopeilian"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
echo "🛑 瑞小美团队开发环境停止器"
|
||||
echo "================================"
|
||||
|
||||
# 停止服务
|
||||
stop_services "$@"
|
||||
|
||||
# 显示状态
|
||||
show_status
|
||||
}
|
||||
|
||||
# 如果直接运行此脚本
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
main "$@"
|
||||
fi
|
||||
|
||||
58
deploy/scripts/test_course_chat.sh
Executable file
58
deploy/scripts/test_course_chat.sh
Executable file
@@ -0,0 +1,58 @@
|
||||
#!/bin/bash
|
||||
# 测试与课程对话功能 - Dify 集成
|
||||
|
||||
echo "============================================================"
|
||||
echo "🧪 与课程对话功能测试 - Dify 集成"
|
||||
echo "============================================================"
|
||||
|
||||
API_BASE="http://localhost:8000"
|
||||
|
||||
# 1. 登录获取 token
|
||||
echo ""
|
||||
echo "🔑 正在登录..."
|
||||
LOGIN_RESPONSE=$(curl -s -X POST "${API_BASE}/api/v1/auth/login" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "username=test_user&password=123456")
|
||||
|
||||
TOKEN=$(echo $LOGIN_RESPONSE | python3 -c "import sys, json; print(json.load(sys.stdin).get('access_token', ''))")
|
||||
|
||||
if [ -z "$TOKEN" ]; then
|
||||
echo "❌ 登录失败"
|
||||
echo "响应: $LOGIN_RESPONSE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ 登录成功"
|
||||
echo "Token: ${TOKEN:0:20}..."
|
||||
|
||||
# 2. 测试首次对话
|
||||
echo ""
|
||||
echo "============================================================"
|
||||
echo "测试场景 1: 首次对话(创建新会话)"
|
||||
echo "============================================================"
|
||||
echo ""
|
||||
echo "💬 测试与课程 1 对话"
|
||||
echo "问题: 这门课程讲什么?"
|
||||
echo ""
|
||||
echo "📡 SSE 事件流:"
|
||||
echo "------------------------------------------------------------"
|
||||
|
||||
curl -N -X POST "${API_BASE}/api/v1/course/chat" \
|
||||
-H "Authorization: Bearer ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"course_id": 1,
|
||||
"query": "这门课程讲什么?"
|
||||
}'
|
||||
|
||||
echo ""
|
||||
echo "------------------------------------------------------------"
|
||||
echo ""
|
||||
echo "✅ 测试完成!"
|
||||
echo ""
|
||||
echo "如需测试续接对话,请复制上面输出的 conversation_id,然后运行:"
|
||||
echo " curl -N -X POST '${API_BASE}/api/v1/course/chat' \\"
|
||||
echo " -H 'Authorization: Bearer ${TOKEN}' \\"
|
||||
echo " -H 'Content-Type: application/json' \\"
|
||||
echo " -d '{\"course_id\": 1, \"query\": \"能详细说说吗?\", \"conversation_id\": \"你的conversation_id\"}'"
|
||||
|
||||
39
deploy/scripts/test_statistics_apis.sh
Executable file
39
deploy/scripts/test_statistics_apis.sh
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
# 测试所有统计分析API
|
||||
|
||||
API_BASE="http://localhost:8000/api/v1/statistics"
|
||||
PERIOD="month"
|
||||
|
||||
echo "=========================================="
|
||||
echo "测试统计分析API"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
echo "1️⃣ 测试关键指标..."
|
||||
curl -s "${API_BASE}/key-metrics?period=${PERIOD}" | python3 -m json.tool | head -20
|
||||
|
||||
echo ""
|
||||
echo "2️⃣ 测试成绩分布..."
|
||||
curl -s "${API_BASE}/score-distribution?period=${PERIOD}" | python3 -m json.tool
|
||||
|
||||
echo ""
|
||||
echo "3️⃣ 测试难度分析..."
|
||||
curl -s "${API_BASE}/difficulty-analysis?period=${PERIOD}" | python3 -m json.tool
|
||||
|
||||
echo ""
|
||||
echo "4️⃣ 测试知识点掌握度..."
|
||||
curl -s "${API_BASE}/knowledge-mastery" | python3 -m json.tool | head -30
|
||||
|
||||
echo ""
|
||||
echo "5️⃣ 测试学习时长..."
|
||||
curl -s "${API_BASE}/study-time?period=${PERIOD}" | python3 -m json.tool | head -30
|
||||
|
||||
echo ""
|
||||
echo "6️⃣ 测试详细数据..."
|
||||
curl -s "${API_BASE}/detail?period=${PERIOD}" | python3 -m json.tool | head -40
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "✅ 测试完成"
|
||||
echo "=========================================="
|
||||
|
||||
205
deploy/scripts/validate_config.py
Normal file
205
deploy/scripts/validate_config.py
Normal file
@@ -0,0 +1,205 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
环境配置验证脚本
|
||||
验证开发和生产环境的配置是否正确
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlparse
|
||||
|
||||
def validate_frontend_config(env_file):
|
||||
"""验证前端环境配置"""
|
||||
print(f"\n🌐 验证前端配置: {env_file}")
|
||||
|
||||
if not os.path.exists(env_file):
|
||||
print(f"❌ 配置文件不存在: {env_file}")
|
||||
return False
|
||||
|
||||
config = {}
|
||||
with open(env_file, 'r', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line and not line.startswith('#') and '=' in line:
|
||||
key, value = line.split('=', 1)
|
||||
config[key] = value
|
||||
|
||||
# 必需配置检查
|
||||
required_configs = [
|
||||
'VITE_APP_TITLE',
|
||||
'VITE_APP_ENV',
|
||||
'VITE_API_BASE_URL',
|
||||
'VITE_WS_BASE_URL'
|
||||
]
|
||||
|
||||
for key in required_configs:
|
||||
if key not in config:
|
||||
print(f"❌ 缺少必需配置: {key}")
|
||||
return False
|
||||
else:
|
||||
print(f"✅ {key} = {config[key]}")
|
||||
|
||||
# 环境特定验证
|
||||
env_type = config.get('VITE_APP_ENV', '')
|
||||
if env_type == 'development':
|
||||
# 开发环境验证
|
||||
if 'localhost' not in config.get('VITE_API_BASE_URL', ''):
|
||||
print("⚠️ 开发环境建议使用localhost")
|
||||
if config.get('VITE_ENABLE_DEVTOOLS') != 'true':
|
||||
print("⚠️ 开发环境建议启用开发工具")
|
||||
elif env_type == 'production':
|
||||
# 生产环境验证
|
||||
api_url = config.get('VITE_API_BASE_URL', '')
|
||||
if 'localhost' in api_url or '127.0.0.1' in api_url:
|
||||
print("❌ 生产环境不应使用localhost")
|
||||
return False
|
||||
if config.get('VITE_ENABLE_DEVTOOLS') == 'true':
|
||||
print("⚠️ 生产环境建议禁用开发工具")
|
||||
|
||||
print("✅ 前端配置验证通过")
|
||||
return True
|
||||
|
||||
def validate_backend_config(env_file):
|
||||
"""验证后端环境配置"""
|
||||
print(f"\n🚀 验证后端配置: {env_file}")
|
||||
|
||||
if not os.path.exists(env_file):
|
||||
print(f"❌ 配置文件不存在: {env_file}")
|
||||
return False
|
||||
|
||||
config = {}
|
||||
with open(env_file, 'r', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if line and not line.startswith('#') and '=' in line:
|
||||
key, value = line.split('=', 1)
|
||||
config[key] = value
|
||||
|
||||
# 必需配置检查
|
||||
required_configs = [
|
||||
'ENV',
|
||||
'SECRET_KEY',
|
||||
'DATABASE_URL',
|
||||
'REDIS_URL'
|
||||
]
|
||||
|
||||
for key in required_configs:
|
||||
if key not in config:
|
||||
print(f"❌ 缺少必需配置: {key}")
|
||||
return False
|
||||
else:
|
||||
# 敏感信息脱敏显示
|
||||
if 'PASSWORD' in key or 'SECRET' in key or 'TOKEN' in key or 'KEY' in key:
|
||||
value = config[key]
|
||||
if len(value) > 8:
|
||||
masked_value = value[:4] + '*' * (len(value) - 8) + value[-4:]
|
||||
else:
|
||||
masked_value = '*' * len(value)
|
||||
print(f"✅ {key} = {masked_value}")
|
||||
else:
|
||||
print(f"✅ {key} = {config[key]}")
|
||||
|
||||
# 安全性检查
|
||||
secret_key = config.get('SECRET_KEY', '')
|
||||
if secret_key in ['your-secret-key', 'your-secret-key-here', 'secret']:
|
||||
print("❌ 请设置安全的密钥,不要使用默认值")
|
||||
return False
|
||||
|
||||
if len(secret_key) < 32:
|
||||
print("⚠️ 建议使用至少32位的密钥")
|
||||
|
||||
# 数据库URL检查
|
||||
db_url = config.get('DATABASE_URL', '')
|
||||
if db_url:
|
||||
try:
|
||||
parsed = urlparse(db_url)
|
||||
print(f"📊 数据库信息: {parsed.scheme}://{parsed.hostname}:{parsed.port}/{parsed.path.lstrip('/')}")
|
||||
except Exception as e:
|
||||
print(f"⚠️ 数据库URL格式可能有误: {e}")
|
||||
|
||||
# 环境特定验证
|
||||
env_type = config.get('ENV', '')
|
||||
if env_type == 'development':
|
||||
if config.get('DEBUG') != 'true':
|
||||
print("⚠️ 开发环境建议启用调试模式")
|
||||
elif env_type == 'production':
|
||||
if config.get('DEBUG') == 'true':
|
||||
print("❌ 生产环境不应启用调试模式")
|
||||
return False
|
||||
|
||||
# 生产环境数据库检查
|
||||
if 'localhost' in db_url or '127.0.0.1' in db_url:
|
||||
print("⚠️ 生产环境建议使用容器内数据库")
|
||||
|
||||
print("✅ 后端配置验证通过")
|
||||
return True
|
||||
|
||||
def validate_docker_config():
|
||||
"""验证Docker配置"""
|
||||
print(f"\n🐳 验证Docker配置")
|
||||
|
||||
compose_files = [
|
||||
'docker-compose.dev.yml',
|
||||
'docker-compose.yml'
|
||||
]
|
||||
|
||||
for compose_file in compose_files:
|
||||
if os.path.exists(compose_file):
|
||||
print(f"✅ {compose_file} 存在")
|
||||
else:
|
||||
print(f"❌ {compose_file} 不存在")
|
||||
|
||||
return True
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("=== 考培练系统环境配置验证 ===")
|
||||
|
||||
# 检查工作目录
|
||||
if not os.path.exists('kaopeilian-frontend') or not os.path.exists('kaopeilian-backend'):
|
||||
print("❌ 请在项目根目录运行此脚本")
|
||||
sys.exit(1)
|
||||
|
||||
all_valid = True
|
||||
|
||||
# 验证前端配置
|
||||
frontend_configs = [
|
||||
'kaopeilian-frontend/.env.development',
|
||||
'kaopeilian-frontend/.env.production'
|
||||
]
|
||||
|
||||
for config_file in frontend_configs:
|
||||
if os.path.exists(config_file):
|
||||
if not validate_frontend_config(config_file):
|
||||
all_valid = False
|
||||
else:
|
||||
print(f"⚠️ 前端配置文件不存在: {config_file}")
|
||||
|
||||
# 验证后端配置
|
||||
backend_configs = [
|
||||
'kaopeilian-backend/.env.development',
|
||||
'kaopeilian-backend/.env.production'
|
||||
]
|
||||
|
||||
for config_file in backend_configs:
|
||||
if os.path.exists(config_file):
|
||||
if not validate_backend_config(config_file):
|
||||
all_valid = False
|
||||
else:
|
||||
print(f"⚠️ 后端配置文件不存在: {config_file}")
|
||||
|
||||
# 验证Docker配置
|
||||
validate_docker_config()
|
||||
|
||||
print(f"\n=== 验证结果 ===")
|
||||
if all_valid:
|
||||
print("✅ 所有配置验证通过")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("❌ 配置验证失败,请检查上述错误")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
164
deploy/scripts/webhook_handler.py
Executable file
164
deploy/scripts/webhook_handler.py
Executable file
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
GitHub Webhook处理器
|
||||
监听GitHub推送事件,自动触发部署
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import json
|
||||
import hmac
|
||||
import hashlib
|
||||
import logging
|
||||
from flask import Flask, request, jsonify
|
||||
import threading
|
||||
import time
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# 配置
|
||||
WEBHOOK_SECRET = "kaopeilian-webhook-secret-2025" # GitHub中配置的密钥
|
||||
PROJECT_DIR = "/root/aiedu"
|
||||
UPDATE_SCRIPT = "/root/aiedu/scripts/auto_update.sh"
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler('/var/log/kaopeilian_webhook.log'),
|
||||
logging.StreamHandler()
|
||||
]
|
||||
)
|
||||
|
||||
def verify_signature(payload_body, signature_header):
|
||||
"""验证GitHub Webhook签名"""
|
||||
if not signature_header:
|
||||
return False
|
||||
|
||||
hash_object = hmac.new(
|
||||
WEBHOOK_SECRET.encode('utf-8'),
|
||||
payload_body,
|
||||
hashlib.sha256
|
||||
)
|
||||
expected_signature = "sha256=" + hash_object.hexdigest()
|
||||
|
||||
return hmac.compare_digest(expected_signature, signature_header)
|
||||
|
||||
def run_update_async():
|
||||
"""异步执行更新脚本"""
|
||||
try:
|
||||
# 等待5秒再执行,避免GitHub推送过程中的竞争条件
|
||||
time.sleep(5)
|
||||
|
||||
result = subprocess.run(
|
||||
[UPDATE_SCRIPT],
|
||||
cwd=PROJECT_DIR,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=600 # 10分钟超时
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
logging.info(f"Update completed successfully: {result.stdout}")
|
||||
else:
|
||||
logging.error(f"Update failed: {result.stderr}")
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
logging.error("Update script timed out")
|
||||
except Exception as e:
|
||||
logging.error(f"Error running update script: {e}")
|
||||
|
||||
@app.route('/webhook', methods=['POST'])
|
||||
def github_webhook():
|
||||
"""处理GitHub Webhook请求"""
|
||||
|
||||
# 验证签名
|
||||
signature = request.headers.get('X-Hub-Signature-256')
|
||||
if not verify_signature(request.data, signature):
|
||||
logging.warning("Invalid webhook signature")
|
||||
return jsonify({"error": "Invalid signature"}), 403
|
||||
|
||||
# 解析请求
|
||||
try:
|
||||
payload = request.get_json()
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to parse JSON: {e}")
|
||||
return jsonify({"error": "Invalid JSON"}), 400
|
||||
|
||||
# 检查事件类型
|
||||
event_type = request.headers.get('X-GitHub-Event')
|
||||
if event_type != 'push':
|
||||
logging.info(f"Ignoring event type: {event_type}")
|
||||
return jsonify({"message": "Event ignored"}), 200
|
||||
|
||||
# 检查分支
|
||||
ref = payload.get('ref', '')
|
||||
if ref != 'refs/heads/production':
|
||||
logging.info(f"Ignoring push to branch: {ref}")
|
||||
return jsonify({"message": "Branch ignored"}), 200
|
||||
|
||||
# 获取提交信息
|
||||
commit_info = {
|
||||
'id': payload.get('after', 'unknown'),
|
||||
'message': payload.get('head_commit', {}).get('message', 'No message'),
|
||||
'author': payload.get('head_commit', {}).get('author', {}).get('name', 'Unknown'),
|
||||
'timestamp': payload.get('head_commit', {}).get('timestamp', 'Unknown')
|
||||
}
|
||||
|
||||
logging.info(f"Received push event: {commit_info}")
|
||||
|
||||
# 异步触发更新
|
||||
update_thread = threading.Thread(target=run_update_async)
|
||||
update_thread.daemon = True
|
||||
update_thread.start()
|
||||
|
||||
return jsonify({
|
||||
"message": "Update triggered successfully",
|
||||
"commit": commit_info
|
||||
}), 200
|
||||
|
||||
@app.route('/health', methods=['GET'])
|
||||
def health_check():
|
||||
"""健康检查端点"""
|
||||
return jsonify({
|
||||
"status": "healthy",
|
||||
"service": "kaopeilian-webhook",
|
||||
"timestamp": time.time()
|
||||
}), 200
|
||||
|
||||
@app.route('/status', methods=['GET'])
|
||||
def status():
|
||||
"""状态检查端点"""
|
||||
try:
|
||||
# 检查项目目录
|
||||
project_exists = os.path.exists(PROJECT_DIR)
|
||||
|
||||
# 检查更新脚本
|
||||
script_exists = os.path.exists(UPDATE_SCRIPT)
|
||||
script_executable = os.access(UPDATE_SCRIPT, os.X_OK) if script_exists else False
|
||||
|
||||
# 检查Docker服务
|
||||
docker_result = subprocess.run(['docker', 'compose', 'ps'],
|
||||
cwd=PROJECT_DIR, capture_output=True)
|
||||
docker_running = docker_result.returncode == 0
|
||||
|
||||
return jsonify({
|
||||
"status": "ok",
|
||||
"checks": {
|
||||
"project_directory": project_exists,
|
||||
"update_script_exists": script_exists,
|
||||
"update_script_executable": script_executable,
|
||||
"docker_compose_running": docker_running
|
||||
}
|
||||
}), 200
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
"status": "error",
|
||||
"error": str(e)
|
||||
}), 500
|
||||
|
||||
if __name__ == '__main__':
|
||||
logging.info("Starting GitHub Webhook handler...")
|
||||
app.run(host='0.0.0.0', port=9000, debug=False)
|
||||
57
deploy/scripts/启动资料预览功能.sh
Executable file
57
deploy/scripts/启动资料预览功能.sh
Executable file
@@ -0,0 +1,57 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 课程资料预览功能启动脚本
|
||||
# 用途:重建Docker镜像并启动服务
|
||||
|
||||
set -e
|
||||
|
||||
echo "========================================="
|
||||
echo "课程资料预览功能启动脚本"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
|
||||
# 切换到后端目录
|
||||
cd "$(dirname "$0")/kaopeilian-backend"
|
||||
|
||||
echo "步骤 1/4: 停止现有服务..."
|
||||
docker-compose -f docker-compose.dev.yml down
|
||||
|
||||
echo ""
|
||||
echo "步骤 2/4: 重建后端镜像(安装LibreOffice)..."
|
||||
echo "注意:首次构建可能需要5-10分钟,请耐心等待..."
|
||||
docker-compose -f docker-compose.dev.yml build backend
|
||||
|
||||
echo ""
|
||||
echo "步骤 3/4: 启动所有服务..."
|
||||
docker-compose -f docker-compose.dev.yml up -d
|
||||
|
||||
echo ""
|
||||
echo "步骤 4/4: 等待服务启动(30秒)..."
|
||||
sleep 30
|
||||
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "服务启动完成!"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
echo "📋 服务信息:"
|
||||
echo " - 后端API: http://localhost:8000"
|
||||
echo " - 前端页面: http://localhost:3001"
|
||||
echo " - 课程详情: http://localhost:3001/trainee/course-detail?id=1"
|
||||
echo ""
|
||||
echo "🔍 检查LibreOffice安装状态:"
|
||||
echo " curl http://localhost:8000/api/v1/preview/check-converter"
|
||||
echo ""
|
||||
echo "📝 测试建议:"
|
||||
echo " 1. 先在课程管理中上传各种格式的测试文件"
|
||||
echo " 2. 访问课程详情页查看资料列表"
|
||||
echo " 3. 点击不同类型的文件测试预览功能"
|
||||
echo " 4. 特别测试Office文档的转换预览"
|
||||
echo ""
|
||||
echo "📖 详细测试指南:"
|
||||
echo " 查看文件: kaopeilian-frontend/课程资料预览功能测试指南.md"
|
||||
echo ""
|
||||
echo "🔧 查看服务日志:"
|
||||
echo " docker-compose -f docker-compose.dev.yml logs -f backend"
|
||||
echo ""
|
||||
|
||||
92
deploy/scripts/测试资料预览功能.sh
Executable file
92
deploy/scripts/测试资料预览功能.sh
Executable file
@@ -0,0 +1,92 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 课程资料预览功能测试脚本
|
||||
# 用途:快速测试API接口是否正常
|
||||
|
||||
set -e
|
||||
|
||||
BASE_URL="http://localhost:8000"
|
||||
COURSE_ID=1
|
||||
|
||||
echo "========================================="
|
||||
echo "课程资料预览功能测试"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
|
||||
# 测试1: 检查后端服务是否启动
|
||||
echo "测试 1/4: 检查后端服务..."
|
||||
if curl -s "${BASE_URL}/health" > /dev/null 2>&1; then
|
||||
echo "✅ 后端服务正常"
|
||||
else
|
||||
echo "❌ 后端服务未启动,请先运行启动脚本"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# 测试2: 检查LibreOffice安装
|
||||
echo "测试 2/4: 检查LibreOffice安装状态..."
|
||||
CONVERTER_STATUS=$(curl -s "${BASE_URL}/api/v1/preview/check-converter" || echo "{}")
|
||||
echo "$CONVERTER_STATUS" | python3 -m json.tool 2>/dev/null || echo "$CONVERTER_STATUS"
|
||||
|
||||
if echo "$CONVERTER_STATUS" | grep -q '"libreoffice_installed": true'; then
|
||||
echo "✅ LibreOffice安装成功"
|
||||
else
|
||||
echo "⚠️ LibreOffice未安装或检测失败"
|
||||
echo " 请检查Docker镜像是否正确构建"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# 测试3: 获取课程资料列表
|
||||
echo "测试 3/4: 获取课程资料列表..."
|
||||
MATERIALS=$(curl -s "${BASE_URL}/api/v1/courses/${COURSE_ID}/materials" || echo "{}")
|
||||
echo "$MATERIALS" | python3 -m json.tool 2>/dev/null || echo "$MATERIALS"
|
||||
|
||||
MATERIAL_COUNT=$(echo "$MATERIALS" | grep -o '"id"' | wc -l)
|
||||
if [ "$MATERIAL_COUNT" -gt 0 ]; then
|
||||
echo "✅ 找到 ${MATERIAL_COUNT} 个资料"
|
||||
else
|
||||
echo "⚠️ 该课程暂无资料"
|
||||
echo " 请先在课程管理中上传测试文件"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# 测试4: 测试预览接口(如果有资料)
|
||||
if [ "$MATERIAL_COUNT" -gt 0 ]; then
|
||||
echo "测试 4/4: 测试资料预览接口..."
|
||||
|
||||
# 提取第一个资料的ID
|
||||
MATERIAL_ID=$(echo "$MATERIALS" | grep -o '"id": *[0-9]*' | head -1 | grep -o '[0-9]*')
|
||||
|
||||
if [ -n "$MATERIAL_ID" ]; then
|
||||
echo " 测试资料ID: ${MATERIAL_ID}"
|
||||
PREVIEW_INFO=$(curl -s "${BASE_URL}/api/v1/preview/material/${MATERIAL_ID}" || echo "{}")
|
||||
echo "$PREVIEW_INFO" | python3 -m json.tool 2>/dev/null || echo "$PREVIEW_INFO"
|
||||
|
||||
if echo "$PREVIEW_INFO" | grep -q '"preview_type"'; then
|
||||
echo "✅ 预览接口正常"
|
||||
else
|
||||
echo "❌ 预览接口异常"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "测试 4/4: 跳过(无资料可测试)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "测试完成!"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
echo "📝 下一步:"
|
||||
echo " 1. 如果LibreOffice未安装,请重新构建Docker镜像"
|
||||
echo " 2. 如果无资料,请访问管理后台上传测试文件"
|
||||
echo " 3. 在浏览器中访问课程详情页进行完整测试"
|
||||
echo " http://localhost:3001/trainee/course-detail?id=${COURSE_ID}"
|
||||
echo ""
|
||||
echo "📖 详细测试指南:"
|
||||
echo " kaopeilian-frontend/课程资料预览功能测试指南.md"
|
||||
echo ""
|
||||
|
||||
Reference in New Issue
Block a user