5795a07a7d
- `deploy/nginx/genarrative.conf:237`:allowlist 交替项插入 `games/lineage`(放在 `games/detail` 与 `games/mine` 之间,保持既有字典序) - `deploy/nginx/genarrative-dev-http.conf:209`:同上 - `deploy/container/nginx.conf:160`:同上(8 空格缩进,`~*` + `/?$` 形态不变) - 原因:M3 新增网页端 SPA 路由 `/games/lineage`(`src/routing/activeAppPageRoutes.ts:18`)后没同步三份 nginx 模板,`npm run check:nginx-spa-routes` 以 9 条断言失败(每份模板 3 条:缺少路由 / 未匹配完整路径 / 未允许大小写差异与尾部斜杠);CI run 3387 的 `Repository checks`(3m36s)即因此红,属本分支引入(master 尖端 `6c9566058` 8/8 绿) - 只补路由项,未改门禁脚本、未放宽任何断言、未动 `apps/**`、`server-rs/**`、`src/**` - 验证:`npm run check:nginx-spa-routes` → OK(15 SPA routes / 1 prefix routes / 3 Nginx templates)
243 lines
7.6 KiB
Plaintext
243 lines
7.6 KiB
Plaintext
# 开发服无域名时使用的 HTTP 入口,只允许用于 DEPLOY_TARGET=development。
|
||
# 没有域名时,将 SERVER_NAME 填为开发机 IP 或临时主机名。
|
||
# 生产 release 仍必须使用 genarrative.conf 的 HTTPS 配置。
|
||
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 127.0.0.1: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;
|
||
|
||
# 维护期间允许真实 TCP 内网来源继续访问整站;不信任请求头伪造的客户端地址。
|
||
geo $remote_addr $genarrative_internal_client {
|
||
default 0;
|
||
127.0.0.0/8 1;
|
||
10.0.0.0/8 1;
|
||
172.16.0.0/12 1;
|
||
192.168.0.0/16 1;
|
||
169.254.0.0/16 1;
|
||
::1 1;
|
||
fc00::/7 1;
|
||
fe80::/10 1;
|
||
}
|
||
|
||
server {
|
||
listen 80;
|
||
server_name genarrative.example.com;
|
||
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;
|
||
|
||
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;
|
||
|
||
# __GENARRATIVE_BROTLI_DIRECTIVES__
|
||
|
||
root /srv/genarrative/web;
|
||
index index.html;
|
||
|
||
include /etc/nginx/snippets/genarrative-maintenance.conf;
|
||
|
||
location ^~ /admin/api/ {
|
||
default_type application/json;
|
||
limit_conn genarrative_api_conn 64;
|
||
limit_req zone=genarrative_admin_rps burst=16 nodelay;
|
||
|
||
if ($genarrative_maintenance) {
|
||
return 503 '{"ok":false,"error":{"code":"MAINTENANCE","message":"服务维护中"}}';
|
||
}
|
||
|
||
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 {
|
||
error_page 503 /maintenance.html;
|
||
|
||
if ($genarrative_maintenance) {
|
||
return 503;
|
||
}
|
||
|
||
return 301 /admin/;
|
||
}
|
||
|
||
location ^~ /admin/assets/ {
|
||
error_page 503 /maintenance.html;
|
||
|
||
if ($genarrative_maintenance) {
|
||
return 503;
|
||
}
|
||
|
||
try_files $uri =404;
|
||
}
|
||
|
||
location ^~ /admin/ {
|
||
error_page 503 /maintenance.html;
|
||
|
||
if ($genarrative_maintenance) {
|
||
return 503;
|
||
}
|
||
|
||
try_files $uri $uri/ /admin/index.html;
|
||
}
|
||
|
||
location ^~ /assets/ {
|
||
try_files $uri =404;
|
||
}
|
||
|
||
|
||
# 临时兼容主站仍在使用的 /api/* HTTP facade;前端完成 SpacetimeDB SDK 迁移后删除。
|
||
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;
|
||
|
||
if ($genarrative_maintenance) {
|
||
return 503 '{"ok":false,"error":{"code":"MAINTENANCE","message":"服务维护中"}}';
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
# 仅开放前端 SpacetimeDB SDK 运行所需的最小公网路由。
|
||
location ~ ^/v1/database/[^/]+/subscribe$ {
|
||
if ($genarrative_maintenance) {
|
||
return 503;
|
||
}
|
||
|
||
proxy_pass http://127.0.0.1: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 {
|
||
if ($genarrative_maintenance) {
|
||
return 503;
|
||
}
|
||
|
||
proxy_pass http://127.0.0.1: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 = / {
|
||
error_page 503 /maintenance.html;
|
||
|
||
if ($genarrative_maintenance) {
|
||
return 503;
|
||
}
|
||
|
||
try_files /index.html =404;
|
||
}
|
||
|
||
location ~* "^/(?:creation|editor/canvas|pay|profile|profile/payment|project|components|design-system|games|games/detail|games/lineage|games/mine|games/play|games/publish)/?$" {
|
||
error_page 503 /maintenance.html;
|
||
|
||
if ($genarrative_maintenance) {
|
||
return 503;
|
||
}
|
||
|
||
try_files $uri /index.html =404;
|
||
}
|
||
|
||
# 收银台深链 `/pay/<checkoutToken>`:token 由前端从最后一个路径段读取(payment.rs 生成该链接),
|
||
# 只放行裸前缀会让真实收银台链接落到默认 location 变 404;这里只放行「/pay/ + 恰好一个路径段」。
|
||
location ~* "^/pay/[^/]+/?$" {
|
||
error_page 503 /maintenance.html;
|
||
|
||
if ($genarrative_maintenance) {
|
||
return 503;
|
||
}
|
||
|
||
try_files $uri /index.html =404;
|
||
}
|
||
# END GENARRATIVE MAIN SPA ROUTES
|
||
|
||
location / {
|
||
error_page 503 /maintenance.html;
|
||
error_page 404 /404.html;
|
||
|
||
if ($genarrative_maintenance) {
|
||
return 503;
|
||
}
|
||
|
||
try_files $uri $uri/ =404;
|
||
}
|
||
}
|