feat(web): 部署方式由 Docker 改为 Linux 原生站点(nginx + systemd)

需求变更:Web 服务端不再使用 Docker,改为直接运行在 Linux 上的网站。

- 移除 deploy/docker-compose.yml、web-server/Dockerfile、
  web-server/admin/Dockerfile、web-server/.dockerignore
- 新增 deploy/install.sh:建服务用户与目录 → 装运行期依赖 → 迁移
  → 注册 systemd 服务 → 配置 nginx 站点 → 健康检查,一键完成
- 新增 deploy/systemd/opendance-api.service:ProtectSystem=strict 等
  安全加固,ReadWritePaths 限定为上传目录
- 新增 deploy/nginx/opendance.conf:同站点托管管理端静态资源 +
  反代 /api /health /static,client_max_body_size 512m,SPA 回退
- 新增 deploy/uninstall.sh:默认保留数据,--purge 才删除
- 新增配置项 HOST(AppConfig.bindHost):原生部署下 Node 只监听
  127.0.0.1,不直接对外暴露,实测外部 IP 不可达
- 同步更新 contracts/rest-api.yaml、DSH.md、project.yaml、README.txt、
  tasks/008、tasks/010 与发布打包脚本
This commit is contained in:
2026-09-14 22:42:56 +08:00
parent 0c133986b4
commit 75498db1c0
18 changed files with 516 additions and 315 deletions
+93
View File
@@ -0,0 +1,93 @@
# ============================================================
# OpenDance Studio —— nginx 站点配置(Linux 原生部署)
#
# 一个站点同时提供:
# / 管理端静态站点(React 构建产物)
# /api/ NestJS API 反向代理
# /health 健康检查反向代理
# /static/ 上传文件(模型 / 音频 / 关键点),由 API 提供
# /contracts/ 契约快照(只读,方便另两端查阅)
#
# 安装位置:/etc/nginx/sites-available/opendance.conf
# (并软链到 /etc/nginx/sites-enabled/)
# ============================================================
server {
listen 80;
listen [::]:80;
server_name __SERVER_NAME__;
# 上传上限:单个 VRM / FBX 可达 512 MiB,必须与后端 UPLOAD_MAX_BYTES 一致
client_max_body_size 512m;
client_body_timeout 300s;
# 管理端静态站点
root __APP_DIR__/admin;
index index.html;
charset utf-8;
# ---- 管理端 SPA ----
location / {
try_files $uri $uri/ /index.html;
}
# Vite 产物带内容哈希,可长缓存
location /assets/ {
expires 30d;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}
# ---- API 反向代理 ----
location /api/ {
proxy_pass http://__API_HOST__:__API_PORT__;
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 30s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
proxy_request_buffering off;
}
location = /health {
proxy_pass http://__API_HOST__:__API_PORT__/health;
proxy_http_version 1.1;
proxy_set_header Host $host;
access_log off;
}
# ---- 上传文件 ----
location /static/ {
proxy_pass http://__API_HOST__:__API_PORT__;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
expires 7d;
add_header Cache-Control "public";
}
# ---- 契约快照(只读)----
location /contracts/ {
alias __APP_DIR__/contracts/;
autoindex on;
default_type text/plain;
}
# 隐藏文件一律拒绝
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
gzip on;
gzip_types text/plain text/css application/json application/javascript
application/x-ndjson text/xml application/xml image/svg+xml;
gzip_min_length 1024;
}