Qbao

Qbao 环境与网络地图(开发 / 内测 / 生产)

面向非网络专业读者的环境说明书:几个网址分别是什么、给谁用、改代码先上哪、为什么有时”看不到新版本”。 按隐私铁律,本文所有真实地址一律用占位符:{DOMAIN} 生产域名、{BETA_HOST} 内测域名、{HK_IP} 服务器 IP(香港)、{HOST_ROOT} 服务器部署父目录、{PROD_ROOT}/{BETA_ROOT} 两套部署根、{SSH_USER} 服务器用户。 真实值对照表只保存在本机 gitignored 的 local/ENV.md(机制原理见 ARCHITECTURE.md,部署/发布纪律见 DEVELOPMENT_FLOW.md,服务器细节见 DEPLOY.md,游戏 QA 见 GAMES.md)。

1. 一句话概括

Qbao 有三层”环境”,改动一律从左到右流动:

本机写代码(L0) → 内测环境验收(L1) → 生产上线(L2) → 推送 GitHub

2. 概念卡片(用生活类比)

名词 是什么 类比 在本项目的角色
域名 人类可读的网址 门牌号 {DOMAIN}(生产)、{BETA_HOST}(内测)
DNS 把域名翻译成服务器 IP 电话簿 由 Cloudflare 托管,指向香港服务器
Cloudflare 全球 CDN + DNS 快递总站 DNS 管理;{DOMAIN} 走代理(加速 + 边缘 HTTPS);{BETA_HOST} 仅 DNS 直连
Caddy 收下 HTTPS 请求,就地直出网页或转给业务程序 前台柜员 + 仓库管理员一肩挑 香港服务器 {HK_IP} 上的 Caddy:TLS 加密 + 静态直出 + API 反代,两个域名各一个 site 块
Node API 处理业务逻辑的程序 业务员 同一台机器上的 Express 服务(生产 :3000 / 内测 :3100)
PostgreSQL 数据库 账本 生产库 qbao / 内测库 qbao_beta(同一个数据库软件里的两本账)
缓存 浏览器/CDN 暂存文件以加速 复印留底 生产静态资源保留 7 天(改版靠 ?v= 刷新);内测不缓存
WebSocket 双向长连接 对讲机 狼人杀房间实时通信(:3011)
HTTPS 加密的网页传输 密封信封 生产与内测都走 HTTPS,证书由 Caddy 自动申请续期

3. 拓扑(数据流图)

【生产 · 真实用户】
  用户 ──https://{DOMAIN}──> Cloudflare(CDN/代理)──> 香港 {HK_IP}
                                                    Caddy(TLS 终结 + 静态直出 + 反代)
                                             ├── 静态 {PROD_ROOT}/app(7 天缓存)
                                             ├── /api ··· → Node :3000 → PostgreSQL 库 qbao
                                             └── 狼人杀 ··· → Node :3011(房间在内存,不落库)

【内测 · 测试/内测人员】
  内测者 ──https://{BETA_HOST}──>(DNS 直连,不过 CDN)香港 {HK_IP}
                                                    Caddy(另一个 site 块)
                                             ├── 静态 {BETA_ROOT}/app(不缓存)
                                             └── /api ··· → Node :3100 → PostgreSQL 库 qbao_beta

关键点:生产与内测在同一台香港服务器上,靠 Caddy 的两个域名配置块分成两套「静态目录 + API + 账本」,两边互不碰面。

2026-09-10 起服务全部由香港单机提供:此前是「香港中转 + 大陆源站」两层(大陆机房会拦截未备案域名,才需要那套绕行方案), 源站已于同日退役清理。现在香港主机直接就是源站,链路更短,也不再需要任何「回源改写 / 路由暗号」机制。

4. 环境矩阵

  L0 本机开发 L1 内测环境 L2 生产
入口 http://127.0.0.1:8124/games/ 等本地地址 https://{BETA_HOST} https://{DOMAIN}
谁在用 开发时自己 你 + 受邀内测同学 全部真实用户
数据库 无(单测用假库) qbao_beta(可随时删了重建) qbao(不可测试)
API 无(单测内嵌) 香港 :3100 香港 :3000
静态目录 仓库 app/public/games / 构建产物 {BETA_ROOT}/app {PROD_ROOT}/app
缓存 无 不缓存(改完刷新即见) 静态 7 天(改版需 ?v= 版本号)
测试动作 只跑单测与页面 注册小号/对局/兑换/清库 全都可以 只读巡检(金丝雀账号)
上线顺序 ① ②(默认验收点) ③(验收通过后)

5. 隔离纪律(”测试”与”用户使用”如何分开)

  1. 数据隔离:测试账号只存在于 qbao_beta;生产库禁止任何测试写入(对局/兑换/领奖/删改)。
  2. 服务隔离:内测 API(:3100)与生产 API(:3000)是两个进程、两本账本;改坏内测只影响内测。
  3. 代码同步顺序:新代码先同步到内测目录并重启内测服务 → 冒烟 → 验收 → 才同步生产目录。
  4. 数据库迁移顺序:同一迁移文件先在内测库跑、验收后在生产库跑(每本账本独立记录已应用版本)。
  5. 自动化 QA 钩子门禁:游戏页 ?qa=1 调试钩子只在 {BETA_HOST} 与 localhost 生效,生产域名一律忽略(代码层强制,见 docs/GAMES.md 第六节)。
  6. 生产只读巡检:每次生产上线后,用金丝雀账号跑 health/登录/GET 断言,不产生任何写操作。
  7. 缓存纪律:生产静态缓存 7 天 → 网页文件里引用 js/css 必须带版本号参数(?v=…)才能让老浏览器拿到新版本;内测不缓存所以没有这个问题。
  8. 内测服全员管理员(AUTO_ADMIN=1):内测环境注册即管理员,便于测试管理功能;配套防互害保护——管理员账号不可被其他管理员封禁、重置密码或改变角色(服务端强制),角色变更只能由后台脚本完成。正式环境严禁开启该开关,正式服管理员只能由后台脚本授予。
  9. 数据互导通道:正式服/内测服「设置 → 数据 → 本地备份」下载的 JSON 可在两个环境间互相导入(目标环境先用同一账号注册登录,再「上传恢复」);备份仅包含学习数据,管理员身份与封禁状态属于账号属性,不随备份迁移,任何环境都无法借备份转移解除封禁或获得管理员。

