feat: 初始化考培练系统项目

- 从服务器拉取完整代码
- 按框架规范整理项目结构
- 配置 Drone CI 测试环境部署
- 包含后端(FastAPI)、前端(Vue3)、管理端

技术栈: Vue3 + TypeScript + FastAPI + MySQL
This commit is contained in:
111
2026-01-24 19:33:28 +08:00
commit 998211c483
1197 changed files with 228429 additions and 0 deletions

View 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

View 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

View 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

View 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

View 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

View 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
View 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
View 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
View 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";
}
}

View 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";
}
}

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

View 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";
}
}

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

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

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

View 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 "=== 环境检查完成 ==="

View 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
View 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"

View 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 "=== 诊断完成 ==="

View 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网络配置"

View 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 "=== 重启完成 ==="

View 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 ""

View 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
View 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 "=== 服务启动完成 ==="

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

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

View 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\"}'"

View 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 "=========================================="

View 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
View 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)

View 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 ""

View 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 ""