feat: 生产化改造(服务端安装程序/PWA/Docker+HTTPS)并修复侧边栏点击无反应、SPA 静态托管 dotfiles 两个缺陷

- 修复 web-admin 与 desktop-client 侧边栏点击无反应:el-menu 的 index 是路径,不能当路由名使用
- 修复 Web 产物目录含点号目录(如 .cache)时 res.sendFile 因 dotfiles:ignore 返回 500
- 后端生产模式:静态托管 Web 端 + SPA 回退、helmet、限流(通用/登录)、trust proxy、
  /healthz 与 /readyz、请求耗时日志、生产环境变量强校验、首启动自动迁移与创建管理员、PID 文件
- Web 管理端 PWA:manifest + service worker + 图标,可在浏览器安装为应用
- 新增服务端 + Web 管理端 Windows 安装程序(内置 Node 运行时、启停与服务脚本、开机自启、卸载保留数据)
- 新增 Docker/Compose/Nginx 部署样例与 docs/production.md
- 新增 UI 回归测试:Web 端 Electron 驱动真实点击 7 项、客户端冒烟 11 项(含侧边栏点击与离线回退)
This commit is contained in:
LoveChengke committed 2026-09-12 00:20:21 +08:00
1 parent 6e4c7c1050
commit 5545ef6f55
46 files changed
+3020 -152

No files matched your search

+106
View File
@@ -0,0 +1,106 @@
# ---------------------------------------------------------------------------
# 班级小助手 · Nginx 反向代理样例(HTTPS + WebSocket)
#
# 作用:把 https://class.example.com 反向代理到本机 4000 端口的服务端,
# 同时处理 WebSocket 升级、真实 IP 透传、静态资源缓存与安全响应头。
#
# 使用:
# 1) 把本文件放到 /etc/nginx/conf.d/classhelper.conf(或 sites-available 并做软链)
# 2) 修改 server_name 与证书路径
# 3) nginx -t && systemctl reload nginx
# 4) 服务端 .env 中设置 TRUST_PROXY=1(否则限流按代理 IP 统计)
# 以及 CORS_ORIGIN=https://class.example.com
# ---------------------------------------------------------------------------
# 上游:本机运行的服务端(Windows 安装包 / Docker / 手动启动均适用)
upstream classhelper_backend {
server 127.0.0.1:4000;
keepalive 32;
}
# HTTP -> HTTPS 跳转
server {
listen 80;
listen [::]:80;
server_name class.example.com;
# Let's Encrypt 校验目录(如使用 certbot --webroot)
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name class.example.com;
# ---------------------------------------------------------------- 证书
ssl_certificate /etc/nginx/certs/class.example.com.crt;
ssl_certificate_key /etc/nginx/certs/class.example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
# ---------------------------------------------------------------- 基础
client_max_body_size 8m;
charset utf-8;
access_log /var/log/nginx/classhelper.access.log;
error_log /var/log/nginx/classhelper.error.log warn;
# 安全响应头(应用层已用 helmet 设置,这里作为兜底)
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options SAMEORIGIN always;
add_header Referrer-Policy no-referrer always;
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
# gzip(静态资源与接口响应)
gzip on;
gzip_comp_level 5;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript application/manifest+json image/svg+xml;
# ---------------------------------------------------------------- WebSocket(Socket.IO)
location /socket.io/ {
proxy_pass http://classhelper_backend;
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_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
}
# ---------------------------------------------------------------- 静态资源(带哈希,长缓存)
location /assets/ {
proxy_pass http://classhelper_backend;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# ---------------------------------------------------------------- 接口与页面
location / {
proxy_pass http://classhelper_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_set_header Connection "";
proxy_read_timeout 120s;
proxy_connect_timeout 10s;
}
}