merge latest master
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled

This commit is contained in:
2026-10-04 04:34:48 +01:00
30 changed files with 1185 additions and 118 deletions
@@ -63,7 +63,7 @@ npm run check:pingora-release-readiness
`check:pingora-gateway-smoke` 会临时启动 mock `api-server`、mock SpacetimeDB、mock Gitea 和 `pingora-gateway`,覆盖精确主站 SPA fallback、大小写与尾部斜杠兼容、同前缀未知路径真实 404、后台静态路由、HTML / 普通静态资源 `no-cache`、Vite 指纹静态资源 immutable 缓存、静态 `ETag` / `Last-Modified` 与 `304` 协商缓存、静态 `HEAD` 响应、静态 Range、静态 access log method/path/status 对账、gzip 最小长度、小响应不压缩、图片资源不压缩、大响应压缩、ACME、TLS 直连、HTTP/2 ALPN、HTTP 到 HTTPS 重定向、内部路由拒绝、shadow probe、API 代理头(`Host` / `X-Forwarded-Host` / `X-Forwarded-Proto` / `X-Real-IP` / `X-Forwarded-For`)、Gitea Host 整站转发、请求体上限、429 接流保护、上游断连 / 超时 JSON 错误、维护模式、维护模式不拦截 Gitea Host 和 SpacetimeDB WebSocket Upgrade,并复用 `check-pingora-direct-live.mjs` 对临时 HTTPS / HTTP redirect / WSS subscribe 入口做 live smoke。该本地 fixture 会让首页同时引用普通静态资源和 Vite 指纹静态资源,direct live JSON 必须确认指纹资源 GET / HEAD / `Range: bytes=0-0` 以及 access log method/path/status 证据,避免正式直连前只证明普通静态读取。排查失败时可追加 `-- --verbose` 输出网关 stderr / stdout;已确认二进制无需重编时可追加 `-- --skip-build`。
`check:nginx-spa-routes` 从 `appPageRoutes.ts` 的 `STAGE_ROUTE_ENTRIES` / `APP_RUNTIME_ROUTES`、`appRoutes.tsx` 的精确路由判断和兼容恢复路径 `/creation/rpg/agent` 提取当前主站 SPA allowlist,确认生产、开发和容器三套 Nginx 模板集合一致,并验证大小写、尾部斜杠和 `/creation/not-exist`、`/runtime/not-exist`、`/puzzle/not-exist` 等未知反例。
`check:nginx-spa-routes` 从 `appPageRoutes.ts` 的 `STAGE_ROUTE_ENTRIES` 与 `APP_PREFIX_ROUTE_ENTRIES`、`appRoutes.tsx` 的精确路由判断和兼容恢复路径 `/creation/rpg/agent` 提取当前主站 SPA allowlist,确认生产、开发和容器三套 Nginx 模板集合一致,并验证大小写、尾部斜杠、前缀路由的「前缀 + 恰好一个路径段」锚定形状(收银台深链 `/pay/<checkoutToken>` 必须整体回退 `index.html`,只放行裸前缀会让真实链接落到默认 location 变 404)和 `/creation/not-exist`、`/runtime/not-exist`、`/puzzle/not-exist` 等未知反例。该脚本自带正/反用例(`node --test scripts/check-nginx-spa-routes.test.mjs`,由 `npm run check:nginx-spa-routes` 一起执行),防止有人把前缀路由改回精确匹配。
`check:pingora-route-parity` 会先执行同一 Nginx SPA 路由门禁,再读取 `deploy/pingora/nginx-route-parity.matrix.json`,静态确认生产 / 开发 Nginx 模板、Pingora Rust 路由 allowlist / 单测和本文档都覆盖同一组核心路由,并做**反向覆盖**(模板里的每条 `location` 都必须被矩阵声明)。`cargo test -p pingora-gateway --manifest-path server-rs/Cargo.toml matches_nginx_route_parity_matrix` 会读取同一份矩阵,逐条断言 `classify_path` 的路由结果、body limit 和接流保护分组。`check:nginx-spa-routes` 与 `check:pingora-route-parity` 已串进 `npm run lint`(因此 `check:repository-ci`、CI 与 pre-push 都会执行),接线本身由 `check:production-ops` 的 guardrail 锁定。
@@ -535,7 +535,8 @@ dev 根盘空间在安装后曾接近满盘;2026-06-17 进入 canary 前已清
| `/v1/database/{db}/subscribe`、`/v1/identity*` | 转发到 SpacetimeDB,保留 WebSocket Upgrade 头。 |
| `/__genarrative_pingora/healthz` | 仅在携带 `X-Genarrative-Pingora-Probe` 且匹配配置 token 时返回 shadow JSON,否则 404。 |
| `/v1/*`、`/generated-*`、`/healthz*`、`/readyz*` | 返回 404,保持生产公网不暴露口径。 |
| 主站 SPA allowlist | 只对 `/`、`/components`、`/creation`、`/design-system`、`/editor/canvas`、`/games`、`/games/detail`、`/games/mine`、`/games/play`、`/games/publish`、`/profile`、`/project` 失败回退 `/index.html`(集合与前端路由源、Nginx 三份模板逐条一致,由 `npm run check:pingora-route-parity` 与 `cargo test -p pingora-gateway matches_nginx_route_parity_matrix` 比对);匹配大小写不敏感并允许一个尾部斜杠,HTML 默认 `no-cache`。`/games/game_<32 位十六进制 id>/…` 是发行网关路由,不在 SPA allowlist 内。 |
| 主站 SPA allowlist | 只对 `/`、`/components`、`/creation`、`/design-system`、`/editor/canvas`、`/games`、`/games/detail`、`/games/mine`、`/games/play`、`/games/publish`、`/pay`、`/profile`、`/profile/payment`、`/project` 失败回退 `/index.html`(集合与前端路由源、Nginx 三份模板逐条一致,由 `npm run check:pingora-route-parity` 与 `cargo test -p pingora-gateway matches_nginx_route_parity_matrix` 比对);匹配大小写不敏感并允许一个尾部斜杠,HTML 默认 `no-cache`。`/games/game_<32 位十六进制 id>/…` 是发行网关路由,不在 SPA allowlist 内。 |
| 主站 SPA 前缀路由 | 收银台深链 `/pay/<checkoutToken>` 走 `MAIN_SPA_PREFIX_PATHS`:只放行「前缀 + 恰好一个路径段」(大小写不敏感),裸前缀由上面的精确集合负责,多段路径与 `/payment/x` 这类前缀同名邻居都不进 SPA fallback;Nginx 三份模板同口径写成 `location ~* "^/pay/[^/]+/?$"`,由矩阵的 `pay_checkout_spa_fallback` 用例固定。 |
| 其它 Web 路径 | 只读取真实静态文件或目录 index,缺失时返回真实 404;`/creation/not-exist`、`/runtime/not-exist`、`/puzzle/not-exist` 不进入 SPA fallback。 |
SPA allowlist 里属于游戏分发入口的深链(游戏目录 / 详情 / 游玩 / 我的 / 发布深链:`/games`、`/games/detail`、`/games/play`、`/games/mine`、`/games/publish`)与 Nginx 三份模板同口径;Pingora 侧由路由对照矩阵的 `games_spa_fallback` 用例与 `cargo test -p pingora-gateway matches_nginx_route_parity_matrix` 逐条断言。根路径 `/` 精确回退 `/index.html`(Nginx 在 `location = /` 里用 `try_files /index.html =404;`,不带 `$uri`),由矩阵的 `web_root_spa` 用例固定。发行网关路径 `/games/game_<32 位十六进制 id>/…` 不走 SPA,见下一节的对照说明。