feat: v0.10.0 生产环境优化 — HTTPS、前端生产构建、安全加固

- feat(deploy): 前端多阶段构建 (vite build + nginx:alpine),移除 Vite 开发模式
- feat(deploy): OpenResty HTTPS 配置 (SSL + HSTS + 安全头)
- fix(ws): WebSocket 路由添加 /api 前缀,修正前后端路径不匹配
- security: SSH AutoAddPolicy → WarningPolicy
- security: CORS 来源环境变量化 (CORS_ORIGINS)
- security: 限流器使用 X-Forwarded-For 真实客户端 IP
- perf(db): 数据库连接池配置 (pool_size=20, max_overflow=40)
- refactor: 移除硬编码 URL/IP (NTP、域名、微信代理),改为环境变量
- chore: 更新 .env.example 模板,补充新增配置项
- chore: 清理 .reasonix/、scripts/、guide.md 无用文件
- docs: 更新 CLAUDE.md 至 v0.10.0,补充生产架构文档

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 19:00:08 +08:00
parent b2c20ec43d
commit fcfa5af614
17 changed files with 340 additions and 791 deletions
+29 -14
View File
@@ -65,6 +65,9 @@ CASDOOR_ORG_NAME=your_organization
# 应用名称
CASDOOR_APP_NAME=h3c-onu-ms
# Casdoor 回调地址(部署后改为实际域名)
CASDOOR_REDIRECT_URL=
# ============================================
# SSH连接配置
# ============================================
@@ -98,20 +101,35 @@ HISTORY_RETENTION=90
BATCH_CHECK_SIZE=100
# ============================================
# 前端配置
# 前端 & CORS 配置
# ============================================
# API基础URL (前端访问后端地址)
VITE_API_BASE_URL=http://localhost:8000
# 前端访问地址
FRONTEND_URL=https://your-domain.com
# Casdoor前端配置
VITE_CASDOOR_ENDPOINT=https://casdoor.example.com
VITE_CASDOOR_CLIENT_ID=your_casdoor_client_id
VITE_CASDOOR_ORG_NAME=your_organization
VITE_CASDOOR_APP_NAME=h3c-onu-ms
# CORS允许的域名 (逗号分隔)
CORS_ORIGINS=http://localhost:8080,http://localhost:5173
# 应用标题
VITE_APP_TITLE=H3C ONU设备管理系统
# ============================================
# NTP 同步配置
# ============================================
# NTP 旧服务器 IP
NTP_OLD_SERVER=172.16.0.254
# NTP 新服务器 IP
NTP_NEW_SERVER=172.16.1.252
# ============================================
# iMC 网管 API 配置(ONU 远程重启/光功率查询)
# ============================================
IMC_API_URL=
IMC_API_USERNAME=
IMC_API_PASSWORD=
# 本地认证不需要 SSL 验证
IMC_API_VERIFY_SSL=false
IMC_CONNECT_TIMEOUT=5
IMC_READ_TIMEOUT=20
# ============================================
# 日志配置
@@ -154,9 +172,6 @@ METRICS_PORT=8000
# 安全配置
# ============================================
# CORS允许的域名 (逗号分隔)
CORS_ORIGINS=http://localhost:8080,http://localhost:5173
# 速率限制配置
RATE_LIMIT_PER_MINUTE=60
RATE_LIMIT_PER_HOUR=1000
@@ -175,7 +190,7 @@ WECHAT_AGENTID=
WECHAT_TOKEN=
WECHAT_ENCODING_AES_KEY=
WECHAT_USE_PROXY=True
WECHAT_PROXY_API_URL=https://api.v6ole.top
WECHAT_PROXY_API_URL=
# 备份保留天数
BACKUP_RETENTION=30
+12 -11
View File
@@ -16,15 +16,23 @@ services:
- CASDOOR_CERTIFICATE=${CASDOOR_CERTIFICATE}
- CASDOOR_ORG_NAME=${CASDOOR_ORG_NAME}
- CASDOOR_APP_NAME=${CASDOOR_APP_NAME}
- CASDOOR_REDIRECT_URL=${CASDOOR_REDIRECT_URL:-}
- SECRET_KEY=${SECRET_KEY}
- DEBUG=${DEBUG:-false}
- CORS_ORIGINS=${CORS_ORIGINS:-}
- FRONTEND_URL=${FRONTEND_URL:-https://onu.dhdx.fun}
- NTP_OLD_SERVER=${NTP_OLD_SERVER:-172.16.0.254}
- NTP_NEW_SERVER=${NTP_NEW_SERVER:-172.16.1.252}
- IMC_API_URL=${IMC_API_URL:-}
- IMC_API_USERNAME=${IMC_API_USERNAME:-}
- IMC_API_PASSWORD=${IMC_API_PASSWORD:-}
- WECHAT_CORPID=${WECHAT_CORPID:-}
- WECHAT_CORPSECRET=${WECHAT_CORPSECRET:-}
- WECHAT_AGENTID=${WECHAT_AGENTID:-}
- WECHAT_TOKEN=${WECHAT_TOKEN:-}
- WECHAT_ENCODING_AES_KEY=${WECHAT_ENCODING_AES_KEY:-}
- WECHAT_USE_PROXY=${WECHAT_USE_PROXY:-True}
- WECHAT_PROXY_API_URL=${WECHAT_PROXY_API_URL:-https://api.v6ole.top}
- WECHAT_PROXY_API_URL=${WECHAT_PROXY_API_URL:-}
volumes:
- ../backend/logs:/app/logs
- ../backend/static:/app/static
@@ -96,26 +104,19 @@ services:
- backend
restart: unless-stopped
# 前端应用
# 前端应用(生产模式:nginx 静态文件服务)
frontend:
build:
context: ../frontend
dockerfile: Dockerfile
container_name: h3c-onu-ms-frontend
ports:
- "5173:5173"
environment:
- VITE_API_BASE_URL=${VITE_API_BASE_URL:-http://localhost:8001}
- VITE_API_PROXY_TARGET=http://backend:8000
- VITE_CASDOOR_ENDPOINT=${VITE_CASDOOR_ENDPOINT}
- VITE_CASDOOR_CLIENT_ID=${VITE_CASDOOR_CLIENT_ID}
- VITE_CASDOOR_ORG_NAME=${VITE_CASDOOR_ORG_NAME}
- VITE_CASDOOR_APP_NAME=${VITE_CASDOOR_APP_NAME}
- "18062:80"
depends_on:
- backend
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:5173"]
test: ["CMD", "wget", "-qO-", "http://localhost:80/health"]
interval: 30s
timeout: 10s
retries: 3
+54
View File
@@ -0,0 +1,54 @@
server {
listen 80;
listen 443 ssl http2;
server_name onu.dhdx.fun;
index index.html;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Real-IP $remote_addr;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
access_log /www/sites/onu.dhdx.fun/log/access.log main;
error_log /www/sites/onu.dhdx.fun/log/error.log;
location ^~ /.well-known/acme-challenge {
allow all;
root /usr/share/nginx/html;
}
if ($scheme = http) {
return 301 https://$host$request_uri;
}
ssl_certificate /www/sites/onu.dhdx.fun/ssl/fullchain.pem;
ssl_certificate_key /www/sites/onu.dhdx.fun/ssl/privkey.pem;
ssl_protocols TLSv1.3 TLSv1.2 TLSv1.1 TLSv1;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:!aNULL:!eNULL:!EXPORT:!DSS:!DES:!RC4:!3DES:!MD5:!PSK:!KRB5:!SRP:!CAMELLIA:!SEED;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;
proxy_set_header X-Forwarded-Proto https;
add_header Strict-Transport-Security "max-age=31536000";
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# 前端(生产 nginx 容器,端口 18062
location / {
proxy_pass http://127.0.0.1:18062;
}
# 后端 APIfrp 隧道 → 本机后端 /api/ws/dashboard WebSocket
location /api/ {
proxy_pass http://127.0.0.1:18060;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_connect_timeout 60s;
}
}