eeb101458a
Project CI / Backend tests (pull_request) Failing after 34s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m27s
Project CI / Native shell tests (pull_request) Failing after 2m46s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 5m58s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 4m23s
Project CI / AI game creator shell web tests (pull_request) Failing after 45s
Project CI / Frontend tests (pull_request) Successful in 2m24s
Project CI / Repository checks (pull_request) Failing after 1m1s
# Conflicts: # apps/ai-game-creator-shell/src-tauri/src/game_distribution_publish.rs # apps/ai-game-creator-shell/src/components/game-distribution/GameDistributionPublishPanel.tsx # apps/ai-game-creator-shell/tests/gameDistributionPublishPanel.test.tsx # docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md # package.json # packages/shared/src/contracts/gameDistribution.ts # scripts/check-game-distribution-dto-parity.mjs # server-rs/crates/api-server/src/modules/game_distribution.rs # server-rs/crates/module-game-distribution/src/errors.rs # server-rs/crates/module-game-distribution/src/lib.rs # server-rs/crates/shared-contracts/src/game_distribution.rs # server-rs/crates/spacetime-client/src/active.rs # server-rs/crates/spacetime-client/src/active/mapper.rs # server-rs/crates/spacetime-client/src/active/mapper/game_distribution.rs # server-rs/crates/spacetime-client/src/game_distribution.rs # server-rs/crates/spacetime-client/src/module_bindings.rs # server-rs/crates/spacetime-client/src/module_bindings/game_distribution_game_snapshot_type.rs # server-rs/crates/spacetime-client/src/module_bindings/game_distribution_game_type.rs # server-rs/crates/spacetime-module/src/game_distribution.rs # server-rs/crates/spacetime-module/src/migration.rs # src/components/game-distribution/GameDetailPage.tsx # src/components/game-distribution/GameDistributionPages.test.tsx # src/components/game-distribution/GamePlayPage.tsx # src/components/platform-entry/PlatformEntryActiveFlowShell.tsx # src/services/gameDistributionClient.test.ts # src/services/gameDistributionClient.ts
277 lines
9.6 KiB
Plaintext
277 lines
9.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/game-distribution/play-sessions/<token>/…` 是 sandbox iframe
|
||
# 的 src,包内相对资源沿同一前缀解析。它与通用 `/api` 同口径代理到 api-server(大小上限、
|
||
# 限流、超时、维护判断都保持一致),唯一差别是清空 Cookie:播放会话不读账号凭证,而
|
||
# api-server 播放网关对带平台 refresh Cookie 的请求返回 403(纵深防御保留)。
|
||
# 前缀 location 必须写 `^~`:不加时正则 location `~ ^/api(?:/|$)` 会先命中,Cookie 又会被转发。
|
||
# 只匹配带尾斜杠的前缀:`/play-sessions/<token>` 与 `/play-sessions/<token>/…` 都落在该前缀内,
|
||
# 裸 `/api/game-distribution/play-sessions` 仍走通用 `/api`。该前缀下 api-server 只注册 GET
|
||
# 入口与包内资源;创建会话是 `POST /api/game-distribution/games/<gameId>/play-session`,不在此前缀下。
|
||
location ^~ /api/game-distribution/play-sessions/ {
|
||
default_type application/json;
|
||
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;
|
||
proxy_set_header Cookie "";
|
||
}
|
||
|
||
# 临时兼容主站仍在使用的 /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|creators|creators/connections|games|games/compare|games/detail|games/lineage|games/mine|games/play|games/publish|games/theme|games/themes)/?$" {
|
||
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;
|
||
}
|
||
}
|