ad0430fcd2
- 现状:只把 `/pay`、`/profile/payment` 加进 allowlist 只能让 check:nginx-spa-routes 变绿; payment.rs 生成的 checkoutUrl 是 `/pay/<checkoutToken>`,深链仍落默认 location 的 try_files → 404。同一批漂移里 check:pingora-route-parity 也是红的(Pingora MAIN_SPA_PATHS 缺 /pay、/profile/payment),只是被 lint 链里先失败的门禁掩盖,修一条要跑到链尾 - 真相源:src/routing/activeAppPageRoutes.ts 新增 APP_PREFIX_ROUTE_ENTRIES ('/pay' → payment-checkout),resolveSelectionStageFromPath 改用它 - 门禁:scripts/check-nginx-spa-routes.mjs 要求三份模板都有锚定前缀 location `location ~* "^/pay/[^/]+/?$"`(裸前缀仍由精确 location 负责;前缀 location 必须镜像精确 location 的维护闸与 try_files 回退);新增 scripts/check-nginx-spa-routes.test.mjs 正/反用例 (把前缀写成精确匹配或过宽裸前缀都会红),由 npm run check:nginx-spa-routes 一起执行; check-pingora-route-parity 新增 MAIN_SPA_PREFIX_PATHS 与前端前缀路由的逐条比对 - 模板:deploy/nginx/genarrative.conf、deploy/nginx/genarrative-dev-http.conf、 deploy/container/nginx.conf 各加一条锚定前缀 location - Pingora:MAIN_SPA_PATHS 补 /pay、/profile/payment;新增 MAIN_SPA_PREFIX_PATHS 与 is_main_spa_prefix_path(大小写不敏感,只认「前缀 + 恰好一段」),矩阵新增 pay_checkout_spa_fallback 用例,并给网关补一条前缀正/反单测 - 文档:Pingora 试点文档的路由表与门禁说明、deploy/nginx/README 与本地开发/生产运维文档 同步前缀路由口径与线上 curl 复验方式 - 本地实跑:node --test scripts/check-nginx-spa-routes.test.mjs(4 passed)、 node scripts/check-nginx-spa-routes.mjs(OK,14 SPA routes / 1 prefix routes / 3 templates)、 npm run check:pingora-route-parity(OK,25 routes)、 cargo test -p pingora-gateway -- pay_checkout_deep_link matches_nginx_route_parity_matrix(2 passed)
175 lines
5.9 KiB
Nginx Configuration File
175 lines
5.9 KiB
Nginx Configuration File
worker_processes auto;
|
||
|
||
events {
|
||
worker_connections 768;
|
||
}
|
||
|
||
http {
|
||
include /etc/nginx/mime.types;
|
||
default_type application/octet-stream;
|
||
|
||
log_format genarrative_upstream
|
||
'$remote_addr - $remote_user [$time_local] "$request" '
|
||
'$status $body_bytes_sent "$http_referer" "$http_user_agent" '
|
||
'request_time=$request_time upstream_connect_time=$upstream_connect_time '
|
||
'upstream_header_time=$upstream_header_time upstream_response_time=$upstream_response_time '
|
||
'upstream_status=$upstream_status request_id=$request_id';
|
||
|
||
upstream genarrative_api {
|
||
server api-server:8082;
|
||
keepalive 64;
|
||
}
|
||
|
||
limit_conn_zone $binary_remote_addr zone=genarrative_api_conn:10m;
|
||
limit_req_zone $binary_remote_addr zone=genarrative_api_rps:10m rate=300r/s;
|
||
limit_req_zone $binary_remote_addr zone=genarrative_admin_rps:10m rate=30r/s;
|
||
|
||
sendfile on;
|
||
keepalive_timeout 65;
|
||
|
||
gzip on;
|
||
gzip_vary on;
|
||
gzip_proxied any;
|
||
gzip_comp_level 5;
|
||
gzip_min_length 1024;
|
||
gzip_types
|
||
text/plain
|
||
text/css
|
||
text/javascript
|
||
application/javascript
|
||
application/json
|
||
application/xml
|
||
application/xml+rss
|
||
image/svg+xml;
|
||
|
||
server {
|
||
listen 80;
|
||
server_name _;
|
||
|
||
access_log /var/log/nginx/genarrative.access.log genarrative_upstream;
|
||
error_log /var/log/nginx/genarrative.error.log warn;
|
||
limit_conn_status 429;
|
||
limit_conn_log_level warn;
|
||
limit_req_status 429;
|
||
limit_req_log_level warn;
|
||
|
||
root /srv/genarrative/web;
|
||
index index.html;
|
||
|
||
location ^~ /admin/api/ {
|
||
default_type application/json;
|
||
limit_conn genarrative_api_conn 64;
|
||
limit_req zone=genarrative_admin_rps burst=16 nodelay;
|
||
|
||
proxy_pass http://genarrative_api/admin/api/;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Connection "";
|
||
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 X-Request-Id $request_id;
|
||
}
|
||
|
||
location = /admin {
|
||
return 301 /admin/;
|
||
}
|
||
|
||
location ^~ /admin/assets/ {
|
||
try_files $uri =404;
|
||
}
|
||
|
||
location ^~ /admin/ {
|
||
try_files $uri $uri/ /admin/index.html;
|
||
}
|
||
|
||
location ^~ /assets/ {
|
||
try_files $uri =404;
|
||
}
|
||
|
||
|
||
location ~ ^/api(?:/|$) {
|
||
default_type application/json;
|
||
# 中文注释:创作接口会携带参考图 Data URL,游戏发行包 PUT 更大,Nginx 只负责放行到 api-server;
|
||
# 真实大小限制仍由路由 DefaultBodyLimit(发行包 200 MiB + 1 KiB)和业务字节校验负责。
|
||
client_max_body_size 210m;
|
||
limit_conn genarrative_api_conn 64;
|
||
limit_req zone=genarrative_api_rps burst=64 nodelay;
|
||
|
||
proxy_pass http://genarrative_api;
|
||
proxy_http_version 1.1;
|
||
proxy_buffering off;
|
||
proxy_read_timeout 3600s;
|
||
proxy_send_timeout 3600s;
|
||
add_header X-Accel-Buffering no always;
|
||
proxy_set_header Connection "";
|
||
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 X-Forwarded-Host $host;
|
||
proxy_set_header X-Request-Id $request_id;
|
||
}
|
||
|
||
location ~ ^/(generated-|healthz|readyz) {
|
||
return 404;
|
||
}
|
||
|
||
location ~ ^/v1/database/[^/]+/subscribe$ {
|
||
proxy_pass http://spacetimedb:3101;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection "Upgrade";
|
||
proxy_set_header Host $host;
|
||
proxy_read_timeout 3600s;
|
||
}
|
||
|
||
location ^~ /v1/identity {
|
||
proxy_pass http://spacetimedb:3101;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection "Upgrade";
|
||
proxy_set_header Host $host;
|
||
}
|
||
|
||
location ^~ /v1/ {
|
||
return 404;
|
||
}
|
||
|
||
# 平台同源路径发行入口:/games/<gameId>/ 与 /games/<gameId>/<asset> 映射到
|
||
# api-server 发行网关。游戏文档跑在 iframe sandbox="allow-scripts" 的不透明来源里,
|
||
# 离开页面即随 iframe 卸载,因此不再要求独立发行域名与通配证书。
|
||
location ~ "^/games/(?<game_id>game_[0-9a-f]{32})(?<game_path>/.*)?$" {
|
||
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 X-Request-Id $request_id;
|
||
proxy_set_header Cookie "";
|
||
proxy_pass http://genarrative_api/api/game-distribution/releases/$game_id$game_path;
|
||
proxy_read_timeout 60s;
|
||
proxy_send_timeout 60s;
|
||
}
|
||
|
||
# BEGIN GENARRATIVE MAIN SPA ROUTES
|
||
location = / {
|
||
try_files /index.html =404;
|
||
}
|
||
|
||
location ~* "^/(?:creation|editor/canvas|pay|profile|profile/payment|project|components|design-system|games|games/detail|games/mine|games/play|games/publish)/?$" {
|
||
try_files $uri /index.html =404;
|
||
}
|
||
|
||
# 收银台深链 `/pay/<checkoutToken>`:只放行裸前缀会让真实收银台链接落到默认 location 变 404。
|
||
location ~* "^/pay/[^/]+/?$" {
|
||
try_files $uri /index.html =404;
|
||
}
|
||
# END GENARRATIVE MAIN SPA ROUTES
|
||
|
||
location / {
|
||
try_files $uri $uri/ =404;
|
||
}
|
||
}
|
||
}
|