6. 一次改动的完整旅程(新流程)

以”调整弹珠兑换规则”为例:

1) 本机改代码 + 补/改单测 → DoD 全绿(L0)
2) 运行同步脚本:server 代码 → 内测目录;执行内测库迁移;重启内测 API(L1)
3) 内测自动冒烟:注册→登录→兑换→对局,断言写进 qbao_beta 且 qbao 零变化
4) 你在 https://{BETA_HOST} 上点一遍验收(或发给内测同学)
5) 验收通过 → 同样步骤执行到生产(同步、迁移、重启)→ 生产只读巡检
6) 你明确说"测试通过" → git push + tag + Release

任何一步失败就回到上一步修,生产上永远只有验收过的版本。

7. 内测入口清单(给同学/朋友)

场景 路径
网页/手机浏览器 打开 https://{BETA_HOST},开放注册(内测网址只发给受邀者;页面带”内测环境”角标防混淆)
微信里打开 直接分享内测网址(浏览器内核打开即可玩)
当 App 用 手机浏览器「添加到主屏幕」,全屏体验
游戏自动化冒烟 {BETA_HOST} 或 localhost 下游戏页加 ?qa=1&token=…(生产域名自动失效)
桌面客户端 安装包走既有 beta 渠道 + 应用内「服务器地址」填 https://{BETA_HOST}
手机壳 App 内测 下载页/下载中心「内测版(可选)」自选下载(com.qbao.beta 与正式版共存安装),构建与发布见 §7.1
内测出问题 qbao_beta 一键重建(命令见 docs/DEPLOY.md 4B),不影响任何真实用户
数据迁移(双向) 正式服与内测服之间用「本地备份」JSON 互导(目标环境注册登录同账号→上传恢复);管理员/封禁等账号属性不随备份迁移(见 §5.8-9)

7.1 手机壳”内测版”(已落地 2026-09-08)

8. 常见问题(FAQ)

Q1 生产为什么只有一个网址了? 2026-09-10 迁港后,生产统一走 https://{DOMAIN}(Cloudflare 代理)。此前的 http://{ORIGIN_IP} IP 直连兜底入口随大陆源站退役一并取消。

Q2 我改了代码,为什么网站上还是旧的? 三种可能:(1) 只改了本机,没同步到服务器;(2) 生产静态缓存 7 天——网页引用的 js/css 必须换版本参数 ?v= 才会让浏览器拉新版(或强刷 Ctrl+F5);(3) API 改动后没有重启对应服务。

Q3 为什么改数据库要”先内测后生产”? 迁移文件要在内测库先跑一遍验证无误(幂等、可重试),再去生产库执行;两边各自记账,互不干扰,任何一边失败都可单独重试。

Q4 内测环境会不会被真实用户访问到? 内测网址只有受邀者知道;且它连的是独立账本,即使被访问也只是多一个测试账号,不影响生产。真担心可加访问限制(预留开关)。

Q5 为什么跑测试要开 VPN? VPN 只用于两件事:访问 GitHub 推送代码、登录 Cloudflare 控制台改 DNS。日常打开内测/生产网址不需要 VPN。

Q6 游戏里的”QA 钩子”是什么? 游戏移植时内置的自动化测试入口(模拟登录、自动点按钮),用于冒烟;出于安全它只在 {BETA_HOST}/localhost 放行,生产域名下即使拼了参数也会被忽略。

Q7 内存这么小,跑两套服务会不会崩? 生产与内测 API 是同一台香港服务器上的两个 Node 进程(另有 Caddy、狼人杀与 PostgreSQL)。已预留降载开关(内测不开 AI 任务);若内存吃紧,最坏回退方案见 docs/DEPLOY.md 4B 的”预案”小节。

Q8 内测环境怎么知道当前是哪个版本? 内测页随每次同步即时更新,无缓存;页面顶部有”内测环境”角标。版本号以 server/package.json 与 CHANGELOG 为准。

Q9 服务器挂了怎么办? 服务与数据库同在一台机器,主机故障即整站不可用。数据侧靠服务器每日 04:00 自动双库备份(保留 30 天),另有迁港时点快照与源站退役归档保存在本机 gitignored 的 local/backups/。注意:大陆源站已退役,没有”切回源站”这条回滚路径。

9. 术语表

术语 解释
L0 / L1 / L2 三层环境:本机 / 内测 / 生产
源站 真正运行服务的服务器;本项目即那台香港主机(同时承担 TLS、静态托管与 API)
Caddy 本项目的 Web 服务器:自动 HTTPS + 静态直出 + 反向代理,取代了早期的 nginx + 中转两层结构
TTL DNS 记录存活时间(改动域名记录后等待生效的时长)
金丝雀账号 只读巡检用的专用账号(类比”矿工的金丝雀”先探路)
幂等 同一操作执行多次结果一致(迁移脚本可安全重跑)
CAS 读-改-写带版本比对,防并发覆盖
singlefile 前端构建产物:一个 index.html 内嵌全部 js/css

10. 修订记录