Files
SoundGame/docs/deploy.md
T
xsl ec248e18da ops(deploy): 一键部署脚本 tools/deploy.sh + deploy.md 同步 no-cache 缓存策略
- tools/deploy.sh:git pull --ff-only → rsync web/ → /var/www/soundgame
  (排除开发用文件)→ chown www-data → 线上冒烟(大厅/子站/301/Range)。
  已在部署服务器实测全流程 PASS。
- docs/deploy.md:nginx 配置段同步为线上现行 no-cache 版本(原文档仍是
  expires+public,照抄会把「部署后旧 JS 在浏览器滞留 24h」的坑重新引回),
  并注明一键脚本入口。背景:2026-09-07 引擎句尾截断修复已正确部署,
  但旧 audio.js 被 max-age=86400 public 缓存,用户 24h 内仍执行旧代码。
2026-09-07 18:03:39 +08:00

6.2 KiB
Raw Blame History

SoundGame — 公网部署

线上地址:https://game.xiangsilian.com/

个人简历站 https://xiangsilian.com/ 首页「深入了解」与页脚有入口。本机 Nginx 同时托管这两个站点:根域名仍是简历站,游戏走独立子域名,互不覆盖。

方案摘要

公网只托管静态文件,不启动 web/server/server.js。

原因:用户系统是家庭局域网玩具级认证(PIN 明文、token 永不过期、无速率限制), 见 web/server/README.md。游戏本身不依赖 API——未登录时设置和进度写在浏览器 localStorage。大厅在 https://*.xiangsilian.com 下会隐藏登录面板 (web/index.html 里的 PUBLIC_STATIC)。本地 ./web/serve.sh 仍显示登录。

手机浏览器
    │ HTTPS
    ▼
Nginx 80/443
    ├── xiangsilian.com     → /var/www/xiangsilian   (简历站,已有)
    └── game.xiangsilian.com → /var/www/soundgame     (本游戏)

证书是同一张 Let’s Encrypt(SAN:xiangsilian.com、www、game),webroot 验证目录 /var/www/letsencrypt。certbot.timer 负责续期。

本机路径

用途 路径
源码 /home/ubuntu/SoundGame/web/
线上静态根 /var/www/soundgame/(www-data 可读;家目录 750,Nginx 读不了源码目录)
Nginx 站点 /etc/nginx/sites-available/soundgame.conf → sites-enabled/
简历站静态根 /var/www/xiangsilian/
简历站源码 /home/ubuntu/xiangsilian/

未发布:harness/、*/gen/、*/test/、server/、template/,以及 *.py / *.md / *.sh。Android 工程、tools/、研究文档也不在线上。

更新游戏文件

上述流程已封装为一键脚本 tools/deploy.sh(git pull --ff-only + rsync + chown + 冒烟验证),在部署服务器上执行:

bash tools/deploy.sh

等价的手动步骤(源码有改动后,在本机执行):

sudo rsync -a --delete \
  --exclude harness/ --exclude gen/ --exclude test/ \
  --exclude server/ --exclude template/ \
  --exclude '*.py' --exclude '*.md' --exclude '*.sh' \
  /home/ubuntu/SoundGame/web/ /var/www/soundgame/
sudo chown -R www-data:www-data /var/www/soundgame

静态文件即时生效,不必 reload Nginx;配合全站 no-cache 缓存策略,用户下次 打开页面即可拿到新文件(2026-09-07 之前 js/css 曾是 24h 强缓存,部署后旧 JS 在用户浏览器里滞留一天,表现为"改了却没生效")。改了 soundgame.conf 才需要:

sudo nginx -t && sudo systemctl reload nginx

从零再搭一遍(本服务器已完成,仅作记录)

前置:DNSPod 增加 A 记录 game → 本机公网 IP(当前 118.89.233.55)。 xiangsilian.com / www 的 Nginx + 证书应已存在。

  1. 按上一节 rsync 到 /var/www/soundgame。

  2. 先写 仅 HTTP 的 server_name game.xiangsilian.com,包含 location /.well-known/acme-challenge/(root /var/www/letsencrypt), nginx -t 后 reload。此时还没有该主机名的证书,不能先开 443。

  3. 扩证书(会改同一张 live 证书,现站 HTTPS 不停):

    sudo certbot certonly --webroot -w /var/www/letsencrypt \
      -d xiangsilian.com -d www.xiangsilian.com -d game.xiangsilian.com \
      --expand --non-interactive --agree-tos
    

    扩完后确认 /etc/letsencrypt/renewal/xiangsilian.com.conf 的 [[webroot_map]] 含三个域名(certbot 有时只留下新加的那条,缺了续期会掉名)。

  4. 换成完整配置(HTTP 301 → HTTPS,443 指 /var/www/soundgame),reload。 当前线上配置见下一节。

Nginx 配置(/etc/nginx/sites-available/soundgame.conf)

# game.xiangsilian.com — SoundGame HTML5 static site
server {
    listen 80;
    listen [::]:80;
    server_name game.xiangsilian.com;

    location /.well-known/acme-challenge/ {
        root /var/www/letsencrypt;
    }

    location / {
        return 301 https://game.xiangsilian.com$request_uri;
    }
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name game.xiangsilian.com;

    ssl_certificate /etc/letsencrypt/live/xiangsilian.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/xiangsilian.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    root /var/www/soundgame;
    index index.html;

    # 全站 no-cache:可缓存但必须回源验证(ETag→304)。
    # 2026-09-07 教训:此前 js/css 是 max-age=86400 public,引擎修复部署后
    # 用户浏览器 24h 内仍执行旧 audio.js,句尾截断"修不好"。静态小站回源
    # 验证成本可忽略,换取每次部署立即生效。
    add_header Cache-Control "no-cache";
    add_header X-Content-Type-Options nosniff;
    add_header Referrer-Policy strict-origin-when-cross-origin;

    location ~ /\. {
        deny all;
    }

    location / {
        try_files $uri $uri/ =404;
    }

    location ~* \.(ogg|m4a|mp3|wav|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
        add_header Cache-Control "no-cache";
        add_header X-Content-Type-Options nosniff;
        access_log off;
    }

    location ~* \.(js|css)$ {
        add_header Cache-Control "no-cache";
        add_header X-Content-Type-Options nosniff;
    }
}

音频走 Nginx 原生 Range(Accept-Ranges),不要反代到 Node。系统 mime.types 已有 audio/ogg 与 audio/x-m4a。

以后若要开放用户系统

不要把 server.js 绑到 0.0.0.0。至少做到:监听 127.0.0.1、Nginx 只反代 /api/、加 limit_req、PIN 改哈希、token 设过期。在此之前保持静态-only。

冒烟

curl -sI https://game.xiangsilian.com/                  # 200
curl -sI http://game.xiangsilian.com/                   # 301 → https
curl -sI https://game.xiangsilian.com/rpg/              # 200
curl -sI https://game.xiangsilian.com/api/login         # 404
curl -sI -H 'Range: bytes=0-1023' \
  https://game.xiangsilian.com/rpg/audio/bgm/boss.ogg   # 206
curl -sI https://xiangsilian.com/                       # 200,简历站仍在

真机:手机浏览器打开大厅 → 点进一款游戏 → 「轻触开始」后应出声(建议耳机)。 iOS 用 m4a,Android 用 ogg。