diff --git a/.codex/skills/spacetimedb-cli/SKILL.md b/.codex/skills/spacetimedb-cli/SKILL.md index 68132d822..a3d892458 100644 --- a/.codex/skills/spacetimedb-cli/SKILL.md +++ b/.codex/skills/spacetimedb-cli/SKILL.md @@ -1,6 +1,6 @@ --- name: spacetimedb-cli -description: SpacetimeDB 2.5 CLI reference for Genarrative. Use for spacetime build, publish, generate, call, sql, logs, server management, local dev, explicit server targeting, version checks, and remote runtime verification. +description: SpacetimeDB 2.6 CLI reference for Genarrative. Use for spacetime build, publish, generate, call, sql, logs, server management, local dev, explicit server targeting, version checks, and remote runtime verification. --- # SpacetimeDB CLI @@ -61,7 +61,7 @@ spacetime describe my-db table users --server http://127.0.0.1:3101 --json # Reducer/procedure calls. Arguments are positional JSON values. spacetime call --server http://127.0.0.1:3101 my-db my_reducer '"value"' '123' -# 2.5 accepts hex strings for Identity arguments without full JSON tuple syntax. +# 2.5+ accepts hex strings for Identity arguments without full JSON tuple syntax. spacetime call --server http://127.0.0.1:3101 my-db reducer_needing_identity 0xabc123... # Subscribe from CLI @@ -102,7 +102,7 @@ curl -fsS http://127.0.0.1:3101/v1/ping | Flag | Description | |------|-------------| | `--server`, `-s` | Target server nickname, host, or URL | -| `--yes`, `-y` | Non-interactive prompt skipping; in 2.5 prefer scoped values | +| `--yes`, `-y` | Non-interactive prompt skipping; in 2.6 use scoped values | | `--delete-data`, `-c` | Publish data policy: `always`, `on-conflict`, or `never` | | `--module-path`, `-p` | Module project path | | `--bin-path`, `-b` | Publish/generate from compiled wasm | @@ -146,6 +146,6 @@ pid="$(systemctl show spacetimedb.service -p MainPID --value)" ## Notes -- Procedure calls are stable in 2.5; module HTTP handlers/webhooks, unstable view features, and RLS remain behind unstable gates per release notes. -- 2.5 fixes `publish --delete-data` config fallback so `spacetime.json` can provide the database name. +- Procedure calls remain stable in 2.6; module HTTP handlers/webhooks and RLS capabilities still require their documented gates. +- 2.5 fixed `publish --delete-data` config fallback; 2.6 keeps that behavior and improves CLI binary distribution. - Genarrative scripts should pass `--server` or `--server-url` explicitly instead of relying on CLI defaults. diff --git a/.codex/skills/spacetimedb-concepts/SKILL.md b/.codex/skills/spacetimedb-concepts/SKILL.md index abb665f9b..f43b06519 100644 --- a/.codex/skills/spacetimedb-concepts/SKILL.md +++ b/.codex/skills/spacetimedb-concepts/SKILL.md @@ -1,6 +1,6 @@ --- name: spacetimedb-concepts -description: Understand SpacetimeDB 2.5 architecture, reducer/procedure/table/view semantics, schema evolution, subscriptions, identity, and Genarrative-specific backend boundaries. Use when designing or reviewing SpacetimeDB-backed features. +description: Understand SpacetimeDB 2.6 architecture, reducer/procedure/table/view semantics, schema evolution, subscriptions, identity, and Genarrative-specific backend boundaries. Use when designing or reviewing SpacetimeDB-backed features. --- # SpacetimeDB Core Concepts @@ -20,7 +20,7 @@ SpacetimeDB is a relational database that also executes application logic in upl 1. **Reducers are transactional**: they do not return data to callers. Read through subscriptions, read models, views, or BFF endpoints. 2. **Reducers are deterministic**: no filesystem, network, wall-clock, or external RNG. Use `ctx.timestamp`, `ctx.rng()` / `ctx.random()`, and tables. -3. **Procedures are stable in 2.5**: they can use explicit transactions and outgoing HTTP via `ctx.http`. +3. **Procedures are stable in 2.6**: they can use explicit transactions and outgoing HTTP via `ctx.http`. 4. **Identity comes from context**: use `ctx.sender()` or language equivalent for authorization. Never trust identity passed as an argument. 5. **Auto-increment IDs are not ordering guarantees**: gaps are normal. Use timestamps or explicit sequence columns for ordering. 6. **Schema changes need migration discipline**: existing Genarrative table fields must be appended with defaults; update migration code, table catalog, generated bindings, and run `npm run check:spacetime-schema`. @@ -44,25 +44,25 @@ Reducers are deterministic transactional functions. They are the primary client- ## Procedures -Procedures are stable in 2.5. They can be scheduled, can open explicit transactions with `with_tx` / `try_with_tx`, and can use outgoing HTTP (`ctx.http`). +Procedures are stable in 2.6. They can be scheduled, can open explicit transactions with `with_tx` / `try_with_tx`, and can use outgoing HTTP (`ctx.http`). Genarrative default: keep external provider protocols in `platform-*` and orchestration in `api-server` unless a task explicitly moves a workflow into a module procedure. -Module HTTP handlers/webhooks, unstable view features, and RLS `client_visibility_filter` remain gated behind unstable according to the 2.5 release notes. +Module HTTP handlers/webhooks and RLS `client_visibility_filter` remain subject to their documented gates in 2.6. ## Views -Views expose computed read-only data. In 2.4.1 Rust and TypeScript gained primary key support for procedural views; in 2.5 C# gained the same. Clients can receive `OnUpdate` events when subscribed to such views with primary keys. Ensure the view never returns duplicate primary keys, because that can fail view refresh and roll back the triggering transaction. +Views expose computed read-only data. SpacetimeDB 2.6 supports primary keys on procedural views in Rust, TypeScript, and C#. Clients can receive `OnUpdate` events when subscribed to such views with primary keys. Ensure the view never returns duplicate primary keys, because that can fail view refresh and roll back the triggering transaction. ## Event Tables Event tables broadcast reducer/procedure-specific facts to subscribers and must be subscribed explicitly. They are excluded from `subscribe_to_all_tables()`. -2.5 adds broader layout-altering automigrations for event tables, including column removal, reordering, and type changes that regular tables reject. This relaxed migration behavior is for event-only tables, not persistent tables. +2.6 supports broader layout-altering automigrations for event tables, including column removal, reordering, and type changes that regular tables reject. This relaxed migration behavior is for event-only tables, not persistent tables. Event-table primary keys and constraints are transaction-scoped. They can reject duplicate event rows within one transaction, but event rows are not retained in client cache, so clients observe event tables through insert callbacks only. Do not design Genarrative event tables around `OnUpdate` / `on_update` / `onUpdate`; use a persistent table or a primary-keyed procedural view when update callbacks are required. -Official 2.4.1/2.5 release notes document primary-key-backed update callbacks for procedural views, not event tables. +Official 2.4.1 through 2.6 release notes document primary-key-backed update callbacks for procedural views, not event tables. ## Subscriptions @@ -78,7 +78,7 @@ Best practices: - Avoid overlapping queries that duplicate row delivery. - Use indexes for subscribed filters. -## 2.2.0 to 2.5.0 Delta +## 2.2.0 to 2.6.0 Delta Genarrative introduced SpacetimeDB around 2.2.0. Important changes since then: @@ -87,6 +87,7 @@ Genarrative introduced SpacetimeDB around 2.2.0. Important changes since then: - **2.4.0**: unstable module HTTP handlers/webhooks, faster synchronous WASM reducer runtime, commitlog resume truncation fix for silent data loss risk, better commitlog decode context, V8 heap metrics for procedure workers, JS execution-time billing regression reverted. - **2.4.1**: Rust and TypeScript procedural views can declare primary keys, enabling `OnUpdate` events for subscribed views; fixed index schema from ST tables. - **2.5.0**: procedures are stable, C# procedural views gain primary keys, event tables allow broader layout-altering automigrations, BTreeSet storage makes row insertion deterministic and avoids accidentally quadratic bulk insert behavior, `wasm_memory_bytes` billing metric semantics changed, template version constraints unified, `publish --delete-data` config fallback fixed, CLI `call` accepts hex Identity arguments. +- **2.6.0**: procedural-view primary keys are available across Rust, TypeScript, and C#, commitlog gains `max_segment_size` / `write_buffer_size` / `preallocate_segments`, the default write buffer increases for throughput, event-table automigrations improve, and CLI binary distribution expands. ## Debugging Checklist diff --git a/.codex/skills/spacetimedb-rust/SKILL.md b/.codex/skills/spacetimedb-rust/SKILL.md index 889226ebb..5750ada65 100644 --- a/.codex/skills/spacetimedb-rust/SKILL.md +++ b/.codex/skills/spacetimedb-rust/SKILL.md @@ -1,6 +1,6 @@ --- name: spacetimedb-rust -description: Develop SpacetimeDB 2.5 server modules in Rust for Genarrative. Use when writing or reviewing tables, reducers, procedures, views, migrations, row mappers, schema changes, and module logic. +description: Develop SpacetimeDB 2.6 server modules in Rust for Genarrative. Use when writing or reviewing tables, reducers, procedures, views, migrations, row mappers, schema changes, and module logic. --- # SpacetimeDB Rust Module Development @@ -28,7 +28,7 @@ ctx.db.player.find(id) // Use ctx.db.player().id().find(&id) ctx.sender // Use ctx.sender() ctx.db.user().name().update(..) // Update by primary key only -spacetimedb = { version = "...", features = ["unstable"] } // Not needed for procedures in 2.5 +spacetimedb = { version = "...", features = ["unstable"] } // Not needed for procedures since 2.5 ``` ## Required Patterns @@ -181,11 +181,11 @@ fn deal_damage(ctx: &ReducerContext, target: Identity, amount: u32) { Event tables must be subscribed explicitly and are excluded from `subscribe_to_all_tables()`. -In 2.5, event tables support broader layout-altering automigrations than regular tables, including column removal, reordering, and type changes. This relaxed migration policy does not apply to persistent tables. +In 2.6, event tables support broader layout-altering automigrations than regular tables, including column removal, reordering, and type changes. This relaxed migration policy does not apply to persistent tables. Event-table primary keys and constraints are enforced only within the current transaction. They do not make event rows persistent, and client SDKs expose event tables as insert-only event streams. Do not rely on `OnUpdate` / `on_update` / `onUpdate` for event tables; use a persistent table or a primary-keyed procedural view when update callbacks are required. -Official 2.4.1/2.5 release notes tie primary-key-backed update callbacks to procedural views, not event tables. +Official 2.4.1 through 2.6 release notes tie primary-key-backed update callbacks to procedural views, not event tables. ## Views @@ -228,7 +228,7 @@ For scheduled reducers, check `ctx.sender_auth().is_internal()` when the reducer ## Procedures -Procedures are stable in 2.5 and no longer require the `unstable` feature. +Procedures remain stable in 2.6 and no longer require the `unstable` feature. ```rust use spacetimedb::{procedure, ProcedureContext}; diff --git a/apps/admin-web/src/pages/AdminRechargeProductPage.tsx b/apps/admin-web/src/pages/AdminRechargeProductPage.tsx index ef5745ade..2e18e3c6c 100644 --- a/apps/admin-web/src/pages/AdminRechargeProductPage.tsx +++ b/apps/admin-web/src/pages/AdminRechargeProductPage.tsx @@ -49,10 +49,10 @@ export function AdminRechargeProductPage({ const [priceCents, setPriceCents] = useState('600'); const [kind, setKind] = useState('points'); const [pointsAmount, setPointsAmount] = useState('60'); - const [bonusPoints, setBonusPoints] = useState('60'); + const [bonusPoints, setBonusPoints] = useState('0'); const [durationDays, setDurationDays] = useState('0'); - const [badgeLabel, setBadgeLabel] = useState('首充双倍'); - const [description, setDescription] = useState('首充送60泥点'); + const [badgeLabel, setBadgeLabel] = useState(''); + const [description, setDescription] = useState('60泥点'); const [tier, setTier] = useState('normal'); const [membershipPeriodPoints, setMembershipPeriodPoints] = useState('0'); const [membershipPeriodDays, setMembershipPeriodDays] = useState('0'); diff --git a/deploy/container/README.md b/deploy/container/README.md index 0baccf5f9..c6959b695 100644 --- a/deploy/container/README.md +++ b/deploy/container/README.md @@ -57,7 +57,7 @@ Linux Docker Engine 若要从宿主机 CLI 连到容器内服务,直接用 `ht ## 构建工具链 -`api-server` 容器镜像只构建 Linux release API 二进制,不构建 `spacetime-module`。当前 `api-server -> spacetime-client -> spacetimedb-sdk 2.4.1` 依赖链要求 Rust 1.93,因此 `deploy/container/api-server.Dockerfile` 的 Rust builder 固定为 `rust:1.93-bookworm`。镜像构建阶段会同时复制 `public/`,用于满足 API 二进制里 `include_bytes!` 引用的内置素材;不要把 `public/generated-*` 放入镜像上下文。如果本机 Docker Hub 拉取失败,可以先在本机准备同名本地 builder 镜像,但不要把临时 bootstrap 容器或私有 registry 凭据写入仓库。 +`api-server` 容器镜像只构建 Linux release API 二进制,不构建 `spacetime-module`。当前 `api-server -> spacetime-client -> spacetimedb-sdk 2.6.0` 依赖链要求 Rust 1.93,因此 `deploy/container/api-server.Dockerfile` 的 Rust builder 固定为 `rust:1.93-bookworm`。镜像构建阶段会同时复制 `public/`,用于满足 API 二进制里 `include_bytes!` 引用的内置素材;不要把 `public/generated-*` 放入镜像上下文。如果本机 Docker Hub 拉取失败,可以先在本机准备同名本地 builder 镜像,但不要把临时 bootstrap 容器或私有 registry 凭据写入仓库。 ## 启动与验证 @@ -127,7 +127,7 @@ npm run container:worker-smoke -- status npm run container:worker-smoke -- smoke --force ``` -`container:worker-smoke` 默认会把本机 `spacetime` 2.4.1 CLI 打成轻量 SpacetimeDB 镜像,避免首次 smoke 必须拉取官方大镜像;普通 `npm run container:*` 压测仍默认使用 `clockworklabs/spacetime:v2.4.1`。如果 Docker build 阶段在容器内拉取 crates.io 依赖不稳定,可让容器内 Cargo 复用本机 Cargo 缓存构建当前二进制,再打入临时 smoke 镜像。该模式默认使用 `rust:1.93-bookworm` 作为 builder、Debian bookworm smoke runtime 承载构建产物;需要换 builder 镜像时设置 `GENARRATIVE_WORKER_SMOKE_CARGO_IMAGE`,需要换运行时基础镜像时设置 `GENARRATIVE_WORKER_SMOKE_LOCAL_BASE_IMAGE`: +`container:worker-smoke` 默认会把本机 `spacetime` 2.6.0 CLI 打成轻量 SpacetimeDB 镜像,避免首次 smoke 必须拉取官方大镜像;普通 `npm run container:*` 压测默认使用 `clockworklabs/spacetime:v2.6.0`。如果 Docker build 阶段在容器内拉取 crates.io 依赖不稳定,可让容器内 Cargo 复用本机 Cargo 缓存构建当前二进制,再打入临时 smoke 镜像。该模式默认使用 `rust:1.93-bookworm` 作为 builder、Debian bookworm smoke runtime 承载构建产物;需要换 builder 镜像时设置 `GENARRATIVE_WORKER_SMOKE_CARGO_IMAGE`,需要换运行时基础镜像时设置 `GENARRATIVE_WORKER_SMOKE_LOCAL_BASE_IMAGE`: ```bash npm run container:worker-smoke -- smoke --local-binary diff --git a/deploy/container/docker-compose.loadtest.yml b/deploy/container/docker-compose.loadtest.yml index 2466617a0..10597b127 100644 --- a/deploy/container/docker-compose.loadtest.yml +++ b/deploy/container/docker-compose.loadtest.yml @@ -2,7 +2,7 @@ name: genarrative-container-loadtest services: spacetimedb: - image: ${GENARRATIVE_CONTAINER_SPACETIME_IMAGE:-clockworklabs/spacetime:v2.4.1} + image: ${GENARRATIVE_CONTAINER_SPACETIME_IMAGE:-clockworklabs/spacetime:v2.6.0} user: root command: [ diff --git a/deploy/container/nginx.conf b/deploy/container/nginx.conf index be9dd0ebd..0f5bc237c 100644 --- a/deploy/container/nginx.conf +++ b/deploy/container/nginx.conf @@ -215,8 +215,18 @@ http { return 404; } + # BEGIN GENARRATIVE MAIN SPA ROUTES + location = / { + try_files /index.html =404; + } + + location ~* "^/(?:bark-battle|big-fish|child-motion-demo|creation|creation/baby-object-match|creation/baby-object-match/generating|creation/baby-object-match/result|creation/bark-battle|creation/bark-battle/generating|creation/bark-battle/result|creation/big-fish|creation/big-fish/generating|creation/big-fish/result|creation/creative-agent|creation/jump-hop|creation/jump-hop/generating|creation/jump-hop/result|creation/match3d|creation/match3d/generating|creation/match3d/result|creation/puzzle|creation/puzzle-clear|creation/puzzle-clear/generating|creation/puzzle-clear/result|creation/puzzle/generating|creation/puzzle/result|creation/rpg|creation/rpg/agent|creation/rpg/generating|creation/rpg/result|creation/square-hole|creation/square-hole/generating|creation/square-hole/result|creation/visual-novel|creation/visual-novel/generating|creation/visual-novel/result|creation/wooden-fish|creation/wooden-fish/generating|creation/wooden-fish/result|editor/canvas|gallery/jump-hop/detail|gallery/puzzle/detail|gallery/visual-novel/detail|match3d|project|puzzle|runtime/baby-love-drawing|runtime/baby-object-match|runtime/bark-battle|runtime/big-fish|runtime/jump-hop|runtime/match3d|runtime/puzzle|runtime/puzzle-clear|runtime/rpg/adventure|runtime/rpg/characters|runtime/square-hole|runtime/visual-novel|runtime/wooden-fish|works/detail|worlds/detail)/?$" { + try_files $uri /index.html =404; + } + # END GENARRATIVE MAIN SPA ROUTES + location / { - try_files $uri $uri/ /index.html; + try_files $uri $uri/ =404; } } } diff --git a/deploy/nginx/genarrative-dev-http.conf b/deploy/nginx/genarrative-dev-http.conf index 62e87f148..336a486e8 100644 --- a/deploy/nginx/genarrative-dev-http.conf +++ b/deploy/nginx/genarrative-dev-http.conf @@ -18,6 +18,19 @@ limit_req_zone $binary_remote_addr zone=genarrative_gallery_rps:10m rate=5000r/s 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; @@ -70,10 +83,22 @@ server { } 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; } @@ -249,6 +274,28 @@ server { return 404; } + # BEGIN GENARRATIVE MAIN SPA ROUTES + location = / { + error_page 503 /maintenance.html; + + if ($genarrative_maintenance) { + return 503; + } + + try_files /index.html =404; + } + + location ~* "^/(?:bark-battle|big-fish|child-motion-demo|creation|creation/baby-object-match|creation/baby-object-match/generating|creation/baby-object-match/result|creation/bark-battle|creation/bark-battle/generating|creation/bark-battle/result|creation/big-fish|creation/big-fish/generating|creation/big-fish/result|creation/creative-agent|creation/jump-hop|creation/jump-hop/generating|creation/jump-hop/result|creation/match3d|creation/match3d/generating|creation/match3d/result|creation/puzzle|creation/puzzle-clear|creation/puzzle-clear/generating|creation/puzzle-clear/result|creation/puzzle/generating|creation/puzzle/result|creation/rpg|creation/rpg/agent|creation/rpg/generating|creation/rpg/result|creation/square-hole|creation/square-hole/generating|creation/square-hole/result|creation/visual-novel|creation/visual-novel/generating|creation/visual-novel/result|creation/wooden-fish|creation/wooden-fish/generating|creation/wooden-fish/result|editor/canvas|gallery/jump-hop/detail|gallery/puzzle/detail|gallery/visual-novel/detail|match3d|project|puzzle|runtime/baby-love-drawing|runtime/baby-object-match|runtime/bark-battle|runtime/big-fish|runtime/jump-hop|runtime/match3d|runtime/puzzle|runtime/puzzle-clear|runtime/rpg/adventure|runtime/rpg/characters|runtime/square-hole|runtime/visual-novel|runtime/wooden-fish|works/detail|worlds/detail)/?$" { + 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; @@ -256,6 +303,6 @@ server { return 503; } - try_files $uri $uri/ /index.html; + try_files $uri $uri/ =404; } } diff --git a/deploy/nginx/genarrative.conf b/deploy/nginx/genarrative.conf index fa1a111b4..c3b202613 100644 --- a/deploy/nginx/genarrative.conf +++ b/deploy/nginx/genarrative.conf @@ -16,6 +16,19 @@ limit_req_zone $binary_remote_addr zone=genarrative_gallery_rps:10m rate=5000r/s 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; @@ -90,10 +103,22 @@ server { } 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; } @@ -269,6 +294,28 @@ server { return 404; } + # BEGIN GENARRATIVE MAIN SPA ROUTES + location = / { + error_page 503 /maintenance.html; + + if ($genarrative_maintenance) { + return 503; + } + + try_files /index.html =404; + } + + location ~* "^/(?:bark-battle|big-fish|child-motion-demo|creation|creation/baby-object-match|creation/baby-object-match/generating|creation/baby-object-match/result|creation/bark-battle|creation/bark-battle/generating|creation/bark-battle/result|creation/big-fish|creation/big-fish/generating|creation/big-fish/result|creation/creative-agent|creation/jump-hop|creation/jump-hop/generating|creation/jump-hop/result|creation/match3d|creation/match3d/generating|creation/match3d/result|creation/puzzle|creation/puzzle-clear|creation/puzzle-clear/generating|creation/puzzle-clear/result|creation/puzzle/generating|creation/puzzle/result|creation/rpg|creation/rpg/agent|creation/rpg/generating|creation/rpg/result|creation/square-hole|creation/square-hole/generating|creation/square-hole/result|creation/visual-novel|creation/visual-novel/generating|creation/visual-novel/result|creation/wooden-fish|creation/wooden-fish/generating|creation/wooden-fish/result|editor/canvas|gallery/jump-hop/detail|gallery/puzzle/detail|gallery/visual-novel/detail|match3d|project|puzzle|runtime/baby-love-drawing|runtime/baby-object-match|runtime/bark-battle|runtime/big-fish|runtime/jump-hop|runtime/match3d|runtime/puzzle|runtime/puzzle-clear|runtime/rpg/adventure|runtime/rpg/characters|runtime/square-hole|runtime/visual-novel|runtime/wooden-fish|works/detail|worlds/detail)/?$" { + 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; @@ -276,6 +323,6 @@ server { return 503; } - try_files $uri $uri/ /index.html; + try_files $uri $uri/ =404; } } diff --git a/deploy/nginx/snippets/genarrative-maintenance.conf b/deploy/nginx/snippets/genarrative-maintenance.conf index e6c844fd0..e101c488f 100644 --- a/deploy/nginx/snippets/genarrative-maintenance.conf +++ b/deploy/nginx/snippets/genarrative-maintenance.conf @@ -1,10 +1,14 @@ # 维护模式由发布脚本或人工运维通过固定文件控制。 -# 文件存在时,普通页面展示维护页,管理 API 返回 503。 +# 文件存在时,公网页面展示维护页、API 返回 503;真实 TCP 内网来源仍可访问整站。 set $genarrative_maintenance 0; if (-f /var/lib/genarrative/maintenance/enabled) { set $genarrative_maintenance 1; } +if ($genarrative_internal_client) { + set $genarrative_maintenance 0; +} + location = /maintenance.html { root /srv/genarrative/web; add_header Cache-Control "no-store"; diff --git a/deploy/pingora/nginx-route-parity.matrix.json b/deploy/pingora/nginx-route-parity.matrix.json index 8642b8651..d923df865 100644 --- a/deploy/pingora/nginx-route-parity.matrix.json +++ b/deploy/pingora/nginx-route-parity.matrix.json @@ -359,17 +359,93 @@ }, { "id": "web_spa_fallback", - "samplePath": "/some/spa/path", + "samplePath": "/creation/puzzle/result", "expect": { "kind": "static", "root": "web", "mode": "spa_fallback" }, "nginx": { - "production": ["location /", "try_files $uri $uri/ /index.html;"], - "development": ["location /", "try_files $uri $uri/ /index.html;"] + "production": [ + "# BEGIN GENARRATIVE MAIN SPA ROUTES", + "try_files $uri /index.html =404;" + ], + "development": [ + "# BEGIN GENARRATIVE MAIN SPA ROUTES", + "try_files $uri /index.html =404;" + ] }, - "docs": ["其它路径", "失败回退 `/index.html`"] + "docs": ["主站 SPA allowlist", "失败回退 `/index.html`"] + }, + { + "id": "web_spa_case_trailing_slash", + "samplePath": "/CREATION/PUZZLE/RESULT/", + "expect": { + "kind": "static", + "root": "web", + "mode": "spa_fallback" + }, + "nginx": { + "production": ["location ~*", "try_files $uri /index.html =404;"], + "development": ["location ~*", "try_files $uri /index.html =404;"] + }, + "docs": ["大小写不敏感", "一个尾部斜杠"] + }, + { + "id": "web_unknown_path_exact", + "samplePath": "/some/spa/path", + "expect": { + "kind": "static", + "root": "web", + "mode": "exact" + }, + "nginx": { + "production": ["location /", "try_files $uri $uri/ =404;"], + "development": ["location /", "try_files $uri $uri/ =404;"] + }, + "docs": ["其它 Web 路径", "缺失时返回真实 404"] + }, + { + "id": "creation_unknown_path_exact", + "samplePath": "/creation/not-exist", + "expect": { + "kind": "static", + "root": "web", + "mode": "exact" + }, + "nginx": { + "production": ["try_files $uri $uri/ =404;"], + "development": ["try_files $uri $uri/ =404;"] + }, + "docs": ["`/creation/not-exist`"] + }, + { + "id": "runtime_unknown_path_exact", + "samplePath": "/runtime/not-exist", + "expect": { + "kind": "static", + "root": "web", + "mode": "exact" + }, + "nginx": { + "production": ["try_files $uri $uri/ =404;"], + "development": ["try_files $uri $uri/ =404;"] + }, + "docs": ["`/runtime/not-exist`"] + }, + { + "id": "puzzle_unknown_path_exact", + "samplePath": "/puzzle/not-exist", + "expect": { + "kind": "static", + "root": "web", + "mode": "exact" + }, + "nginx": { + "production": ["try_files $uri $uri/ =404;"], + "development": ["try_files $uri $uri/ =404;"] + }, + "docs": ["`/puzzle/not-exist`"] } ] } diff --git a/docs/README.md b/docs/README.md index 15d04145a..67c19ef4b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -5,6 +5,7 @@ ## 快速入口 - [Agent 工作入口与执行准则](./%E3%80%90%E5%8D%8F%E4%BD%9C%E8%A7%84%E8%8C%83%E3%80%91Agent%E5%B7%A5%E4%BD%9C%E5%85%A5%E5%8F%A3%E4%B8%8E%E6%89%A7%E8%A1%8C%E5%87%86%E5%88%99-2026-06-22.md):复杂任务前的 Agent 阅读顺序、执行边界、技能路由、文档规则和验证口径。 +- [官网 SEO 地基实施约定](./technical/【SEO】官网SEO地基实施约定-2026-07-10.md):首页基础 head、robots/sitemap、唯一 H1、精确 SPA 路由与未知路径 404 的长期技术边界。 - [经验沉淀](./experience/README.md):项目开发经验、UI 交接、历史实现经验。 - [审计与复盘](./audits/README.md):工程审查、文本/乱码审计、专项落地审计。 - [系统设计](./design/README.md):玩法、关系、物品与对话设计。 diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 8c56aeeb8..6aef0e895 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -16,6 +16,22 @@ --- +## 2026-07-12 泥点充值收敛为四档并统一资产入口 + +- 背景:主站与图片画板的泥点余额入口、余额明细和充值弹窗存在不同实现,旧充值口径仍展示六档泥点、首充双倍和会员购买 / 升级入口,容易让展示、商品资格与后端余额真相发生漂移。 +- 决策:主站与图片画板统一复用公共泥点资产入口,收起态展示总额与充值,展开态只展示不限时泥点、每日免费泥点和使用详情;充值中心 BFF 继续统一下发总额、三桶余额、限时到期时间、每日免费基础重置额及下次重置时间,前端不得自行相减推算,但会员周期限时泥点仅用于存量兼容和后端结算,当前版本不在前台展示。默认泥点商品收敛为 `60 / ¥6`、`180 + 90 / ¥18`、`300 + 150 / ¥30`、`680 + 340 / ¥68` 四档,`60` 档无赠送,后三档按现有 `user_id + product_id` 独立资格规则首次购买加赠 `50%`。当前版本关闭会员购买页签、会员商品和购买 / 升级入口。 +- 影响范围:`profile_recharge_product_config` 默认商品、充值中心 read model、共享前后端契约、主站与图片画板泥点资产入口、充值弹窗、后台充值商品默认值。 +- 验证方式:充值与统一入口定向前端测试、`npm run typecheck`、充值商品定向 Rust 测试、`cargo check -p spacetime-module -p spacetime-client -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding`、`git diff --check`。 +- 关联文档:`docs/【项目基线】当前产品与工程约束-2026-05-15.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 + +## 2026-07-12 每日免费泥点独立于任务并按北京时间日切 + +- 背景:现有“每日免费泥点”实际只是每日登录任务领取奖励,领取后进入普通永久余额,既不是独立余额桶,也不会在次日失效;同时主站仍展示每日任务卡片和任务中心入口,与新的产品口径不一致。 +- 决策:新增 `profile_daily_free_points` 作为每日免费泥点事实源,基础额度固定为 `20`,以北京时间 `day_key` 为业务日;跨日后的首次余额读取或扣费原子清除昨日剩余及退款叠加量,并把当日额度重置为 `20`,对外语义始终视为北京时间 `00:00` 已重置。扣费按“每日免费 -> 会员周期限时 -> 永久”顺序;资产退款在同一业务日恢复原每日免费额度,跨业务日时把原每日免费消费部分叠加到退款当日每日免费桶,当日允许超过 `20`,下一业务日仍统一重置为 `20`。每日任务系统和 `daily_task_reward` 保留为普通永久奖励,但主站隐藏每日任务卡片及任务中心入口。 +- 影响范围:`profile_daily_free_points`、`profile_wallet_ledger`、个人资金 read model、钱包扣费和退款 metadata、主站“我的”页、SpacetimeDB 迁移与生成绑定。 +- 验证方式:`npm run spacetime:generate`、`npm run check:spacetime-schema`、钱包定向 Rust 测试、个人中心定向前端测试、`npm run typecheck`、`npm run check:encoding`、`git diff --check`。 +- 关联文档:`docs/【项目基线】当前产品与工程约束-2026-05-15.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 + ## 2026-07-11 BgFilter 交叉模型否决只用于角色形象 - 背景:新版 BgFilter 的 `cross_check` 默认开启,会额外运行 HR-matting 第二意见模型;角色形象需要保留发丝、镂空等复杂边缘质量,但角色动作序列帧、图标 spritesheet 和 UI 素材提取不需要承担这部分额外推理开销。 @@ -24,6 +40,22 @@ - 验证方式:运行 `cargo test -p api-server editor_bgfilter_cross_check --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server editor_canvas_screen_background_generation_uses_bgfilter_postprocess --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server editor_character_animation_frames_use_three_stage_matting_fallback --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。 +## 2026-07-11 SpacetimeDB 工具链统一升级到 2.6.0 + +- 背景:生产数据副本验证已使用 2.6.0 standalone,而仓库 Rust crate、本地 CLI、生成 bindings、容器与 server provision 仍锁定 2.5.0 或更早版本,继续混用会增加 BSATN / procedure 返回值与发布产物错配风险。 +- 决策:`server-rs/Cargo.toml` 的 `spacetimedb`、`spacetimedb-sdk`、`spacetimedb-lib` 精确锁定 2.6.0;本地 CLI / standalone、Rust bindings、worker smoke、容器压测镜像和生产 provision 下载根同步对齐 2.6.0。其它 crate 恰好出现的 2.4.1 / 2.5.0 不随本决策机械替换。 +- 影响范围:Rust workspace lockfile、SpacetimeDB bindings、本地 dev 版本门禁、容器 smoke / loadtest、server provision Jenkins 与项目 SpacetimeDB skills / 文档。 +- 验证方式:核对 `spacetime --version`,运行 `npm run spacetime:generate`、`npm run check:spacetime-schema`、`cargo check` / 定向测试、`npm run test -- scripts/dev.test.ts`、server provision 工具测试、production ops / encoding / diff 门禁。 +- 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。 + +## 2026-07-10 外部生成任务只持久化轻量媒体引用并独立维护摘要投影 + +- 背景:编辑器 worker 化后直接把同步接口 payload 序列化进 `external_generation_job.request_payload_json`;前端又把已有 OSS `objectKey` 下载成 Data URL 再提交,导致单个任务 JSON 膨胀到数 MB,正式任务列表读取 20 条任务时同时搬运约 65 MB payload,并放大为 SpacetimeDB 与 api-server 的瞬时内存峰值。此前“禁止 Data URL 持久化”只覆盖工程、素材、图层和元数据,遗漏了正式生成任务表。 +- 决策:`external_generation_job.request_payload_json` / `result_payload_json` 同样属于正式持久化边界。对于本次事故涉及的 `source_module = editor-canvas` 任务,只允许普通业务参数和 `objectKey` / `resourceId` / `assetId` 等已登记轻量引用;任意层级 `data:` / `blob:` 与超限 JSON 必须由 api-server 和 SpacetimeDB 双重拒绝。编辑器已有媒体直接传正式引用,本地红框标记图先上传 OSS 后再入队,上传目录与文件名使用同一个强唯一 ID。其它玩法现存 Data URL 请求契约不在本次事故修复中被静默禁用,后续必须先完成各自资源化再扩大 DB 门禁。用户任务列表、单任务状态和 acknowledge 只读取不含 request/result payload 的 `external_generation_job_summary` 投影;acknowledge 只更新摘要小表并保留审计事件,不为确认通知加载 / 重写主任务 payload。提示词在入队时提前提取;错误摘要统一去除内联媒体并限制为 2048 字符;列表在单次 owner 扫描中只保留固定大小 top-N,不再收集全量历史后截断。历史终态 payload 仅允许迁移操作员通过默认 dry-run、`editor-canvas + job_id` B-tree cursor 显式分批压缩,pending / running 永不压缩;cursor 选择最多读取 `limit + 1` 行,apply 再逐条主键读取。首次发布默认 fail-closed 暂停在 Stdb 与 API 之间,保持维护模式并停止旧 API/controller/worker,完成压缩和摘要回填后才由指定审批人放行 API。 +- 影响范围:编辑器生成提交 workflow、`external_generation_job`、`external_generation_job_summary`、外部生成 procedure / typed client / BFF、SpacetimeDB bindings、历史数据维护流程和图片画布文档。 +- 验证方式:覆盖编辑器嵌套内联媒体与 payload 上限拒绝、非编辑器既有任务不被本轮门禁误伤、正式任务接口类型不含 payload、终态分批压缩不修改活动任务、已有 objectKey 不转 Data URL、本地标记图先上传再提交;运行外部生成定向 Rust / Vitest、`npm run spacetime:generate`、`npm run check:spacetime-schema`、`npm run typecheck`、`npm run check:encoding` 和 `git diff --check`。 +- 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/project-memory/shared-memory/pitfalls.md`。 + ## 2026-07-10 BgFilter segModel 保留内部字段,不进入外部 OpenAPI - 背景:`api-server` 的图片生成、图标 spritesheet 与 UI 素材提取请求仍可反序列化 `segModel`,并识别 `birefnet` / `anime-seg`,以兼容内部调用和既有任务;但 BgFilter 当前受服务进程内存与并发容量约束,不同分割模型的内存占用并非可由外部调用方自由选择的稳定契约。 @@ -51,7 +83,7 @@ ## 2026-07-09 充值订单过期改为 SpacetimeDB scheduled 表触发 - 背景:旧充值过期处理使用 api-server 后台轮询 worker claim 普通 schedule 表,非 HTTP 的 external-generation-worker / controller 进程也可能启动同一过期任务;扩外部生成 worker 会意外放大微信查单 / 关单流量,并且本地过期后若微信仍可支付,容易出现“微信扣款但本地拒绝入账”的风险。 -- 决策:新建原生 scheduled 表 `profile_recharge_order_expiration_timer`,创建真实微信 pending 充值订单时写入 5 分钟 timer;scheduled reducer 到点只把仍为 `pending` 的订单改为 `expired` 并写 `expired_at`。HTTP `api-server` 订阅 `profile_recharge_order` 的 `Pending -> Expired` 更新并执行微信查单补偿;`SUCCESS` 允许 `Expired -> Paid` 入账,未支付或远端已终态只记录检查结果,本地保持 `expired`。`external-generation-worker` 和 controller 不处理充值过期。 +- 决策:新建原生 scheduled 表 `profile_recharge_order_expiration_timer`,创建真实微信 pending 充值订单时写入 5 分钟 timer;scheduled reducer 到点只把仍为 `pending` 的订单改为 `expired` 并写 `expired_at`。HTTP `api-server` 只订阅活跃 timer 表的删除事件,按 `order_id` 重新读取订单并仅对 `expired` 执行微信查单补偿;支付或主动关闭导致的 timer 删除会被状态判断忽略,断线窗口由未检查过期订单 catch-up 补齐,不订阅完整 `profile_recharge_order` 历史表。`SUCCESS` 允许 `Expired -> Paid` 入账,未支付或远端已终态只记录检查结果,本地保持 `expired`。`external-generation-worker` 和 controller 不处理充值过期。 - 影响范围:`profile_recharge_order`、`profile_recharge_order_expiration_timer`、充值订单状态契约、`spacetime-client` bindings/facade、`api-server` 充值过期监听器、微信支付查单 / 关单、个人中心充值前端、后台表查询和运维文档。 - 验证方式:`npm run spacetime:generate`、`npm run check:spacetime-schema`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、充值过期 listener / 微信支付 / shared contracts / 前端充值定向测试、`npm run check:encoding`、`git diff --check`。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。 @@ -3941,3 +3973,43 @@ - 性能:`api-server` 只在当前判定涉及的已启用 gate 配置了用户标签白名单时读取用户标签;不因无关 gate 或纯用户 ID / 百分比灰度触发额外标签读取。 - 影响范围:`feature_gate_config`、`spacetime-client` runtime facade、`api-server` 创作入口配置与路由熔断、`apps/admin-web` 灰度发布页。 - 验证方式:`npm run spacetime:generate`、`npm run check:spacetime-schema`、`cargo test -p module-runtime --manifest-path server-rs/Cargo.toml feature_gate`、`cargo test -p api-server --manifest-path server-rs/Cargo.toml creation_entry_feature_gate`、`npm run admin-web:typecheck`、后台灰度页 Vitest、`npm run check:encoding`、`git diff --check`。 + +## 2026-07-10 官网 SEO 与主站 SPA 404 边界 + +- 背景:主站 Nginx 和 Pingora 原先会把任意未知路径回退到 `index.html`,导致 soft 404;共享 `index.html` 也缺少首页 SEO head,robots 和 sitemap 请求会落入 SPA fallback。 +- 决策:新增真实 `robots.txt` 和仅首页的 `sitemap.xml`,首页共享 head 提供基础 SEO/OG/JSON-LD 文本但不使用未确认的 image/logo URL;首页 DOM 只保留一个稳定产品定位 H1。Nginx 与 Pingora 只允许当前完整 SPA 路径回退 `index.html`,同前缀未知路径必须返回 404;`/admin` 继续走独立子应用。路由变化必须同步三套 Nginx、Pingora、route parity matrix 和自动门禁。 +- 影响范围:`index.html`、`public/robots.txt`、`public/sitemap.xml`、首页组件、三套 Nginx、Pingora 网关和路由 parity 门禁。 +- 验证方式:前端定向测试与构建、`npm run check:nginx-spa-routes`、`npm run check:pingora-route-parity`、`npm run check:pingora-gateway-smoke`、`npm run check:encoding`、`git diff --check`,部署后同时抽查根级未知路径和 `/creation/not-exist` 等同前缀未知路径。 + +## 2026-07-11 Jenkins Secret File 默认值与 dev 定时发布 + +- 背景:Stdb Build / Publish / Full Job 改用 Secret File 后,live Job UI 默认值为空且会被 SCM Jenkinsfile 覆盖;Full Job 的 04:00 timer 又与默认人工 rollout gate 冲突。dev 服务器不对外,允许定时完整发布。 +- 决策:三个 Jenkinsfile 将 `MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID` 默认固定为 `genarrative-spacetime-bootstrap-secret-dev-file`。Full Job 保留 04:00 timer,默认 `DEPLOY_TARGET=development`、`STDB_API_ROLLOUT_MODE=normal`,按 Stdb → API → Web 完整发布 dev;三个下游 Build 都显式传 `PUBLISH_AFTER_BUILD=false`,防止提前发布和顺序漂移。人工维护窗口才选择 `pause-after-stdb` 并强制校验 approvers。 +- 凭据边界:Secret 原文以 Jenkins Secret File 为事实源;credential ID 与参数行为以仓库 Jenkinsfile 为事实源。旧 Secret Text 继续服务 Database Import / Export,不原地改类型或删除。 +- 影响范围:`jenkins/Jenkinsfile.production-full-build-and-deploy`、`jenkins/Jenkinsfile.production-stdb-module-build`、`jenkins/Jenkinsfile.production-stdb-module-publish`、生产运维门禁与 live Job 参数 schema。 +- 验证方式:`node --check scripts/check-production-ops-guardrails.mjs`、`npm run check:production-ops`、`npm run check:encoding`、`git diff --check`;推送后用首阶段 fail-closed 运行刷新三个 live Job 参数,再只读核对 credential 默认值、`normal` 默认值与 timer。 + +## 2026-07-12 维护模式只拦截公网流量 + +- 背景:此前维护 marker 只对内网放行后台,内网排障或人工维护仍无法访问主站、普通 API 和 SpacetimeDB 路由;当前维护目标是隔离公网访问,不应阻断可信内网流量。 +- 决策:Nginx 与 Pingora 允许 IPv4 loopback / RFC1918 / link-local 和 IPv6 loopback / ULA / link-local 来源在维护期间访问整站,包括主站页面与静态资源、普通 API、后台页面与 `/admin/api/**`、SpacetimeDB 路由。公网应用主站、普通 API、后台和 SpacetimeDB 路由继续维护响应,应用层鉴权不变。 +- 信任边界:Nginx 使用 TCP `$remote_addr`;Pingora 使用 TCP peer,只有同机 loopback Nginx 才可通过其强制覆盖的 `X-Real-IP` 传递原始地址,禁止使用 `X-Forwarded-For` 做维护放行判断。 +- 限制:网关放行不等于后端存活;`pause-after-stdb` 停止 api-server 时,内网普通 API 和后台 API 仍不可用。 +- 影响范围:生产 / dev Nginx 模板、维护 snippet、Pingora maintenance gate、Nginx 静态门禁与 Pingora smoke。 +- 验证方式:`npm run check:nginx-spa-routes`、`npm run check:pingora-route-parity`、`cargo test -p pingora-gateway --manifest-path server-rs/Cargo.toml`、`npm run check:pingora-gateway-smoke`、`npm run check:production-ops`、`npm run check:encoding`、`git diff --check`。 + +## 2026-07-12 Full 发布显式控制成功后维护状态 + +- 背景:Full Job 只能用 `STDB_API_ROLLOUT_MODE` 控制 Stdb 与 API 之间是否暂停,但 API Deploy 在 readiness 成功后固定执行 `maintenance-off.sh`,因此无法选择完整流水线结束后继续保留维护页。 +- 决策:Full Job 新增默认勾选的 `EXIT_MAINTENANCE_MODE_AFTER_COMPLETION`,并让 Stdb Publish、API Deploy 两个下游阶段固定保持维护;Web Deploy 成功后才由独立 `Exit Maintenance` 阶段按该参数决定是否调用 current release 随包 `maintenance-off.sh`。API Deploy Job 单独使用 `KEEP_MAINTENANCE_MODE`,将其转换为随包 `production-api-deploy.sh --keep-maintenance-mode`;默认仍退出维护,失败路径继续沿用 current 切换前后既有安全语义。 +- 参数刷新:Jenkinsfile 是参数事实源。推送后必须让 Full 与 API Deploy live Job 安全加载一次新 Jenkinsfile,再只读确认两个参数已进入 `config.xml`;只在 Jenkins UI 手工加参数不是持久修复。 +- 影响范围:Full / API Deploy Jenkinsfile、API 发布脚本、生产 API deploy fixture、生产运维门禁与 live Job 参数 schema。 +- 验证方式:`bash -n scripts/deploy/production-api-deploy.sh`、`npm run check:production-api-deploy`、`npm run check:production-ops`、`npm run check:encoding`、`git diff --check`。 + +## 2026-07-13 数据库冷备使用 OSS Multipart 上传并在清理前验真 + +- 背景:SpacetimeDB 冷备归档已经超过 OSS 单次 PutObject 的 5 GiB 上限,单请求上传会稳定失败并让本地归档持续积压;网络中断还可能让 CompleteMultipartUpload 的客户端结果不确定。 +- 决策:`scripts/database-backup-to-oss.mjs` 对备份归档统一使用 OSS Multipart Upload,默认按 128 MiB 顺序分片;每个分片请求重新创建文件流、时间和 V4 签名,仅对网络错误、HTTP 408 / 429 / 5xx 做有限重试。V4 canonical query 必须同时支持无等号的 `uploads` 子资源和带值的 `partNumber` / `uploadId` 参数。 +- 验真与清理边界:Complete 后必须发送签名 HEAD,并严格核对 OSS `Content-Length` 与本地归档大小;Complete 响应不确定时也先用 HEAD 判定对象是否已经完整落盘。只有验真成功后才能把 manifest 标记为 `uploaded`,并按 `keepLocal` 决定是否删除本地归档;失败时 best-effort AbortMultipartUpload,不得提前更新 manifest 或清理本地文件。 +- 影响范围:数据库备份 OSS 上传实现、备份回归门禁、release 本地归档保留与 timer 恢复流程。 +- 验证方式:`npm run check:database-backup`、`npm run check:production-ops`、`npm run check:encoding`、`git diff --check`;线上先对既有归档使用 `--upload-archive ... --keep-local`,确认 OSS 对象长度和可恢复性后再清理积压并恢复 timer。 diff --git a/docs/project-memory/shared-memory/pitfalls.md b/docs/project-memory/shared-memory/pitfalls.md index 13c92451c..a71e0d429 100644 --- a/docs/project-memory/shared-memory/pitfalls.md +++ b/docs/project-memory/shared-memory/pitfalls.md @@ -14,6 +14,15 @@ - 关联:相关文件、文档、提交或 Issue ``` +## 禁止 Data URL 持久化时不要漏掉异步任务 JSON + +- 现象:工程、素材、图层和元数据都已禁止 Data URL 后,服务器仍在生成高峰出现 SpacetimeDB / api-server 内存急剧膨胀甚至 OOM;读取少量正式生成任务也会造成远大于响应体的瞬时内存增长。 +- 原因:同步接口 worker 化时把原请求整体序列化到 `external_generation_job.request_payload_json`,而前端又把已有 `objectKey` 下载成 Data URL 提交。任务表也是正式持久化边界;列表 procedure 若先收集完整任务行再截断,还会把 request/result 大字段在 SpacetimeDB、SDK mapper 和 BFF 多次持有。 +- 处理:先在事故涉及的编辑器持久任务 JSON 上由 api-server 与 SpacetimeDB 两层递归拒绝 `data:` / `blob:` 并限制字节数;已有媒体传 `objectKey` / `resourceId` / `assetId`,本地派生图先用强唯一 key 上传。其它玩法若仍以 Data URL 作为正式请求契约,必须先资源化,不能直接扩大门禁造成玩法回归。列表、详情和 acknowledge 只走无 payload 的摘要投影,ack 不能为了同步旧字段重写大任务行;摘要错误文本也必须清除内联媒体并设硬上限,列表只能维护有界 top-N,不能先收集 owner 全量历史再截断。历史只通过迁移操作员的 dry-run + B-tree cursor 分批 procedure 压缩 `editor-canvas` 终态任务,cursor 选择读取量必须受 limit 约束,绝不全表扫描、绝不处理 pending / running;dry-run 后 apply 同一批时保持输入 cursor 不变,最后一批即使 `has_more=false` 只要仍有命中也必须 apply,只有 apply 成功后才推进到返回 cursor。SpacetimeDB CLI 2.5 的 `Option` 非空参数必须使用 SATS sum 编码;维护脚本要统一编码 `cursor_job_id`、`owner_user_id` 和 `completed_before_micros`,否则首批空 cursor 可运行,但第二批或带截止时间的调用会在写入前被拒绝。 +- 发布门禁:生产发布入口必须固定 `--delete-data=never` 与 scoped `--yes=migrate,break-clients`,普通 Jenkins 参数不得暴露清库开关;需要删数据的 schema 冲突必须直接阻断并重新检查 artifact/schema,不能靠裸 `--yes` 放行。 +- 验证:构造嵌套 Data URL、Blob URL 和超限 JSON 确认入队失败;检查正式 UI procedure / client record 不含 request/result payload;用 dry-run 和 apply 测试确认活动任务不变、终态普通提示词保留且内联媒体被替换;至少带一次非空 `--cursor-job-id` 与 `--completed-before-micros` 验证 CLI Option 编码,而不是只测首批空 cursor。 +- 关联:`server-rs/crates/api-server/src/editor_generation_queue.rs`、`server-rs/crates/spacetime-module/src/external_generation.rs`、`server-rs/crates/api-server/src/external_generation.rs`、`src/components/image-editor/useImageCanvasGenerationSubmissionWorkflow.ts`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 + ## React 测试因内部状态或实现细节正常重构就碎 - 现象:修改组件结构、按钮排序、图标库 class、提示文案或 hook 内部状态名后,React 测试大量失败,但真实用户流程和对外契约没有变化。 @@ -359,6 +368,14 @@ - 验证:`npm run test -- src/services/image-editor/editorProjectClient.test.ts src/components/image-editor/useImageCanvasGenerationSubmissionWorkflow.test.tsx -- --runInBand`;后端验证至少覆盖 `editor_image_edit_request_omits_price_mud_points` 和 `editor_image_edit_can_complete_by_replacing_target_layer`。 - 关联:`src/services/image-editor/editorProjectClient.ts`、`src/components/image-editor/useImageCanvasGenerationSubmissionWorkflow.ts`、`server-rs/crates/api-server/src/editor_project.rs`。 +## 图片画布快速编辑尺寸要区分业务目标和 provider 对齐尺寸 + +- 现象:原图经过快速编辑后 Resolution 变成近似比例的 1K / 2K 预设;原图或框选标记图宽高不是 16 的倍数时,VectorEngine edits 直接拒绝请求。 +- 原因:前端已有源图精确 `originalWidth/originalHeight`,提交时却按最近常用比例和 K 档重新计算 `size`;后端又把非 16 倍数的目标尺寸和原始参考图字节直接放进 multipart,并以 provider 回图宽高落库和覆盖画布图层。 +- 处理:画布快速编辑提交源图精确尺寸作为业务目标;api-server 只在 provider 边界向右、向下复制边缘像素,把每张参考图和目标尺寸临时补齐到 16 的倍数,收到回图后裁回业务目标尺寸再持久化。若上游异常返回其他尺寸,先按目标比例裁切缩放;临时对齐尺寸不能进入 OSS 元数据、`editor_project_resource`、`editor_asset` 或画布 Resolution。前端 inline 回填也保留源图显示尺寸和 Resolution,避免旧回包再次放大图层。 +- 验证:`npm run test -- src/components/image-editor/ImageCanvasGenerationSubmissionModel.test.ts src/components/image-editor/ImageCanvasGenerationLayerModel.test.ts src/components/image-editor/useImageCanvasGenerationSubmissionWorkflow.test.tsx` 覆盖 `1537x1025` 精确提交和源图回填;`cargo test -p api-server editor_image_edit_aligns_provider_images_and_restores_source_dimensions --manifest-path server-rs/Cargo.toml` 覆盖 provider `1552x1040`、额外参考图独立对齐和回图恢复。 +- 关联:`src/components/image-editor/ImageCanvasGenerationSubmissionModel.ts`、`src/components/image-editor/useImageCanvasGenerationSubmissionWorkflow.ts`、`src/components/image-editor/ImageCanvasGenerationLayerModel.ts`、`server-rs/crates/api-server/src/editor_project.rs`。 + ## 图片画布快速编辑元数据必须记录原图引用 - 现象:快速编辑生成的新图可以替换画布,但打开图片信息时“生成输入”里看不到被修改的原图。 @@ -2043,10 +2060,10 @@ - 现象:微信小程序支付下单能返回 `prepay_id`,但真实支付通知验签失败,或者本地实现误把商户 API 私钥当作回调验签 key。 - 原因:商户私钥只用于商户请求微信支付和生成小程序 `paySign`;微信支付通知的 `Wechatpay-Signature` 需要使用微信支付平台公钥或平台证书公钥验签,并按通知头里的平台序列号匹配。 -- 处理:api-server 真实微信支付配置同时需要商户私钥与微信平台公钥:`WECHAT_PAY_PRIVATE_KEY_*` 用于签名,`WECHAT_PAY_PLATFORM_PUBLIC_KEY_*` 与 `WECHAT_PAY_PLATFORM_SERIAL_NO` 用于通知验签,`WECHAT_PAY_API_V3_KEY` 只用于解密通知 resource。支付成功后只通过通知里的 `out_trade_no` 确认本地 pending 订单,并保存 `transaction_id` 到 `profile_recharge_order.provider_transaction_id`。 +- 处理:api-server 真实微信支付配置同时需要商户私钥与微信平台公钥:`WECHAT_PAY_PRIVATE_KEY_*` 用于签名,`WECHAT_PAY_PLATFORM_PUBLIC_KEY_*` 与 `WECHAT_PAY_PLATFORM_SERIAL_NO` 用于通知验签,`WECHAT_PAY_API_V3_KEY` 只用于解密通知 resource。微信平台 `PUBLIC KEY` PEM 的 DER 内容是 SPKI `SubjectPublicKeyInfo`,初始化时必须解析并提取其中的 PKCS#1 `RSAPublicKey` DER 后再交给 `ring::RSA_PKCS1_2048_8192_SHA256`;不能把整段 SPKI DER 直接传给 `ring`。支付成功后只通过通知里的 `out_trade_no` 确认本地 pending 订单,并保存 `transaction_id` 到 `profile_recharge_order.provider_transaction_id`。 - APIv3 通知成功应答使用 HTTP `204 No Content`,不要沿用 V2 XML 成功报文;失败仍返回 4XX/5XX 让微信重试。 -- 验证:mock 通知测试只能覆盖本地回调推进;真实环境还需用微信支付平台公钥、真实通知头和 API v3 密钥验证签名与解密链路。 -- 关联:`server-rs/crates/api-server/src/wechat_pay.rs`、`docs/technical/MY_TAB_ACCOUNT_RECHARGE_IMPLEMENTATION_2026-04-25.md`。 +- 验证:mock 通知测试只能覆盖本地回调推进;`platform-wechat` 必须用标准 SPKI `PUBLIC KEY` 和匹配私钥生成真实 RSA-SHA256 签名,覆盖 SPKI 到 PKCS#1 的解析与生产验签 helper。真实环境还需用微信支付平台公钥、真实通知头和 API v3 密钥验证签名与解密链路。 +- 关联:`server-rs/crates/platform-wechat/src/pay.rs`、`server-rs/crates/api-server/src/wechat/pay.rs`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 ## 微信支付 JSAPI 下单必须显式带 User-Agent @@ -2084,10 +2101,17 @@ - 现象:外部生成 worker/controller 扩容后,微信充值过期查单和关单流量也被同步放大;排查时还会误去外部生成 worker 日志里找支付过期任务。 - 原因:支付过期是账户资金链路,不是外部内容生成队列;旧实现把充值过期轮询 worker 挂在通用后台任务启动函数里,非 HTTP 角色也会启动。 -- 处理:充值订单过期由 SpacetimeDB 原生 `profile_recharge_order_expiration_timer` 到点把 `pending` 改为 `expired`,只有 HTTP `api-server` 订阅 `profile_recharge_order` 的 `Pending -> Expired` 更新并查微信补偿。未支付终态本地保持 `expired`,不要再改写成 `closed`;微信成功支付通知或补偿查单仍可把 `Expired -> Paid` 入账。 +- 处理:充值订单过期由 SpacetimeDB 原生 `profile_recharge_order_expiration_timer` 到点把 `pending` 改为 `expired`,只有 HTTP `api-server` 订阅活跃 timer 表的删除事件,按 `order_id` 重新读取订单并仅对 `expired` 查微信补偿;支付或主动关闭导致的删除信号会被状态判断忽略,断线窗口由未检查过期订单 catch-up 补齐。未支付终态本地保持 `expired`,不要再改写成 `closed`;微信成功支付通知或补偿查单仍可把 `Expired -> Paid` 入账。 - 验证:确认 `GENARRATIVE_PROCESS_ROLE=external-generation-worker` / `external-generation-controller` 不启动充值过期监听;创建 pending 充值单后只由 scheduled reducer 产生 `expired`,HTTP api-server listener 记录 `expiration_checked_at` 或补入账。 - 关联:`server-rs/crates/api-server/src/profile_recharge_expiration_listener.rs`、`server-rs/crates/spacetime-module/src/runtime/profile.rs`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。 +## 充值订单状态枚举不能用字符串 SQL 字面量订阅 + +- 现象:API 已 ready,但日志每 5 秒出现 `profile recharge expiration listener failed to subscribe`,并提示 `pending` 不能解析为 `profile_recharge_order.status` 的枚举类型;scheduled reducer 仍会把订单改成 `expired`,但微信查单补偿监听没有运行。 +- 原因:SpacetimeDB 2.6 不会把订阅 SQL 中的 `'pending'` / `'expired'` 字符串自动转换为生成绑定的 sum-type enum;两个按状态过滤的订阅都在应用阶段失败。 +- 处理:不要改成订阅完整 `profile_recharge_order` 历史表。后端订阅只保留活跃五分钟定时器的 `profile_recharge_order_expiration_timer`,监听 timer 删除后按 `order_id` 通过 procedure 读取订单,只处理当前状态为 `expired` 的记录;支付 / 关闭信号会被忽略,断线窗口继续由未检查过期订单 catch-up 补齐。这样既不依赖不受支持的枚举 SQL,也不会把充值历史常驻 API 客户端缓存。 +- 验证:运行 `cargo test -p spacetime-client profile_recharge_expiration --manifest-path server-rs/Cargo.toml`,发布后确认 API 日志不再出现订阅解析错误,并用真实 pending 订单验证 scheduled reducer 过期后写入 `expiration_checked_at`。 + ## 抓大鹅历史草稿外部 Rodin GLB 链接必须转存后再试玩或发布 - 现象:草稿页预览模型失败并报 `GL_INVALID_ENUM: Invalid cap.`,或结果页能看到历史生成记录但试玩、发布和正式运行态仍显示默认积木。 @@ -2928,3 +2952,41 @@ - 处理:产品 UI 不提供模型选择,应用内调用固定 `birefnet`;外部编辑器 OpenAPI 不声明 `segModel`,并保持相关请求 schema 的 `additionalProperties: false`,使外部请求携带该字段时被契约拒绝。只有维护 BgFilter 模型与容量的后端代码可使用该内部字段;若未来需要开放,先完成各模型的内存、并发和超时压测,再明确版本化外部契约。 - 验证:检查 `docs/openapi/genarrative-external-v1.openapi.json` 的图片生成、图标 spritesheet 和 UI 素材提取请求 schema 均未包含 `segModel`,且均保持 `additionalProperties: false`。 - 关联:`server-rs/crates/api-server/src/editor_project.rs`、`src/services/image-editor/editorProjectClient.ts`、`docs/project-memory/shared-memory/decision-log.md`。 +## SPA 路由白名单不能只按一级目录放行 + +- 现象:`/not-exist` 已返回 404,但 `/creation/not-exist`、`/runtime/not-exist` 或 `/puzzle/not-exist` 仍返回 200 首页,搜索引擎继续判定为 soft 404。 +- 原因:Nginx 或 Pingora 使用 `/creation/*`、`/runtime/*` 等宽前缀作为 SPA fallback,前端对未知路径又回到平台首页;只验收根级未知 URL 无法发现该问题。 +- 处理:SPA fallback 必须精确匹配当前真实完整路径,同时允许前端已有的大小写归一和尾部斜杠;最终 catch-all 只提供真实静态文件,失败返回 404。路由增删同步三套 Nginx、Pingora、route parity matrix 和路由门禁。 +- 验证:除全部真实 SPA 路径外,至少检查 `/not-exist`、`/creation/not-exist`、`/runtime/not-exist` 和 `/puzzle/not-exist` 均返回 404;维护模式仍保持页面 503 优先语义。 +- 关联:`src/routing/appRoutes.tsx`、`src/routing/appPageRoutes.ts`、`deploy/nginx/`、`deploy/container/nginx.conf`、`server-rs/crates/pingora-gateway/src/main.rs`。 + +## Jenkins Job UI 参数会被 SCM Jenkinsfile 覆盖 + +- 现象:在 Jenkins Job 页面给 `MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID` 配了默认值,下一次加载 Declarative Pipeline 后又变空或恢复旧描述;04:00 Full Job 还可能因默认选择 `pause-after-stdb` 且 approvers 为空而失败。 +- 原因:这些 Job 使用 Pipeline script from SCM,`parameters {}` 和 `triggers {}` 会作为 Job property 回写现场配置;只改 UI 不是持久修复。构建编排如果不显式关闭下游 `PUBLISH_AFTER_BUILD`,还会受下游默认值漂移影响。 +- 处理:credential ID 和参数默认值写回三个 Jenkinsfile;仅供开发使用的 dev 定时 Full Job 默认 `STDB_API_ROLLOUT_MODE=normal`,三路 Build 调用显式传 `PUBLISH_AFTER_BUILD=false`,再由 Full Job 统一按 Stdb → API → Web 发布。Secret 原文只放 Jenkins Secret File,旧 Secret Text 保留给 Import / Export。 +- 验证:推送后让 Full / Stdb Build 用不存在的源码分支在 checkout 阶段 fail-closed,让 Stdb Publish 用空构建版本在 Prepare 阶段 fail-closed,以安全刷新参数 schema;随后只读检查三个 live `config.xml` 的参数描述和默认值,确认 Full timer 仍为 `0 4 * * *`、rollout 默认值为 `normal`,并确认刷新运行未进入 publish / deploy stage。 +- 关联:`jenkins/Jenkinsfile.production-full-build-and-deploy`、`jenkins/Jenkinsfile.production-stdb-module-build`、`jenkins/Jenkinsfile.production-stdb-module-publish`、`scripts/check-production-ops-guardrails.mjs`。 + +## 维护模式内网全站放行不能信任 X-Forwarded-For + +- 现象:维护期间希望让内网继续访问整站,如果直接按 `X-Forwarded-For: 192.168.x.x` 放行,公网请求可伪造该头绕过维护闸;如果仍按路径只放行后台,又会让内网主站和普通 API 继续返回 503。 +- 原因:XFF 是客户端可提交的普通请求头,当前 Nginx 的 `$proxy_add_x_forwarded_for` 还会保留已有前缀;维护放行属于授权判断,必须建立在不可伪造的网络来源边界上,并在路由分类前按来源统一决定是否绕过维护闸。 +- 处理:Nginx 按 TCP `$remote_addr` 判断内网;Pingora 按 TCP peer 判断,只有 peer 为 loopback 的同机 Nginx 时才接受 Nginx 强制覆盖的 `X-Real-IP`。可信内网来源绕过整站维护响应,公网应用主站、普通 API、后台和 SpacetimeDB 路由仍保持维护响应;绝不能用 `X-Forwarded-For` 做放行判断。 +- 验证:Pingora smoke 同时覆盖公网主站、普通 API、后台为 503,以及内网对应路由为 200;Rust 单测覆盖 IPv4 / IPv6 内网、公网和空来源;Nginx 静态门禁反查两份模板的内网来源定义与全局维护变量清零逻辑。 +- 限制:如果发布门禁已经停止 api-server,网关放行后普通 API 和后台 API 仍会失败;需要调用后端时应确保对应服务仍运行,不能把维护页绕过误当作服务可用性保证。 + +## Full 结束后保持维护不能只加一个 UI 参数 + +- 现象:Full Job 参数页没有“完整发布成功后是否退出维护”选项,或者补了选项后 API readiness 一通过仍自动撤掉维护。 +- 原因:维护退出发生在随 API artifact 发布的 `production-api-deploy.sh` 内;Full、API Deploy Job 和脚本任一层没有透传,最终都会回到固定执行 `maintenance-off.sh`。Declarative Pipeline 参数还要等 live Job 加载新版 Jenkinsfile 后才会刷新。 +- 处理:Full 使用 `EXIT_MAINTENANCE_MODE_AFTER_COMPLETION` 表达产品选择,Stdb Publish 和 API Deploy 全程固定保持维护,Web Deploy 成功后才进入独立最终退出阶段;API Deploy 的独立 `KEEP_MAINTENANCE_MODE` 再转换为脚本 `--keep-maintenance-mode`。API deploy 还必须把 `production-api-deploy.sh`、`maintenance-on.sh` 和 `maintenance-off.sh` 从同一 build artifact 复制进 current release,否则 Full 最终阶段即使有选项也找不到随包退出脚本。默认值仍在 Full 结束时退出维护,避免定时 dev 发布行为变化。 +- 验证:API deploy fixture 必须覆盖成功发布并保留 marker,还要断言 current release 中三个部署 / 维护脚本存在;生产运维静态门禁同时反查 Full 参数、下游透传、API Deploy 参数和脚本 flag。推送后用 fail-closed 首阶段运行刷新 live Job 参数,再核对 `config.xml`,不能只看仓库文件。 + +## 遮罩点击关闭必须校验完整指针序列 + +- 现象:在弹窗内容内按下鼠标,拖到弹窗外的遮罩上松开时,弹窗被误关闭。 +- 原因:只在 `click` 阶段判断 `event.target === event.currentTarget` 不足以确认用户点击了遮罩;跨弹窗边界松开时,浏览器可能把合成点击的目标归到弹窗和遮罩的共同祖先。 +- 处理:共享弹窗统一记录 `pointerdown` 与 `pointerup` 的目标,只有按下和松开都发生在遮罩自身时才允许关闭。新增弹窗优先复用 `UnifiedModal`,不要继续复制只判断最终 `click` 目标的手写遮罩逻辑。 +- 验证:回归测试同时覆盖“弹窗内按下、遮罩松开不关闭”和“遮罩按下、遮罩松开正常关闭”。 +- 关联:`src/components/common/UnifiedModal.tsx`、`src/components/common/UnifiedModal.test.tsx`、`src/components/auth/PlatformAuthModalShell.test.tsx`。 diff --git a/docs/project-memory/shared-memory/project-overview.md b/docs/project-memory/shared-memory/project-overview.md index 674dac26e..fa617a0c1 100644 --- a/docs/project-memory/shared-memory/project-overview.md +++ b/docs/project-memory/shared-memory/project-overview.md @@ -36,7 +36,7 @@ Genarrative / 陶泥儿是一个 AI 原生互动内容与小游戏平台,把 A server-rs + Axum + SpacetimeDB ``` -当前 SpacetimeDB crate、SDK、CLI / standalone、生成 bindings 和容器压测镜像统一按 `2.5.0` 对齐;遇到版本不匹配时先升级到 `server-rs/Cargo.toml` 锁定版本,升级后重启对应 SpacetimeDB 进程再重试。 +当前 SpacetimeDB crate、SDK、CLI / standalone、生成 bindings 和容器压测镜像统一按 `2.6.0` 对齐;遇到版本不匹配时先升级到 `server-rs/Cargo.toml` 锁定版本,升级后重启对应 SpacetimeDB 进程再重试。 职责边界: diff --git a/docs/technical/【SEO】官网SEO地基实施约定-2026-07-10.md b/docs/technical/【SEO】官网SEO地基实施约定-2026-07-10.md new file mode 100644 index 000000000..3cc1d9e0e --- /dev/null +++ b/docs/technical/【SEO】官网SEO地基实施约定-2026-07-10.md @@ -0,0 +1,52 @@ +# 官网 SEO 地基实施约定 + +更新时间:`2026-07-10` + +## 本轮边界 + +本轮只建立陶泥儿中文首页的 SEO 基础设施,不新增英文站、长尾落地页、SSR、动态 sitemap、前端访问埋点或分享图。 + +- `public/robots.txt` 是真实静态文件;非 SEO 产品路径本轮阻止抓取,但不把 `Disallow` 解释为保证不收录。 +- `public/sitemap.xml` 本轮只包含 `https://www.genarrative.world/`,不写难以持续维护的 `lastmod`。 +- `index.html` 提供首页 title、description、canonical、robots、基础 OG/Twitter 文本和 JSON-LD;没有正式分享图时不写 `og:image`、`twitter:image` 或 JSON-LD `logo`。 +- 桌面首页与 `/creation` 创作主页渲染后只有一个稳定、可见的产品定位 H1,固定为 `陶泥儿 · 开启全民精品游戏创作`;小眉题、产品说明、权益提示、动态作品名、卡片标题、按钮内部标题、弹窗标题和隐藏 Tab 不作为 H1。 +- H1 下方真实展示游戏美术 AI 工作台、美术 Agent、无限画布以及角色、场景、UI、宣发素材说明,不使用透明、极小、屏幕外或页面底部堆词文本;`游戏美术 AI 创作工具` 使用 H2,能力卡标题使用 H3。 +- 本轮可见首页文案调整不得删除或改写已经验收的 title、description、canonical、robots、OG/Twitter 文本和 JSON-LD。 +- Nginx 和 Pingora 只对当前已知完整 SPA 路径回退 `index.html`;未知路径以及 `/creation/not-exist` 等同前缀未知路径返回 HTTP 404。 +- Nginx access log 继续使用现有 `$http_referer`,本轮不新增前端 pageview 或用户身份采集。 + +## 路由事实源 + +主站 SPA 路由以以下源码为事实源: + +- `src/routing/appRoutes.tsx` +- `src/routing/appPageRoutes.ts` + +`/creation/rpg/agent` 仍被现有刷新恢复链路使用,当前作为兼容深链保留。新增或删除前端路由时,必须同步三套 Nginx 配置、Pingora 路由、`deploy/pingora/nginx-route-parity.matrix.json` 和对应自动门禁。不得把 `/creation/*`、`/runtime/*` 等一级目录整体设为 SPA fallback。 + +后续新增 SEO 落地页时,还必须同时满足:返回 200、不被 robots.txt 阻止抓取、加入 sitemap,并提供独立 title、description、canonical、H1、正文和内链入口。纯 SPA 页面需要独立 head 时,应评估构建时静态 HTML、预渲染或 SSR。 + +## 验收口径 + +```bash +npm run build +npm run typecheck +npm run check:nginx-spa-routes +npm run check:pingora-route-parity +npm run check:pingora-gateway-smoke +npm run check:encoding +git diff --check +``` + +部署后至少验证: + +```bash +curl -I https://www.genarrative.world/robots.txt +curl -I https://www.genarrative.world/sitemap.xml +curl -I https://www.genarrative.world/not-exist-test +curl -I https://www.genarrative.world/creation/not-exist-test +curl -I https://www.genarrative.world/runtime/not-exist-test +curl -I https://www.genarrative.world/puzzle/not-exist-test +``` + +真实 SPA 路径不得误 404;以上未知路径必须返回 404。首页浏览器 DOM 应只有一个 H1,并确认桌面和移动端布局、导航、推荐流和创作入口没有因 SEO 文案变形。 diff --git a/docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md b/docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md index b9e760512..d180077eb 100644 --- a/docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md +++ b/docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md @@ -9,7 +9,7 @@ - 主站新增 `/editor/canvas` 路由,进入独立图片画布编辑器阶段。 - 主站新增 `/project` 项目页,从“我的”页项目入口进入,展示当前用户所有图片画布工程;点击项目进入 `/editor/canvas?projectid=`。 - 创作 Tab 顶部提供编辑器入口,入口只负责跳转,不参与玩法创作链路。 -- 编辑器顶部栏采用紧凑高度,项目标题和重命名入口贴近返回项目按钮;右侧常驻展示当前账号泥点余额,样式对齐创作主页顶部钱包 chip,点击后复用创作主页右上角泥点入口的账户充值 / 兑换码弹窗与支付确认反馈。 +- 编辑器顶部栏采用紧凑高度,项目标题和重命名入口贴近返回项目按钮;右侧复用与主站相同的公共泥点资产入口,并以充值中心 `mudPointBalance` 为余额真相源。余额区只展开不限时、每日免费及重置信息,会员周期限时泥点保留在后端 read model 中用于存量兼容和结算但不展示;独立“充值”按钮进入“购买更多泥点”弹窗,“使用详情”进入泥点账单。 - 编辑器左侧为图片素材栏,可展开 / 收起;移动端优先保持素材栏可折叠。 - 中央画布支持背景拖拽平移、滚轮缩放、缩放百分比菜单、显示所有元素和固定比例缩放。 - 画布左下角提供 Lovart 式状态控件:背景色圆点、素材 / 图层入口、小地图开关;小地图显示图层缩略分布和当前视口框,点击小地图执行显示所有元素。 @@ -86,8 +86,8 @@ - `POST /api/editor/images/generations`:按提示词调用 VectorEngine 生成图片;角色生成可携带 `model`、`screenColor`、`segModel`、`aspectRatio`、`imageSize` 和 `referenceImageSrcs`,生成成功后 api-server 先保存带纯色背景源图,再调用 BgFilter 并传入 `screen_color=`、`seg_model=` 生成透明 PNG。宣发素材携带 `kind: "publication-material"` 时固定归一为 `gpt-image-2`,不支持 `nanobanana2`。`nanobanana2` 参考图作为 `inline_data` 进入 `generateContent`,`gpt-image-2` 参考图进入 edits。普通重绘继续走该接口并把当前图层图片作为参考图;图片快速编辑不走该接口。请求可携带 `projectId`、`assetFolderId`、`assetKind`、`generationInputs` 和 `sourceResourceId`,后端生成成功后创建 project resource / 账号素材并在响应中返回 resource / asset 快照。 - `POST /api/editor/images/background-removals`:接收当前图片源,校验登录态后由 api-server 解析为图片文件并转发到 BiRefNet 去背景服务;请求可携带 `projectId`、`targetLayerId`、`assetFolderId`、`assetLabel`、`sourceResourceId` 和 `canvasCompletion`,有 `canvasCompletion` 时完成后按生成占位写入结果图层,否则沿用旧的目标图层替换路径;响应返回 `imageSrc`、`objectKey`、`assetObjectId`、`width`、`height`、`taskId`、`elapsedMs`、`provider` 和可选 `project` 快照。服务地址由 `GENARRATIVE_EDITOR_BACKGROUND_REMOVAL_BASE_URL` 配置,令牌只在服务端通过 `GENARRATIVE_EDITOR_BACKGROUND_REMOVAL_TOKEN` 注入。 - `POST /api/editor/icon-spritesheets/generations`:按图标规范图和素材描述数组生成 spritesheet,生成成功后 api-server 先保存带纯色背景 spritesheet 源图,再调用 BgFilter 生成透明 spritesheet。请求支持 `model`、`screenColor`、`segModel`、`aspectRatio`、`imageSize`、`priceMudPoints`、`projectId`、`assetFolderId` 和 `generationInputs`;`priceMudPoints` 必须来自编辑器生成计费配置中对应生图模型的尺寸档位(如 `nanobanana2` 的 `0.5K / 1K / 2K` 或 `gpt-image-2` 的 `1K / 2K`),后端用 `editor_generation_config` 校验后才调用上游;`nanobanana2` 走原生 `generateContent` 并写入 `generationConfig.imageConfig.aspectRatio/imageSize`,`0.5K` 传 `"512"`;`gpt-image-2` 走 `/v1/images/edits`。后端保存透明 spritesheet project resource / 账号素材,并随响应返回对应快照。 -- `POST /api/editor/ui-designs/assets/extractions`:以前端已绘入红色框选轮廓的 UI 设计图 Data URL 作为参考图,固定 `gpt-image-2` 和自动决策纯色背景素材提取提示词生成素材 spritesheet,生成成功后 api-server 先保存带纯色背景 spritesheet 源图,再调用 BgFilter 生成透明 spritesheet,并按连通域自动拆分为 `素材 1..N`,返回结构复用图标 spritesheet 响应。请求必须携带 `screenColor`、`segModel`、`aspectRatio: "1:1"`、`imageSize: "1K" | "2K"` 和 `priceMudPoints`;框选数量不超过 6 个时前端按 `1:1·1K` 与 gpt-image-2 1K 价格提交,超过 6 个时按 `1:1·2K` 与 2K 价格提交。后端必须在调用上游前校验比例、尺寸和泥点价格,只允许 `1:1 / 1K / 2K`。请求可携带 `projectId`、`assetFolderId`、`generationInputs` 和 `spritesheetLabel`,后端保存 spritesheet / 拆分素材并返回对应 resource / asset 快照;前端必须把 spritesheet 原图与拆分素材都加入画布。 -- `POST /api/editor/images/edits`:按提示词和当前图片 Data URL 调用 VectorEngine edits,返回新的生成图片元数据;接口能力仍可接收明确参考图,但图片快速编辑当前只提交 `sourceImageSrc`,不提交隐藏的 `referenceImageSrcs`。请求携带 project / asset 上下文时由后端创建新 resource / asset,前端只消费响应快照。 +- `POST /api/editor/ui-designs/assets/extractions`:前端把红色框选轮廓绘入本地临时图后,先将该图上传 OSS 并确认 asset object,再以返回的 `objectKey` 作为参考图入队;Data URL / Blob URL 只允许停留在上传前的浏览器临时态。接口固定 `gpt-image-2` 和自动决策纯色背景素材提取提示词生成素材 spritesheet,生成成功后 api-server 先保存带纯色背景 spritesheet 源图,再调用 BgFilter 生成透明 spritesheet,并按连通域自动拆分为 `素材 1..N`,返回结构复用图标 spritesheet 响应。请求必须携带 `screenColor`、`segModel`、`aspectRatio: "1:1"`、`imageSize: "1K" | "2K"` 和 `priceMudPoints`;框选数量不超过 6 个时前端按 `1:1·1K` 与 gpt-image-2 1K 价格提交,超过 6 个时按 `1:1·2K` 与 2K 价格提交。后端必须在调用上游前校验比例、尺寸和泥点价格,只允许 `1:1 / 1K / 2K`。请求可携带 `projectId`、`assetFolderId`、`generationInputs` 和 `spritesheetLabel`,后端保存 spritesheet / 拆分素材并返回对应 resource / asset 快照;前端必须把 spritesheet 原图与拆分素材都加入画布。 +- `POST /api/editor/images/edits`:按提示词和当前图片的已登记 `objectKey` / `resourceId` 调用 VectorEngine edits,返回新的生成图片元数据;图片快速编辑当前只提交 `sourceImageSrc`,不提交隐藏的 `referenceImageSrcs`。画布快速编辑必须把源图精确 `originalWidth x originalHeight` 作为业务目标 `size` 提交,不能重新映射为近似比例或 1K / 2K 预设;api-server 在 VectorEngine provider 边界把目标尺寸和所有 multipart 参考图临时补齐到 16 的倍数,回图后恢复到业务目标精确尺寸,再落 OSS、project resource、账号素材和画布快照。16 对齐尺寸不得泄漏到响应、持久化资源或图层 Resolution。本地红框标记图必须先上传再提交 objectKey;请求携带 project / asset 上下文时由后端创建新 resource / asset,前端只消费响应快照。 - `POST /api/editor/videos/generations`:按视频描述、模型、比例、时长、分辨率、模式、声音、默认联网搜索标记和泥点价格生成视频。前端可选模型为 `seedance2.0-fast`、`seedance2.0`、`kling3.0`、`kling3.0-omni`,默认 `seedance2.0-fast`;后端必须将 `seedance2.0-fast` 映射到 `doubao-seedance-2-0-fast-260128`,将 `seedance2.0` 映射到 `doubao-seedance-2-0-260128`,两者不得混用。后端允许 6 类比例、4 到 15 秒整数、`480p / 720p / 1080p`,并拒绝 `seedance2.0-fast + 1080p`;`sound=on/off` 映射 Ark `generate_audio=true/false`。后端复用 Ark / VectorEngine content generation task 轮询链路,下载最终视频并持久化到 OSS;请求携带 `projectId` / `assetFolderId` 时同步创建 project resource / 账号素材并返回 `project` / `asset` 快照,基础响应返回 `videoSrc`、尺寸、prompt、model、provider、taskId、durationSeconds、resolution 和 `priceMudPoints`。 - `POST /api/editor/audios/sound-effects/generations` 与 `POST /api/editor/audios/background-music/generations`:按音效 / 背景音乐参数生成音频并持久化到 OSS;请求携带 `projectId` / `assetFolderId` 时同步创建 project resource / 账号素材并返回 `project` / `resource` / `asset` 快照,基础响应返回 `audioSrc`、prompt、model、provider、taskId、duration、歌词和 `priceMudPoints`。 @@ -128,7 +128,7 @@ - 画布 Agent 会话刷新后能从后端恢复会话标题、消息、附件和生成记录;前端不得根据本地临时状态伪造会话持久化结果。 - 图片选中后的浮动工具栏按钮顺序固定为:快速编辑、分割线、裁扩按钮、去除背景按钮、UI设计图专属提取素材、角色图专属生成动画、分割线、重绘、下载按钮。裁扩通过画布边界拖拉完成,不再展示四边数值输入;默认自由比例,选择固定比例后拖拉边界保持对应比例,完成后在原素材旁边新增裁扩结果图层,扩展区域透明填充。去除背景调用同源 BFF `POST /api/editor/images/background-removals`,由 api-server 代理远端 BiRefNet 服务并持久化结果;有项目上下文时先在画布创建关闭面板的去背景生成占位,完成后由后端通过 `canvasCompletion` 把新 project resource 写入该占位并返回快照,无占位上下文时才用新的 project resource 引用替换当前图层。画布任务侧栏按“排队/生成中”和“已完成”分页,生成中排在排队前,生成中耗时从任务开始时间戳实时计算,排队中不计时;进行中任务只显示阶段文本和已用时,不显示百分比;完成态生成任务副标题显示用户提示词并单行截断;点击任务只聚焦对应画布内容,不激活生成面板或改变任务顺序,聚焦时必须预留图片上方工具栏、底部工具栏和可见生成对话框空间。UI设计图的提取素材必须先进入红框素材框选状态,默认启用矩形框选,右侧框选工具与快速编辑统一且可再次点击取消启用态,当前启用工具按钮必须保持高亮。素材提取面板必须在素材下方,使用与生成新素材一致的面板宽度和底部模型 / 按钮样式,提示语显示 `使用框选工具框选你希望从画面中提取的素材`,并展示按原图坐标准确裁剪的框选区域截图预览、固定模型 `gpt-image-2`、左下角计划规格 `1:1·1K/2K` 和 `提取 · N泥点` 按钮,不显示额外取消按钮;点击素材和面板以外的画布区域即退出 UI 素材提取。至少框选一个区域后才可提交,前端把红色轮廓绘入原图后固定走 `gpt-image-2` 和自动决策纯色背景素材提取提示词;生成的透明 spritesheet 原图和拆分后的独立素材都作为画布图层保留。 - 重绘生成资源后,右侧出现新生成结果图层,并自动 fit 原图 + 新图,且重绘面板保持打开。 -- 快速编辑 / 重绘站内 public 示例图、历史 generated 图或 OSS generated 图时,前端先读取成 `data:image/*;base64,...` 再提交,后端不得再收到 `/creation-type-references/*`、`/generated-*` 或 OSS URL 作为 `referenceImageSrcs/sourceImageSrc`。 +- 快速编辑 / 重绘站内 public 示例图、历史 generated 图或 OSS generated 图时,优先复用当前图层已有 `objectKey` / `resourceId` / `sourceAssetId`;只有尚未登记的浏览器本地图片才先上传并取得 objectKey。前端不得再把正式对象下载成 `data:image/*;base64,...` 后提交,也不得把 Data URL / Blob URL 写入外部生成持久任务 JSON;后端收到引用后统一做 owner 归属校验并签名读取。 - 快速编辑不保留额外参考图入口;点击修改时只把原图或红框序号标注图作为 `/api/editor/images/edits` 的 `sourceImageSrc` 提交给后端。 - 素材文件夹可以新建、折叠、重命名和删除;删除普通文件夹后,其素材移动到“项目素材”。普通上传默认落入“上传素材”文件夹;素材库缺少该文件夹时,前端在首次普通上传前创建一次并复用,拖到指定文件夹或点击指定文件夹上传时仍进入目标文件夹。 - 上传按钮和拖拽上传都支持多文件;底部工具栏的上传入口选择文件后直接进入“上传素材”并在当前画布视口中心创建画布图层,素材栏文件夹内的上传入口只写入对应素材文件夹、不自动入画布;拖到文件夹或该文件夹内素材时进入目标文件夹;拖到画布时进入“上传素材”并在投放点创建画布图层。上传图片必须在创建占位素材、画布图层和账号级素材记录前先读取原图 Resolution,图层宽高、`originalWidth/originalHeight` 和素材库 `width/height` 都使用图片本身尺寸;上传视频同样在创建素材和图层前读取视频 metadata 宽高,保证单层下载或 ZIP 导出的真实视频文件重新导入后仍按文件自身尺寸入画布;仅在无法解析尺寸时才使用对应媒体兜底尺寸。 diff --git a/docs/technical/【开发运维】Pingora独立网关试点-2026-06-11.md b/docs/technical/【开发运维】Pingora独立网关试点-2026-06-11.md index 4e22e53b8..7a11eb150 100644 --- a/docs/technical/【开发运维】Pingora独立网关试点-2026-06-11.md +++ b/docs/technical/【开发运维】Pingora独立网关试点-2026-06-11.md @@ -42,6 +42,7 @@ cargo run -p pingora-gateway --manifest-path server-rs/Cargo.toml ```bash npm run check:pingora-gateway-smoke +npm run check:nginx-spa-routes npm run check:pingora-route-parity npm run check:nginx-pingora-canary npm run check:pingora-canary-docker @@ -59,9 +60,11 @@ npm run check:pingora-cutover-evidence-audit npm run check:pingora-release-readiness ``` -`check:pingora-gateway-smoke` 会临时启动 mock `api-server`、mock SpacetimeDB、mock Gitea 和 `pingora-gateway`,覆盖 SPA fallback、后台静态路由、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: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:pingora-route-parity` 读取 `deploy/pingora/nginx-route-parity.matrix.json`,静态确认生产 / 开发 Nginx 模板、Pingora Rust 路由单测和本文档都覆盖同一组核心路由。`cargo test -p pingora-gateway --manifest-path server-rs/Cargo.toml matches_nginx_route_parity_matrix` 会读取同一份矩阵,逐条断言 `classify_path` 的路由结果、body limit 和接流保护分组。 +`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:pingora-route-parity` 会先执行同一 Nginx SPA 路由门禁,再读取 `deploy/pingora/nginx-route-parity.matrix.json`,静态确认生产 / 开发 Nginx 模板、Pingora Rust 路由 allowlist / 单测和本文档都覆盖同一组核心路由。`cargo test -p pingora-gateway --manifest-path server-rs/Cargo.toml matches_nginx_route_parity_matrix` 会读取同一份矩阵,逐条断言 `classify_path` 的路由结果、body limit 和接流保护分组。 `check:nginx-pingora-canary` 会静态校验 `deploy/nginx/snippets/genarrative-pingora-canary.conf` 的本机来源限制、handoff 响应头、probe token 占位、前缀 rewrite、低缓冲和 WebSocket Upgrade 设置,也会校验 `deploy/nginx/snippets/genarrative-pingora-realpath-canary.conf` 只能作为独立 loopback `server` 片段使用、默认监听 `127.0.0.1:18083`、写独立 access log、没有 rewrite、覆盖真实 `/api` / `/v1` / `/assets` 代表路径。本机安装了 Nginx 时脚本会额外把两个 snippet 包进临时 `http {}` 执行 `nginx -t`;需要在 CI / 目标 agent 上强制要求真实 Nginx 语法检查时执行 `node scripts/check-nginx-pingora-canary.mjs --require-nginx`。 @@ -539,9 +542,10 @@ 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,保持生产公网不暴露口径。 | -| 其它路径 | 先读取静态文件或目录 index,失败回退 `/index.html`,HTML 默认 `no-cache`,并支持条件请求返回 `304` 与单段 `Range: bytes=` 返回 `206` / 越界返回 `416`。 | +| 主站 SPA allowlist | 只对当前前端完整路由及兼容恢复路径 `/creation/rpg/agent` 失败回退 `/index.html`;匹配大小写不敏感并允许一个尾部斜杠,HTML 默认 `no-cache`。 | +| 其它 Web 路径 | 只读取真实静态文件或目录 index,缺失时返回真实 404;`/creation/not-exist`、`/runtime/not-exist`、`/puzzle/not-exist` 不进入 SPA fallback。 | -维护模式下,API-like 路由返回 JSON `503`,Web 静态路由优先返回 `maintenance.html`,不存在时返回纯文本 `503`。 +维护模式下,公网 API-like 路由返回 JSON `503`,公网 Web 静态路由优先返回 `maintenance.html`,不存在时返回纯文本 `503`。IPv4 loopback / RFC1918 / link-local 和 IPv6 loopback / ULA / link-local 来源绕过整站维护闸,主站页面与静态资源、普通 API、后台页面与后台 API、SpacetimeDB 路由均按非维护状态继续处理;应用层登录、管理员鉴权和其它业务鉴权保持不变。Pingora 直连按 TCP peer 判定来源;仅当 peer 是 loopback 的同机 Nginx 时才接受 Nginx 强制覆盖的 `X-Real-IP`,绝不使用客户端可伪造的 `X-Forwarded-For` 做维护放行。该放行只绕过网关维护响应;若 `pause-after-stdb` 已停止 api-server,内网普通 API 和后台 API 仍不可用。 代理失败时,API / SpacetimeDB 等代理路由返回统一 JSON 网关错误;本地静态路由仍保持对应 HTTP 错误状态。 静态 `Range` 只支持单段 bytes range;多段 range 暂按完整文件返回,避免在正式替换前引入 multipart 响应面。`If-None-Match` / `If-Modified-Since` 优先于 `Range` 判定,命中时仍返回 `304`;`If-Range` 日期匹配时继续返回 `206`,日期旧于文件或弱 ETag 校验器时回完整 `200`;`206` / `304` / `416` 不做 gzip 压缩,避免 `Content-Range` 语义被响应体改写破坏。Gateway smoke 会用固定 `X-Request-Id` 对账静态 `304`、`405`、`206`、`416` 的 Pingora access log 行,确认本地响应状态也进入正式切换证据链。 静态路由只允许 `GET` / `HEAD` 读取;其它方法在确认命中静态候选后返回 `405` 并写入 `Allow: GET, HEAD`,缺失文件仍返回 `404`,避免直连后错误客户端把静态入口当作可写接口。 diff --git a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md index 85e2b93fe..b833ab561 100644 --- a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md +++ b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md @@ -16,7 +16,7 @@ server-rs + Axum + SpacetimeDB `server-rs/Cargo.toml` 是 workspace 事实源。默认构建成员为 `crates/api-server`;第三方依赖版本和 workspace 内 crate path 统一放在 `[workspace.dependencies]`。 -SpacetimeDB 版本口径:当前 Rust crate `spacetimedb`、`spacetimedb-sdk`、`spacetimedb-lib` 统一锁定 `2.5.0`;本地 `spacetime` CLI / standalone、生成的 `spacetime-client` bindings 和容器压测镜像也必须与 `server-rs/Cargo.toml` 锁定版本对齐,避免 BSATN / procedure result 反序列化错配。遇到版本不匹配时,不继续沿着业务超时排查,先把 CLI / standalone 直接升级到锁定版本并重启后再重试。 +SpacetimeDB 版本口径:当前 Rust crate `spacetimedb`、`spacetimedb-sdk`、`spacetimedb-lib` 统一锁定 `2.6.0`;本地 `spacetime` CLI / standalone、生成的 `spacetime-client` bindings 和容器压测镜像也必须与 `server-rs/Cargo.toml` 锁定版本对齐,避免 BSATN / procedure result 反序列化错配。遇到版本不匹配时,不继续沿着业务超时排查,先把 CLI / standalone 直接升级到锁定版本并重启后再重试。 当前主要 crate: @@ -189,18 +189,20 @@ npm run check:server-rs-ddd ## 账户充值数据契约 1. `profile_recharge_product_config` 是泥点和会员商品配置真相源,默认商品只在表为空时由 SpacetimeDB 播种。`module-runtime` 中的默认商品 helper 只作为空库种子和兼容入口,不再作为运行期业务真相。 -2. 后台通过 `/admin/api/profile/recharge-products` 读写充值商品配置;字段覆盖 `productId`、标题、商品类型、金额分、基础泥点、首充赠送泥点、会员天数、徽标、说明、会员层级、会员每周期限时泥点、周期天数、队列上限、折扣率、启用状态和排序。 -3. 充值中心、下单校验和支付确认入账都读取 `profile_recharge_product_config`。历史订单保留下单时写入的商品标题、金额、渠道、状态和 provider transaction id,不随配置改动回写。 -4. 泥点首充资格按 `user_id + product_id` 的历史 `paid` 订单独立判断。某个档位已支付后,只隐藏该档位的首充赠送;其它未购买档位仍展示和结算首充赠送。 -5. `hasPointsRecharged` 只保留为账号是否发生过任一泥点充值的兼容字段,不得驱动所有商品展示隐藏或结算金额计算。前端只渲染后端返回的商品快照。 -6. 默认会员商品为空库播种时使用 `Starter / Basic / Pro / Ultimate` 四档,默认有效期均为 30 天,每周期限时泥点分别为 `200 / 800 / 2500 / 6000`,队列上限分别为 `2 / 2 / 5 / 10`。 -7. 会员有效期和周期重置时间是两条独立时间线。`expires_at` 只决定会员是否生效;`cycle_resets_at` 只决定当前周期限时泥点何时重置。会员升级只更新档位并补齐当前周期限时泥点差额,不延长 `expires_at`,不移动 `cycle_resets_at` 和周期天数。同级会员购买只从当前 `expires_at` 延长有效期,不发放额外当前周期泥点,也不移动重置时间。 -8. 会员周期刷新发生在个人中心、充值中心、任务中心、账单读取和钱包扣费入口:到达 `cycle_resets_at` 时先清除上周期剩余限时泥点,再发放当前会员档位周期额度;会员过期时清除剩余限时泥点并把状态降为普通。周期发放和重置流水分别使用 `membership_period_grant`、`membership_period_reset`。 -9. `paymentChannel` 缺失、未知或冒用小程序支付设备时必须拒绝;真实微信渠道只允许 `wechat_mp`、`wechat_mp_virtual`、`wechat_jsapi`、`wechat_h5`、`wechat_native`,生产配置不得把真实支付静默降级为 `mock`。 -10. access JWT 只携带最小设备快照 `device.client_type`、`device.client_runtime`、`device.client_platform`。充值下单按该快照拦截小程序渠道:小程序只允许 `wechat_mp` / `wechat_mp_virtual`;移动网页和微信内 H5 走 `wechat_h5`;桌面网页和桌面微信走 `wechat_native`;`wechat_jsapi` 仅保留后端能力,未接微信开放平台前不由前端自动选择。历史普通 Web 登录态若缺少设备快照也允许继续进入 JSAPI / H5 / Native 渠道的后续支付配置校验,但不放宽小程序虚拟支付。 -11. 所有微信真实渠道都以微信支付通知或服务端查单确认 `SUCCESS` 为到账事实;小程序、H5 跳转和 Native 二维码返回都不能直接发放泥点或会员。 -12. 微信 JSAPI / H5 / 小程序 / Native 下单统一显式传 5 分钟 `time_expire`,格式为 RFC3339 秒级时间;Native 额外通过 `wechatNativePayment.expiresAt` 下发给前端二维码弹窗展示。 -13. 真实微信渠道的新建 pending 充值订单会写入 SpacetimeDB 原生 scheduled 表 `profile_recharge_order_expiration_timer`。到期 reducer 只做数据库内状态转换:订单仍为 `pending` 时更新为 `expired` 并写 `expired_at`,同时删除 timer。HTTP `api-server` 订阅 `profile_recharge_order` 的 `Pending -> Expired` 更新并执行微信查单补偿:`SUCCESS` 可把 `expired` 补确认成 `paid` 入账;`NOTPAY` 会调用微信关单并把本地订单保持为 `expired`;`CLOSED` / `REVOKED` / `PAYERROR` / `ORDER_NOT_EXIST` 只记录检查结果。`external-generation-worker` / controller 不处理充值过期;`wechat_mp_virtual` 到期只记录虚拟渠道不可查,后续真实支付通知仍允许 `Expired -> Paid`。 +2. 默认泥点商品固定为四档:`points_60 = 60 泥点 / 600 分`、`points_180 = 180 + 90 泥点 / 1800 分`、`points_300 = 300 + 150 泥点 / 3000 分`、`points_680 = 680 + 340 泥点 / 6800 分`。`points_60` 的首充赠送为 `0`;后三档首次购买分别赠送基础泥点的 `50%`。 +3. 后台通过 `/admin/api/profile/recharge-products` 读写充值商品配置;字段覆盖 `productId`、标题、商品类型、金额分、基础泥点、首充赠送泥点、会员天数、徽标、说明、会员层级、会员每周期限时泥点、周期天数、队列上限、折扣率、启用状态和排序。 +4. 充值中心、下单校验和支付确认入账都读取 `profile_recharge_product_config`。充值中心 BFF 还必须在 `mudPointBalance` 下发 `totalPoints`、`permanentPoints`、`limitedPoints`、`limitedExpiresAt`、`dailyFreePoints`、`dailyFreeResetPoints` 和 `dailyFreeResetsAt`;前端以该 read model 为真相源,不得自行用总额相减推算余额桶。当前版本公开 UI 只渲染不限时泥点和每日免费泥点,`limitedPoints` 与 `limitedExpiresAt` 仅保留给存量兼容和后端结算。历史订单保留下单时写入的商品标题、金额、渠道、状态和 provider transaction id,不随配置改动回写。 +5. 泥点首充资格按 `user_id + product_id` 的历史 `paid` 订单独立判断。某个档位已支付后,只隐藏该档位的首充赠送;其它未购买档位仍展示和结算首充赠送。 +6. `hasPointsRecharged` 只保留为账号是否发生过任一泥点充值的兼容字段,不得驱动所有商品展示隐藏或结算金额计算。前端只渲染后端返回的商品快照。 +7. 当前版本公开充值 UI 只展示泥点商品,不渲染会员购买页签、会员商品、购买会员或升级会员入口。充值中心响应中的会员商品兼容字段、默认会员商品、`profile_membership` 和周期刷新逻辑继续保留;存量会员的 `cycle_remaining_points` 仍通过充值中心 read model 下发用于兼容和结算,但不作为限时泥点在当前版本前台展示。 +8. 默认会员商品为空库播种时使用 `Starter / Basic / Pro / Ultimate` 四档,默认有效期均为 30 天,每周期限时泥点分别为 `200 / 800 / 2500 / 6000`,队列上限分别为 `2 / 2 / 5 / 10`。 +9. 会员有效期和周期重置时间是两条独立时间线。`expires_at` 只决定会员是否生效;`cycle_resets_at` 只决定当前周期限时泥点何时重置。会员升级只更新档位并补齐当前周期限时泥点差额,不延长 `expires_at`,不移动 `cycle_resets_at` 和周期天数。同级会员购买只从当前 `expires_at` 延长有效期,不发放额外当前周期泥点,也不移动重置时间。 +10. 会员周期刷新发生在个人中心、充值中心、任务中心、账单读取和钱包扣费入口:到达 `cycle_resets_at` 时先清除上周期剩余限时泥点,再发放当前会员档位周期额度;会员过期时清除剩余限时泥点并把状态降为普通。周期发放和重置流水分别使用 `membership_period_grant`、`membership_period_reset`。 +11. `paymentChannel` 缺失、未知或冒用小程序支付设备时必须拒绝;真实微信渠道只允许 `wechat_mp`、`wechat_mp_virtual`、`wechat_jsapi`、`wechat_h5`、`wechat_native`,生产配置不得把真实支付静默降级为 `mock`。 +12. access JWT 只携带最小设备快照 `device.client_type`、`device.client_runtime`、`device.client_platform`。充值下单按该快照拦截小程序渠道:小程序只允许 `wechat_mp` / `wechat_mp_virtual`;移动网页和微信内 H5 走 `wechat_h5`;桌面网页和桌面微信走 `wechat_native`;`wechat_jsapi` 仅保留后端能力,未接微信开放平台前不由前端自动选择。历史普通 Web 登录态若缺少设备快照也允许继续进入 JSAPI / H5 / Native 渠道的后续支付配置校验,但不放宽小程序虚拟支付。 +13. 所有微信真实渠道都以微信支付通知或服务端查单确认 `SUCCESS` 为到账事实;小程序、H5 跳转和 Native 二维码返回都不能直接发放泥点或会员。 +14. 微信 JSAPI / H5 / 小程序 / Native 下单统一显式传 5 分钟 `time_expire`,格式为 RFC3339 秒级时间;Native 额外通过 `wechatNativePayment.expiresAt` 下发给前端二维码弹窗展示。 +15. 真实微信渠道的新建 pending 充值订单会写入 SpacetimeDB 原生 scheduled 表 `profile_recharge_order_expiration_timer`。到期 reducer 只做数据库内状态转换:订单仍为 `pending` 时更新为 `expired` 并写 `expired_at`,同时删除 timer。HTTP `api-server` 只订阅这张活跃 timer 表的删除事件,收到 `order_id` 后通过 procedure 重新读取订单,只有状态确认为 `expired` 才执行微信查单补偿;支付或主动关闭同样会删除 timer,但会被状态判断忽略。监听断线期间遗漏的删除事件由未检查过期订单 catch-up 补齐,不订阅完整 `profile_recharge_order` 历史表。`SUCCESS` 可把 `expired` 补确认成 `paid` 入账;`NOTPAY` 会调用微信关单并把本地订单保持为 `expired`;`CLOSED` / `REVOKED` / `PAYERROR` / `ORDER_NOT_EXIST` 只记录检查结果。`external-generation-worker` / controller 不处理充值过期;`wechat_mp_virtual` 到期只记录虚拟渠道不可查,后续真实支付通知仍允许 `Expired -> Paid`。 ## 创作入口泥点扣费契约 @@ -215,13 +217,14 @@ npm run check:server-rs-ddd ## 用户钱包与编辑器生成扣费契约 1. 新用户账号完成注册并成功同步正式认证表后,注册赠送金额读取 `profile_wallet_config.initial_mud_points`;后台通过 `/admin/api/profile/wallet-config` 维护“账号初始泥点数”。未写入配置时默认仍为 `100` 泥点。流水原因仍使用 `new_user_registration_reward`,流水 ID 继续保持幂等,重复发放请求不得叠加余额。 -2. 用户钱包余额对外仍暴露为一个总余额,但后端扣费时优先消耗 `profile_membership.cycle_remaining_points` 中的会员周期限时泥点,再消耗普通永久泥点;扣费流水 `metadata_json` 会记录 `membershipPeriodPointsDelta`、`permanentPointsDelta` 和限时泥点所属 `cycleResetsAtMicros`,退款会按原消费流水优先恢复同一周期的限时泥点,前端不得自行决定扣费桶。 -3. 每日免费泥点当前由每日任务体系发放,流水来源为 `daily_task_reward`,任务进度和可领取状态按北京时间每日刷新;它不参与会员 `cycle_resets_at`,也不由前端合并进会员周期泥点。 -4. 编辑器画板所有会调用外部生成 provider 的入口都不从前端请求接收 `priceMudPoints`;同步请求以 SpacetimeDB `editor_generation_pricing_config` 当前全局配置计算,外部生成队列则以 `external_generation_job.price_mud_points` 保存的入队价格为准,worker 的扣费、退款、响应和资产成本不得按执行时配置重算。前端按钮泥点只作为展示。 -5. 编辑器图片生成 / 图片修改 / 图标 spritesheet / UI 设计图提取素材 / 视频 / 角色动作 / 音效 / 背景音乐必须在后端计算模型价格后使用 `execute_billable_asset_operation_with_cost` 预扣泥点;预扣失败必须 fail-closed,不得继续提交 VectorEngine、Ark、Suno 或 Vidu 上游任务。 -6. 队列任务按 `job_id + claim_attempt` 使用独立 consume/refund ledger。新 attempt 结算旧 attempt 时必须先写 `asset_operation_wallet_settlement`:旧 consume 已存在则原子退款,尚不存在则写取消 intent;迟到 consume 在同一 SpacetimeDB 事务内看到 intent 后必须失败关闭。重复 consume/refund 只有用户、金额、来源和配对 ledger 全部一致时才可视为幂等成功。lease 过期时只有 `attempt < max_attempts` 才能递增并重领;最终 attempt 已耗尽时,claim transaction 必须直接把 job 收口为 `failed`、清理 lease、写失败事件并结算当前 attempt,不能再把任务返回 worker 或调用 provider。 -7. 音频生成的编辑器链路虽然任务提交和结果发布分离,仍必须把提交时后端计算出的模型价格写入 `AudioAssetBindingTarget.billing_points_cost`,最终发布落资产时按该价格扣费;创作音频目标未提供该字段时才使用旧的创作音频固定成本。 -8. 编辑器图片生成、图片修改、图标 spritesheet 和 UI 设计图提取素材的参考图可以提交 Data URL 或已登记的 generated objectKey;objectKey 必须归属于当前账号的 `editor_project_resource`、`editor_asset` 或 `asset_object`,后端通过归属校验后才签名读取 OSS。图标素材和 UI 素材提取的额外参考图必须真正传入 provider,不得只写入 `generationInputs` 展示快照;图片快速编辑当前不开放额外参考图,只提交原图或红框序号标注图作为 `sourceImageSrc`。UI 素材提取额外参考图上限为 5 张,普通图片生成上限 5 张,图标素材上限 8 张额外参考图。 +2. 用户钱包余额对外仍暴露为一个总余额,但后端扣费必须按“每日免费泥点 -> 会员周期限时泥点 -> 普通永久泥点”的顺序消耗,前端不得自行决定扣费桶。扣费流水 `metadata_json` 必须记录 `dailyFreePointsDelta`、`dailyFreeDayKey`、`membershipPeriodPointsDelta`、`permanentPointsDelta` 和会员限时泥点所属 `cycleResetsAtMicros`;资产退款中的会员限时泥点只在原周期仍有效时恢复会员额度,其余会员部分进入普通永久泥点。原每日免费消费部分在同一业务日退款时恢复原当日额度;跨北京时间业务日退款时叠加到退款当日每日免费桶,不进入普通永久泥点,当日 `granted_points` 与 `remaining_points` 均可因此超过 `20`。原永久泥点消费部分无论是否跨业务日,均按退款流水中的 `permanentPointsDelta` 退回普通永久泥点。 +3. 每日免费泥点是独立于每日任务和会员周期的正式余额额度,基础发放量固定为 `20`,不得由前端或后台任务配置改写。`profile_daily_free_points` 保存当前北京时间业务日、当日基础发放及跨日退款叠加后的总额度和剩余额度;北京时间每日 `00:00` 作为业务日边界,个人中心、充值中心、账单读取和钱包扣费入口在首次触达新业务日时原子清除昨日剩余及退款叠加量,并把今日 `granted_points`、`remaining_points` 重置为 `20`。首次初始化使用 `daily_free_grant` 流水,跨日重置使用 `daily_free_reset` 流水。惰性落库不能改变“北京时间 00:00 后读取即为新日额度”的对外语义。 +4. 每日任务奖励继续使用 `daily_task_reward` 流水并进入普通永久泥点,但主站隐藏每日任务卡片和任务中心入口,不再把每日登录任务描述为“每日免费泥点”。任务配置、进度、领取记录和后台管理能力暂时保留,除非后续需求明确删除。 +5. 编辑器画板所有会调用外部生成 provider 的入口都不从前端请求接收 `priceMudPoints`;同步请求以 SpacetimeDB `editor_generation_pricing_config` 当前全局配置计算,外部生成队列则以 `external_generation_job.price_mud_points` 保存的入队价格为准,worker 的扣费、退款、响应和资产成本不得按执行时配置重算。前端按钮泥点只作为展示。 +6. 编辑器图片生成 / 图片修改 / 图标 spritesheet / UI 设计图提取素材 / 视频 / 角色动作 / 音效 / 背景音乐必须在后端计算模型价格后使用 `execute_billable_asset_operation_with_cost` 预扣泥点;预扣失败必须 fail-closed,不得继续提交 VectorEngine、Ark、Suno 或 Vidu 上游任务。 +7. 队列任务按 `job_id + claim_attempt` 使用独立 consume/refund ledger。新 attempt 结算旧 attempt 时必须先写 `asset_operation_wallet_settlement`:旧 consume 已存在则原子退款,尚不存在则写取消 intent;迟到 consume 在同一 SpacetimeDB 事务内看到 intent 后必须失败关闭。重复 consume/refund 只有用户、金额、来源和配对 ledger 全部一致时才可视为幂等成功。lease 过期时只有 `attempt < max_attempts` 才能递增并重领;最终 attempt 已耗尽时,claim transaction 必须直接把 job 收口为 `failed`、清理 lease、写失败事件并结算当前 attempt,不能再把任务返回 worker 或调用 provider。 +8. 音频生成的编辑器链路虽然任务提交和结果发布分离,仍必须把提交时后端计算出的模型价格写入 `AudioAssetBindingTarget.billing_points_cost`,最终发布落资产时按该价格扣费;创作音频目标未提供该字段时才使用旧的创作音频固定成本。 +9. 编辑器进入外部生成持久队列的图片生成、图片修改、去背景、图标 spritesheet、UI 设计图提取、角色动作和视频参考图,只允许提交已登记的 generated `objectKey`、`resourceId` 或 `assetId`;任务 `request_payload_json` / `result_payload_json` 任意层级都禁止 `data:` / `blob:`,并受统一字节上限保护。objectKey 必须归属于当前账号的 `editor_project_resource`、`editor_asset` 或 `asset_object`,后端通过归属校验后才签名读取 OSS。本地红框序号标注图必须先上传并确认对象,再把 objectKey 入队;不得把既有 objectKey 下载成 Data URL 后写入任务。图标素材和 UI 素材提取的额外参考图必须真正传入 provider,不得只写入 `generationInputs` 展示快照;图片快速编辑当前不开放额外参考图。UI 素材提取额外参考图上限为 5 张,普通图片生成上限 5 张,图标素材上限 8 张额外参考图。同步且不持久化的历史兼容入口即使仍能解析 Data URL,也不能把该值转存到工程、素材、元数据、审计或任务表。 ## 外部服务与资产 @@ -266,7 +269,15 @@ npm run check:server-rs-ddd - Rust 结构体:`ExternalGenerationJob` - 源码:`server-rs/crates/spacetime-module/src/external_generation.rs` -- 用途:外部生成 worker 的持久任务队列和用户可见生成任务列表;`GENARRATIVE_EXTERNAL_GENERATION_MODE=queue` 时,`api-server` HTTP 角色只入队,`external-generation-worker` 角色通过 claim lease 领取、续租、执行,并用 `lease_token` 栅栏回写完成 / 失败。队列行同时保存 `price_mud_points`、`refund_ledger_id` 和 `notification_acknowledged_at`,BFF 通过 `GET /api/runtime/external-generation/jobs` 返回当前账号的正式生成任务列表、价格、状态和未确认终态数量;前端只能展示该后端事实,完成 / 失败提示展示后后台调用 `POST /api/runtime/external-generation/jobs/acknowledge` 由后端写确认时间,关闭按钮只收起本地弹窗,未确认终态任务会在下次登录后再次集中弹出。拼图 `compile_puzzle_draft` 的前置 `compile_puzzle_agent_draft`、`generate_puzzle_images` 与 `generate_puzzle_ui_background` 的业务写回也在对应 SpacetimeDB transaction 内校验 `job_id + worker_id + lease_token`、job kind、owner 和 source entity,避免过期 worker 写 session / work profile;图片画布编辑器的 `editor_image_generation`、`editor_image_edit`、`editor_background_removal`、`editor_icon_spritesheet_generation`、`editor_ui_design_asset_extraction`、`editor_character_animation_generation`、`editor_video_generation`、`editor_sound_effect_generation` 和 `editor_background_music_generation` 复用同一队列表,worker 成功后经 `api-server` facade 写入 `editor_project_resource` / `editor_asset` / `editor_canvas.layers_json`,前端只通过 BFF job 状态轮询和项目快照读取恢复完成态。`GENARRATIVE_EXTERNAL_GENERATION_MODE=inline` 时不创建该队列行,三个 external generation guard 字段必须同时为空才允许 api-server 受控同步写回,半空 guard 仍会拒绝。worker 成功写回业务事实后才能 complete job;业务失败态写回成功后才能 fail job,失败态未写回时保留租约等待后续重领。 +- 用途:外部生成 worker 的内部持久任务队列;`GENARRATIVE_EXTERNAL_GENERATION_MODE=queue` 时,`api-server` HTTP 角色只入队,`external-generation-worker` 角色通过 claim lease 领取、续租、执行,并用 `lease_token` 栅栏回写完成 / 失败。队列行继续保存 worker 执行、计费与滚动发布兼容所需字段,但用户可见任务列表、价格、状态、未确认终态数量和通知确认时间的正式读取事实源已经迁到 `external_generation_job_summary`;BFF 不得再为列表 / 详情 / acknowledge 读取该大表。拼图 `compile_puzzle_draft` 的前置 `compile_puzzle_agent_draft`、`generate_puzzle_images` 与 `generate_puzzle_ui_background` 的业务写回也在对应 SpacetimeDB transaction 内校验 `job_id + worker_id + lease_token`、job kind、owner 和 source entity,避免过期 worker 写 session / work profile;图片画布编辑器的 `editor_image_generation`、`editor_image_edit`、`editor_background_removal`、`editor_icon_spritesheet_generation`、`editor_ui_design_asset_extraction`、`editor_character_animation_generation`、`editor_video_generation`、`editor_sound_effect_generation` 和 `editor_background_music_generation` 复用同一队列表,worker 成功后经 `api-server` facade 写入 `editor_project_resource` / `editor_asset` / `editor_canvas.layers_json`,前端只通过 BFF job 状态轮询和项目快照读取恢复完成态。`GENARRATIVE_EXTERNAL_GENERATION_MODE=inline` 时不创建该队列行,三个 external generation guard 字段必须同时为空才允许 api-server 受控同步写回,半空 guard 仍会拒绝。worker 成功写回业务事实后才能 complete job;业务失败态写回成功后才能 fail job,失败态未写回时保留租约等待后续重领。 +- 载荷约束:本次先对 `source_module = editor-canvas` 的 `request_payload_json` / `result_payload_json` 实施有限大小合法 JSON、任意层级禁止 `data:` / `blob:` 的双层门禁,只保存 worker 执行必需的普通参数和已登记媒体引用;其它玩法在完成各自参考图资源化之前不由本次门禁静默改变既有请求契约。该主表只供 worker claim / 执行和受控维护读取;正式用户任务列表、单任务状态、队列概览与 acknowledge 不得再返回或解析这两个 payload。 + +### `external_generation_job_summary` + +- Rust 结构体:`ExternalGenerationJobSummary` +- 源码:`server-rs/crates/spacetime-module/src/external_generation.rs` +- 用途:外部生成正式任务列表的轻量投影,按 `job_id` 保存 owner、来源、状态、价格、有界错误摘要、通知确认时间、各阶段时间和入队时提取的 `request_prompt`,不包含 request/result payload、worker lease 或 dedupe 内部字段。错误摘要统一拒绝内联媒体并限制为 2048 字符;列表在单次 owner 扫描中同时计数并只保留请求 limit 的固定大小 top-N,不得先收集全量历史再截断。enqueue、claim、renew、complete、fail 事务同步投影;acknowledge 只更新该轻量表并写审计事件,后续主任务同步必须保留已有确认时间,禁止为了写确认时间加载 / 重写大 payload 行。BFF 的列表、状态和确认只调用 summary procedure。历史终态任务由迁移操作员的游标分批 maintenance procedure 在压缩 payload 时同步回填摘要,正式列表不得为兼容旧数据回扫完整主表。 +- 正式读取 procedure 为 `get_external_generation_job_summary_and_return`、`list_external_generation_job_summaries_and_return` 和 `acknowledge_external_generation_job_summaries_and_return`。历史维护 procedure 为 `compact_external_generation_job_payloads_and_return` 与 `backfill_external_generation_job_summaries_and_return`,仅 migration operator 可调用;运维入口统一使用 `npm run spacetime:external-generation:maintain -- ...`,默认 dry-run、单批最多 25 条。B-tree cursor 选择阶段最多反序列化 `limit + 1` 行,apply 再按主键逐条读取选中行;怀疑存在单行异常巨型 JSON 时必须先使用 `--limit 1`。payload 压缩额外固定使用 `source_module = editor-canvas` 的复合 cursor 索引,不得静默改写其它玩法历史任务。 ### `external_generation_job_event` @@ -683,6 +694,12 @@ npm run check:server-rs-ddd - Rust 结构体:`ProfileDashboardState` - 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +### `profile_daily_free_points` + +- Rust 结构体:`ProfileDailyFreePoints` +- 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` +- 作用:每日免费泥点事实源。`day_key` 使用北京时间业务日,基础发放量固定为 `20`,`remaining_points` 保存当日剩余额度;跨业务日退款的每日免费消费部分会叠加到退款当日,使 `granted_points` 和 `remaining_points` 可暂时超过 `20`,下一业务日首次触达时旧余额与叠加量一并失效并重置为 `20`。 + ### `profile_feedback_submission` - Rust 结构体:`ProfileFeedbackSubmission` @@ -709,7 +726,7 @@ npm run check:server-rs-ddd - Rust 结构体:`ProfileRechargeProductConfig` - 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` -- 作用:泥点和会员充值商品配置真相源,供充值中心展示、下单校验、支付确认和后台“充值商品”页维护。 +- 作用:泥点和会员充值商品配置真相源,供充值中心展示、下单校验、支付确认和后台“充值商品”页维护;当前公开充值中心只展示四档泥点商品,会员商品配置留存但不公开购买或升级入口。 - 字段补充:会员商品追加 `membership_period_points`、`membership_period_days`、`membership_queue_limit`、`membership_discount_bps`;泥点商品这些字段必须为 `0`。 ### `profile_played_world` @@ -774,7 +791,7 @@ npm run check:server-rs-ddd - Rust 结构体:`ProfileWalletLedger` - 源码:`server-rs/crates/spacetime-module/src/runtime/profile.rs` -- 说明:账号钱包流水表。`metadata_json` 为可选 JSON 对象字符串,旧行缺失时读取层按 `{}` 归一;外部生成扣费 / 退款写入 `externalGenerationJobId`,使退款记录可以追溯到对应 `external_generation_job`。 +- 说明:账号钱包流水表。`created_at` 表示钱包事务实际结算时间,列表先按当前余额反向校验 `balance_after - amount_delta` 的结算链,再以该时间倒序兜底,避免支付回调或退款重放延迟时出现余额顺序倒置;支付平台确认时间继续保存在充值订单 `paid_at`。`metadata_json` 为可选 JSON 对象字符串,旧行缺失时读取层按 `{}` 归一;外部生成扣费 / 退款写入 `externalGenerationJobId`,使退款记录可以追溯到对应 `external_generation_job`。 ### `asset_operation_wallet_settlement` diff --git a/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md b/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md index b9c713dfa..38c55fe08 100644 --- a/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md +++ b/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md @@ -67,13 +67,19 @@ lease 过期后不代表任务一定再次执行:claim transaction 只有在 ` `我的` 页签或排障面板展示队列等待时,只读取 BFF 队列接口:`GET /api/runtime/external-generation/queue-overview` 查看当前用户可见队列概览,`GET /api/runtime/external-generation/jobs/{jobId}` 查看单 job 状态。生成页 / 进度页不承接队列概览,只展示当前玩法业务进度;队列接口只提供等待 / 运行 / 失败 / 完成状态补充,最终草稿、作品和结果页仍要轮询对应玩法 session/detail 接口收敛到 ready 或 failed;不要直接查询 `external_generation_job` private table,也不要把 worker 内部 payload 暴露到前端。 +外部生成任务摘要投影与历史 payload 维护使用 `npm run spacetime:external-generation:maintain -- ...`,且只能由已授权 migration operator 的 SpacetimeDB CLI 登录态执行。脚本默认 dry-run、每次只处理一批,绝不自动循环全表;`--apply` 才写入。先发布包含 `external_generation_job_summary` 与 cursor 索引的 SpacetimeDB 模块,在维护模式内对事故时间以前的编辑器终态任务执行小批 dry-run,例如 `npm run spacetime:external-generation:maintain -- --database --server-url --limit 5 --completed-before-micros `;核对 `matched_count`、`before_bytes`、`after_bytes` 和 `inline_media_count` 后,保持本批输入 cursor 不变并追加 `--apply` 重跑同一批,即使最后一批 `has_more = false`,只要 dry-run 仍有 `matched_count` / `selected_count` 也必须 apply;只有 apply 成功后才使用它返回的 `next_cursor_job_id` 继续。B-tree cursor 的选择阶段最多反序列化 `limit + 1` 行,apply 会再按主键逐条读取选中行但不会同时保留整批 payload;如怀疑存在单行异常巨型历史 JSON,先用 `--limit 1`。payload 压缩硬限制 `source_module = editor-canvas`;终态压缩完成后,用 `--backfill-summaries` 先 dry-run、再 `--apply` 分批补齐仍缺失的活动任务或无内联媒体历史任务摘要,直到 `has_more = false`,最后再切换使用 summary procedure 的 api-server。Stdb 构建 artifact 和完整 release 包都必须包含 `scripts/spacetime-maintain-external-generation-jobs.mjs` 与 `scripts/spacetime-migration-common.mjs`。首次上线不得让 Full Build 从 Stdb 自动直落 API:`STDB_API_ROLLOUT_MODE` 默认 fail-closed 为 `pause-after-stdb`,必须填写受限的 `STDB_API_ROLLOUT_APPROVERS`;Stdb Publish 通过 `KEEP_MAINTENANCE_MODE` 保持维护文件并停止旧 API/controller/worker,暂停点最多等待 4 小时,完成上述维护并确认无后续批次后才由指定审批人放行 API。定时构建缺少审批人时必须在发布前失败,不能静默退回 `normal`;也可分开运行 Stdb publish、维护、API deploy 三个受控 Job。任一批次都不得处理 pending / running payload;不要用 runtime writer、bootstrap secret 或匿名 identity 代替 migration operator,也不要在未核对 dry-run 时直接 apply。 + +自 2026-07-11 起,`Genarrative-Full-Build-And-Deploy` 的每日 04:00 timer 默认以 `DEPLOY_TARGET=development`、`STDB_API_ROLLOUT_MODE=normal` 对仅供开发使用的 dev 服务器执行 Stdb → API → Web 完整发布,不进入人工 rollout gate。三个下游 Build 都由 Full Job 显式传 `PUBLISH_AFTER_BUILD=false`,不得依赖下游 Job 默认值或提前各自发布;统一 Build 完成后仍由 Full Job 按固定顺序发布。人工维护窗口才选择 `pause-after-stdb`,且必须配置 `STDB_API_ROLLOUT_APPROVERS`。上文“定时构建缺少审批人时失败”的旧口径不再作为当前 dev 定时发布行为。 + +Full Job 通过 `EXIT_MAINTENANCE_MODE_AFTER_COMPLETION` 明确选择完整发布成功后是否退出维护,默认勾选以保持历史行为。Full 对 Stdb Publish 和 API Deploy 两个下游阶段都固定传 `KEEP_MAINTENANCE_MODE=true`,让 maintenance marker 持续覆盖 Stdb → API → Web 整段发布;Web Deploy 成功后才进入独立 `Exit Maintenance` 阶段。取消勾选时跳过最终退出阶段,便于内网验收完成后人工恢复公网。`Genarrative-Api-Deploy` 也单独暴露 `KEEP_MAINTENANCE_MODE` 参数,并转换为随发布包脚本的 `--keep-maintenance-mode`;失败路径仍按既有 current 切换边界保留或退出维护,不受成功态选项覆盖。 + 需要验证“更新 API 不停 worker”和“worker 是否持续消费队列”时,优先使用隔离容器 smoke:`npm run container:worker-smoke -- smoke`。该脚本生成 gitignored 的 `deploy/container/worker-smoke/api-server.env`,启动独立 compose project 与独立 SpacetimeDB,发布当前 `spacetime-module` 后写入 `worker_smoke_unsupported` 测试 job;预期 worker claim 后执行 unsupported 失败分支,再执行 API-only recreate 并确认 worker 容器 ID 不变,最后再次入队验证 API 更新后队列仍可消费。`external_generation_job` 是 private table,脚本通过 worker 日志确认 job_id 被消费,不用 CLI SQL 查询私表。该 smoke 不读取 `.env.local`,也不依赖真实 VectorEngine / OSS 密钥;真实生图链路联调再在本地私有 env 中补齐 provider 配置。worker-smoke 默认把本机 `spacetime` CLI 打成轻量 SpacetimeDB 镜像,避免本机首次 smoke 依赖官方大镜像下载。若容器内 Cargo 拉取 crates.io 依赖不稳定,可用 `npm run container:worker-smoke -- smoke --local-binary` 让容器内 Cargo 复用本机 Cargo 缓存构建当前二进制,再打入 Debian bookworm smoke runtime 临时镜像;可用 `GENARRATIVE_WORKER_SMOKE_LOCAL_BASE_IMAGE` 覆盖运行时基础镜像;若隔离端口或库数据需要重建,追加 `--force`。完成 queue 链路验证时,还要用队列概览 BFF 和单 job 状态接口确认 job 从 queued/running 收敛,并用对应玩法 session/detail 接口确认业务状态同步完成。 本地只做账号/UI smoke 且需要短信登录时,`SMS_AUTH_PROVIDER` 应显式设为 `mock`,并把 `SMS_AUTH_MOCK_VERIFY_CODE` 设为固定值(当前常用 `123456`),再重启 `npm run dev` 或 `npm run dev:api-server`。如果 `.env.local` 还保留 `SMS_AUTH_PROVIDER=aliyun`,`POST /api/auth/phone/login` 用 mock 验证码会稳定报“验证码错误”,不是前端表单问题。真实短信联调再切回 `aliyun` 并重启。 微信小程序虚拟支付使用 `WECHAT_MINI_PROGRAM_VIRTUAL_PAYMENT_OFFER_ID`、`WECHAT_MINI_PROGRAM_VIRTUAL_PAYMENT_APP_KEY`、`WECHAT_MINI_PROGRAM_VIRTUAL_PAYMENT_SANDBOX_APP_KEY` 和 `WECHAT_MINI_PROGRAM_VIRTUAL_PAYMENT_ENV` 配置。小程序充值统一走 `wechat_mp_virtual` / `wx.requestVirtualPayment`:泥点属于代币(`coin`),`buyQuantity` 按当前充值商品快照里的 `points_amount` 传;会员和后台新增道具类商品走 `short_series_goods`,`productId` 对应微信后台道具 ID。旧登录快照若缺 `session_key`,需要用户在小程序内重新登录后再支付;客户端成功回调不是最终到账,仍以后端通知或查询确认订单为准。详细口径见 `docs/【技术方案】微信虚拟支付接入-2026-05-26.md`。 -普通微信充值订单本地有效期为 5 分钟。SpacetimeDB 原生 `profile_recharge_order_expiration_timer` 到点后只把仍为 `pending` 的订单改为 `expired`;HTTP `api-server` 通过订阅 `profile_recharge_order` 的 `Pending -> Expired` 更新执行微信查单补偿。`external-generation-worker` 和 `external-generation-controller` 不运行充值过期逻辑,也不应因为扩容外部生成 worker 放大微信查单或关单流量。查账时本地未支付终态保持 `expired`,不再改写为 `closed`;`expiration_checked_at`、`expiration_provider_state`、`expiration_last_error` 用于判断 HTTP 监听器是否已经完成补偿。 +普通微信充值订单本地有效期为 5 分钟。SpacetimeDB 原生 `profile_recharge_order_expiration_timer` 到点后只把仍为 `pending` 的订单改为 `expired`;HTTP `api-server` 只订阅活跃 timer 表的删除事件,按事件中的 `order_id` 重新读取订单并仅对 `expired` 执行微信查单补偿,不订阅完整充值订单历史表。支付或主动关闭也会删除 timer,但读取到非 `expired` 后直接忽略;监听断线窗口由未检查过期订单 catch-up 补齐。`external-generation-worker` 和 `external-generation-controller` 不运行充值过期逻辑,也不应因为扩容外部生成 worker 放大微信查单或关单流量。查账时本地未支付终态保持 `expired`,不再改写为 `closed`;`expiration_checked_at`、`expiration_provider_state`、`expiration_last_error` 用于判断 HTTP 监听器是否已经完成补偿。 微信小程序订阅消息生成结果通知使用 `WECHAT_MINIPROGRAM_SUBSCRIBE_MESSAGE_ENABLED`、`WECHAT_MINIPROGRAM_GENERATION_RESULT_TEMPLATE_ID` 和 `WECHAT_MINIPROGRAM_SUBSCRIBE_MESSAGE_STATE` 配置。当前模板为 `AI创作生成结果通知`;H5 在生成动作发起前先进入生成进度态并立即继续生成动作,同时非阻塞跳转到小程序原生订阅授权页尝试请求授权,用户接受、拒绝或返回都不能阻塞生成,且原生页不改写上一页 `webViewUrl`,避免返回后丢失 H5 当前进度页状态。后端只在玩法草稿生成成功或失败终态后用微信登录保存的 openid 调用 `subscribeMessage.send`,发送失败只打 warning,不影响生成主链路。模板 `thing1` 字段发送玩法模板名,例如 `拼图`、`敲木鱼`、`抓大鹅`;`number6` 字段发送本次生成结算后的实际泥点扣除,失败退款后固定为 `0`。模板 `time4` 字段固定发送北京时间 `YYYY-MM-DD HH:mm`,不要使用内部微秒时间戳、秒级时间戳或带时区后缀的 RFC3339 字符串,否则微信会返回 `argument invalid! data.time4.value invalid`。当前已接入拼图、敲木鱼、抓大鹅、跳一跳、方洞、视觉小说的草稿生成终态;分槽素材生成或发布动作不得直接复用生成结果通知,避免一次作品生成产生多条订阅消息。 @@ -89,7 +95,7 @@ spacetime sql "SELECT * FROM puzzle_gallery_card_view LIMIT 1" --serv 本地 `npm run dev:spacetime` 发布模块时必须显式忽略仓库根目录的 `spacetime.json`,由脚本固定追加 `--no-config` 并使用命令参数里传入的数据库名和 `--server http://127.0.0.1:3101`。否则 CLI 可能把发布目标改写到配置文件里的其他数据库,导致 `dev:spacetime` 启动后又因发布失败自动退出,浏览器随后会在 `ws://127.0.0.1:3101/v1/database/.../subscribe` 看到连接拒绝。 -本地 `spacetime` CLI / standalone 版本必须和 `server-rs/Cargo.toml` 里锁定的 `spacetimedb` 版本一致;当前统一版本为 `2.5.0`。若版本错配,procedure 返回值可能在宿主侧触发 `Failed to BSATN deserialize procedure return value`,api-server 最终表现为敲木鱼等创作动作的 `SpacetimeDB procedure 调用超时`。排障时先运行 `spacetime --version`,再对照 `server-rs/Cargo.toml` 的 `spacetimedb = "..."`;遇到版本不匹配时不要继续深挖业务超时,直接执行 `spacetime version install && spacetime version use `,或在目标就是最新版本时执行 `spacetime version upgrade`,升级后重启 `npm run dev:spacetime` 再重试。当前 `scripts/dev.mjs` 会在启动和复用本地 SpacetimeDB 前写入并校验 `dev-spacetime-tool-version`,避免把旧 standalone 继续带进新一轮创作。 +本地 `spacetime` CLI / standalone 版本必须和 `server-rs/Cargo.toml` 里锁定的 `spacetimedb` 版本一致;当前统一版本为 `2.6.0`。若版本错配,procedure 返回值可能在宿主侧触发 `Failed to BSATN deserialize procedure return value`,api-server 最终表现为敲木鱼等创作动作的 `SpacetimeDB procedure 调用超时`。排障时先运行 `spacetime --version`,再对照 `server-rs/Cargo.toml` 的 `spacetimedb = "..."`;遇到版本不匹配时不要继续深挖业务超时,直接执行 `spacetime version install && spacetime version use `,或在目标就是最新版本时执行 `spacetime version upgrade`,升级后重启 `npm run dev:spacetime` 再重试。当前 `scripts/dev.mjs` 会在启动和复用本地 SpacetimeDB 前写入并校验 `dev-spacetime-tool-version`,避免把旧 standalone 继续带进新一轮创作。 本地 `.env`、`.env.local` 或 `.env.secrets.local` 修改后必须重启 `api-server` 才会生效;若已经通过 `npm run dev` 启动完整联调,可在该终端输入 `rs api-server`。排查 RPG / 拼图 / 抓大鹅等 VectorEngine 生图链路时,确认 `VECTOR_ENGINE_BASE_URL`、`VECTOR_ENGINE_API_KEY` 和 `VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS` 只在本地或服务器密钥文件中配置,不能写入 Git。VectorEngine `gpt-image-2` 图片协议、URL / base64 响应解析、远端图片下载和 provider 侧结构化日志在 `server-rs/crates/platform-image`;`api-server` 只做配置、玩法编排、OSS / asset 持久化、计费和失败审计落库。开局 CG 故事板、首图、背景和图集都属于长耗时图片请求;后端默认会把 `VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS` 下限收口到 `1000000`,旧进程仍可能沿用重启前的短超时。若 VectorEngine 在 `send()` 阶段失败且日志显示 `SendRequest`,先看同一 `request_id` 的 provider 日志字段 `source`、`source_chain`、`source_chain_depth`,再查 `external_api_call_failure.metadata_json.errorSource`;当前 multipart `/v1/images/edits` 单独强制 HTTP/1.1。拼图关卡资产按 `level_scene -> ui_spritesheet -> level_background` 顺序生成,日志会带 `slot`、`asset_kind` 和 `elapsed_ms`。 @@ -389,12 +395,24 @@ Pingora current release 自审脚本 `scripts/ops/pingora-current-release-audit. `Genarrative-Stdb-Module-Build` 的 Jenkins 归档产物必须包含 `build//spacetime_module.wasm`、`spacetime_module.wasm.sha256`、`release-manifest.json`、`scripts/deploy/production-stdb-publish.sh`、`scripts/deploy/production-runtime-writer-identity-rotate.mjs`、`scripts/deploy/maintenance-on.sh`、`scripts/deploy/maintenance-off.sh`、`scripts/spacetime-migration-common.mjs` 和 `scripts/database-backup-to-oss.mjs`,不得包含 `migration-bootstrap-secret.txt` 或任何原始 bootstrap secret。`Genarrative-Stdb-Module-Build` 只接受 `MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID` 指向的受保护 Jenkins Secret File:构建 shell 从临时文件读取原始值,强制校验为 64 位十六进制,计算 SHA-256,随后只通过 `GENARRATIVE_SPACETIME_MIGRATION_BOOTSTRAP_SECRET_SHA256` 注入 Rust 编译;WASM 因而只包含摘要,不包含可下载的原文,Stdb `release-manifest.json` 以 `migration_bootstrap_secret_sha256` 记录该非敏感摘要。`Genarrative-Stdb-Module-Publish` 只通过 `copyArtifacts` 复制上述非敏感产物,不在目标机器 checkout Git,并在发布阶段用同一个凭据 ID 再次挂载 Secret File;publish 必须再次校验 64 位十六进制、重算 SHA-256,并与 manifest 的 `migration_bootstrap_secret_sha256` 强制匹配后才可发布。Full Build 必须保证 Stdb Build / Publish 的 `MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID` 完全相同并把同一个 ID 同时透传,不能从构建 artifact 传 secret;ID 不同、manifest 缺摘要或摘要不匹配都必须在发布前失败。 +三个 SCM Jenkinsfile 将 `MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID` 默认固定为 `genarrative-spacetime-bootstrap-secret-dev-file`。Secret File 的原文只存在于 Jenkins Credentials;credential ID、参数默认值和定时 / 发布行为以仓库 Jenkinsfile 为事实源,不能只改 Job UI,因为 Declarative Pipeline 下一次载入会重写参数定义。旧 Secret Text `genarrative-spacetime-bootstrap-secret-dev` 继续保留给 Database Import / Export,不得原地改类型或删除。 + +生产 Stdb publish 固定传 `--delete-data=never --yes=migrate,break-clients`,普通 Stdb Jenkins Job 不提供 `CLEAR_DATABASE`;任何需要删除数据的迁移都必须失败并重新核对 schema 与 artifact,不能在发布路径内切换清库继续。 + 生产运行时不把 bootstrap secret 明文写进 `/etc/genarrative/*.env`。`api-server.env` 和 worker env 只登记固定 FILE 路径 `GENARRATIVE_SPACETIME_RUNTIME_SERVICE_BOOTSTRAP_SECRET_FILE=/var/lib/genarrative/spacetime/runtime-service-bootstrap-secret.txt`;若检测到明文 `GENARRATIVE_SPACETIME_RUNTIME_SERVICE_BOOTSTRAP_SECRET` 或其他 FILE 路径,Server-Provision / API deploy 必须失败。Full Build 先执行 Stdb publish、后执行 API deploy,因此两段必须透传同一 `API_ENV_FILE` / `WORKER_ENV_FILE`;Stdb Publish 把 Secret File 路径作为 `--migration-bootstrap-secret-file` 传给随包 `production-stdb-publish.sh`。脚本先进入维护模式、按所选模式完成发布前冷备份、校验 checksum 并发布 module;成功后拒绝符号链接目标,把 secret 安装成 `root:genarrative 0440`、目录收紧为 `root:genarrative 0750`,原子补齐 API / worker env 的固定 FILE 配置,再快照并重启发布前为 active 的 API、controller 和 worker。`systemctl is-active` 只有明确返回合法的非 active 状态时才允许跳过,查询错误或 active worker 的 `list-units` 失败都必须保留维护模式并阻断;所有原 active 服务重启后必须重新确认为 `active`。如果 API 原本 active,还必须在 `maintenance-off` 前通过本机 `http://127.0.0.1:8082/healthz` readiness;可用 `--api-health-url` / `GENARRATIVE_STDB_PUBLISH_API_HEALTH_URL` 调整本机 URL,并用 `--api-readiness-timeout-seconds` / `GENARRATIVE_STDB_PUBLISH_API_READINESS_TIMEOUT_SECONDS` 调整超时。这样旧服务器首次 rollout 也不会等到后续 API deploy 才拿到 FILE;只覆盖 secret 文件或只补 env 而不重启都不生效,因为 `AppConfig` 在进程启动时读取 secret。人工执行 `npm run build:production-release -- --component spacetime-module --name ` 且未显式提供 secret / SHA-256 时,原始随机 secret 只写入 gitignored 的 `server-rs/.spacetimedb/build-secrets/.txt`,目录权限 `0700`、文件权限 `0600`,发布包和 `release-manifest.json` 都不收录它;必须把该受保护文件另行交给 publish 阶段。旧 `npm run deploy:rust:remote` Ubuntu 直传入口也使用同一 sidecar 目录,发布包内不含原文;上传模式通过独立 SSH 标准输入把 secret 原子安装到远端发布目录并收紧为 `0600`,`--skip-upload` 时必须单独受保护交付。 本地 dev 的原始值也只能进入 api-server,不得扩散给 Web / Vite,任何控制台、Jenkins 日志、归档或生成 README 都不得输出明文。相关变更至少运行 `bash -n scripts/deploy/production-stdb-publish.sh scripts/deploy/production-api-deploy.sh scripts/deploy-rust-remote.sh scripts/jenkins-server-provision.sh`、`node --check scripts/dev.mjs scripts/check-production-ops-guardrails.mjs`、`npm run check:production-ops`、`npm run check:encoding` 和 `git diff --check`。 -生产 runtime writer 不能通过替换 bootstrap secret 或重启服务隐式轮换。migration operator 与 runtime writer 必须身份互斥:operator 不能成为 writer,当前 writer 不能授权为 operator;已有任一 operator 后,bootstrap secret 不得新增或接管 operator。先准备新 api-server identity,并使用当前已授权 migration operator 的 CLI 登录态执行 `node scripts/deploy/production-runtime-writer-identity-rotate.mjs --database --server-url --operator-identity --operator-user-id --next-writer-identity --confirm-next-writer-identity --note `。CLI 会校验当前登录 identity、双录新 identity 和审计原因;模块 procedure 还会拒绝把 writer 设为任一已登记 migration operator。成功后必须核对 `editor_generation_runtime_identity_rotation` 的旧 writer、新 writer、operator identity、操作人、原因和服务端时间,再切换 API token;轮换只改 writer,不改模型价格。 +生产 runtime writer 不能通过替换 bootstrap secret 或重启服务隐式轮换。migration operator 与 runtime writer 必须身份互斥:operator 不能成为 writer,当前 writer 不能授权为 operator;已有任一 operator 后,bootstrap secret 不得新增或接管 operator。先准备新 api-server identity,并使用当前已授权 migration operator 的 CLI 登录态执行 `node scripts/deploy/production-runtime-writer-identity-rotate.mjs --database --server-url --operator-identity --operator-user-id --next-writer-identity --confirm-next-writer-identity --note `。CLI 会校验当前登录 identity、双录新 identity 和审计原因;模块 procedure 还会拒绝把 writer 设为任一已登记 migration operator。成功后必须核对 `editor_generation_runtime_identity_rotation` 的旧 writer、新 writer、operator identity、操作人、原因和服务端时间,再切换 API token;轮换只改 writer,不改模型价格。`pause-after-stdb` 人工维护不得把 `api-server.env` 的 `GENARRATIVE_SPACETIME_TOKEN` identity 授权为 migration operator;首次初始化前数据库还没有 writer 记录,模块无法提前识别这枚 identity 的未来用途,误授权会让 API 启动持续报“数据库迁移操作员 identity 不能初始化为模型生成运行时服务 identity”。若已误授权,必须在维护完成后由该 identity 自撤 migration operator 权限,再确认 rollout gate。API deploy 的本机 readiness 探测每次请求固定 `--max-time 2`,即使端口已建立但 API 尚未响应也会回到有限重试,不得使用无超时 curl。 `Genarrative-Web-Build` 打包 `web.tar.gz` 前、`Genarrative-Web-Deploy` 解包后都会把 Web 静态目录规范为目录 `755`、文件 `644`。如果前端页面能打开但 public 图片、字体或音频返回 `403 Forbidden`,优先检查当前 `/srv/genarrative/web` 指向的 release 中对应文件权限是否被异常归档为 `600`,临时恢复可对该 release 的 `web` 目录执行目录 `755`、文件 `644` 的权限修正。 +## 维护模式只拦截公网流量 + +Nginx 与 Pingora 在维护 marker 存在时对内网来源绕过整站维护闸,主站页面与静态资源、普通 API、后台页面与 `/admin/api/**`、SpacetimeDB 路由均按非维护状态继续处理;公网应用主站、普通 API、后台和 SpacetimeDB 路由继续返回维护响应。内网范围为 IPv4 loopback / RFC1918 / link-local 和 IPv6 loopback / ULA / link-local。Nginx 只按 TCP `$remote_addr` 判定;Pingora 只按 TCP peer 判定,peer 为 loopback 的同机 Nginx 时才读取 Nginx 强制覆盖的 `X-Real-IP`,绝不能把客户端可伪造的 `X-Forwarded-For` 用作维护放行依据。应用本身的登录、管理员鉴权和其它业务鉴权不变。 + +该规则只绕过网关维护响应,不会自动拉起 api-server、SpacetimeDB 或其它已停止的服务。人工执行 `maintenance-on.sh` 且后端仍运行时,可以从内网继续访问整站和修改后台数据;`pause-after-stdb` 会停止旧 API/controller/worker,在 API 被停期间静态页面可能仍可加载,但普通 API 与 `/admin/api/**` 仍不可用。验证使用 `npm run check:nginx-spa-routes`、`npm run check:pingora-route-parity`、`cargo test -p pingora-gateway --manifest-path server-rs/Cargo.toml`、`npm run check:pingora-gateway-smoke` 和 `npm run check:production-ops`,不要在 live 机器上为测试临时创建维护 marker。 + +维护页源码固定为 `public/maintenance.html`,正常 Web 构建由 Vite 复制到发布包根目录的 `web/maintenance.html`。计划内停服需要临时更新公告时,先更新该源码,避免后续 Web Deploy 把现场公告覆盖回旧内容;现场紧急替换必须原子写入当前 `/srv/genarrative/web/maintenance.html`,并同时用 `genarrative.world` 与 `www.genarrative.world` 的真实 HTTPS 响应校验 `503` 和公告正文。 + 生产 Jenkins 的 `Pipeline script from SCM` 由 Jenkins controller 读取 Jenkinsfile。`Genarrative-Server-Provision` 是服务器初始化流水线,Job 配置里的 SCM URL 必须使用 controller 本机可访问的仓库路径或内网 Gitea 地址,不能使用 `https://git.genarrative.world/...`;否则日志一开始的 `Checking out git ... to read jenkins/Jenkinsfile.production-server-provision` 就会先从公网拉 Jenkinsfile。构建类流水线和 `Genarrative-Server-Provision` 的 Jenkinsfile 内部源码准备阶段统一使用 `ssh://git@192.168.35.82:2222/GenarrativeAI/Genarrative.git`,并显式传入 Jenkins SSH 凭据 `genarrative-local-gitea-ssh`;不再配置 `https://git.genarrative.world/...` 公网 fallback,也不再默认使用 `http://genarrative-station/git/GenarrativeAI/Genarrative.git`。所有 `GitSCM checkout` 都必须保留单分支 refspec、`shallow=true`、`depth=1`、`noTags=true` 与 `honorRefspec=true`。API / Web / Stdb 发布类流水线不在目标机器 checkout Git,统一执行上游构建归档里的部署脚本,避免产物 commit 与部署脚本 commit 漂移;Server-Provision 也不在目标 dev / release agent checkout Git,而是由 Jenkins 构建节点先准备 provision 脚本与配置并上传给目标 agent。 当前 Jenkins / 本机内网 Git 入口固定为 `ssh://git@192.168.35.82:2222/GenarrativeAI/Genarrative.git`,用于 controller、构建节点和本机 Agent 直接拉取仓库,避免绕公网 `git.genarrative.world`。验证时在具备对应 SSH key 和 known_hosts 的环境执行 `git ls-remote ssh://git@192.168.35.82:2222/GenarrativeAI/Genarrative.git HEAD`,应能返回 HEAD。若机器仍保留旧的 `http://genarrative-station/git/GenarrativeAI/Genarrative.git` 或 `http://10.2.0.10/GenarrativeAI/Genarrative.git` 内网入口,只作为历史兼容和排障参考,新流水线不再默认使用。 @@ -424,7 +442,7 @@ worker 被硬杀或断电后,lease 过期任务只有尚未耗尽 `max_attempt - `api-server` 正常运行时 `/healthz` 只返回进程存活状态,`/readyz` 会同时检查进程是否仍接收新流量和 SpacetimeDB 连接租约是否健康;收到 `SIGINT` / `SIGTERM` 后会先把 readiness 标记为不可用,再让 Axum 停止接新连接并等待已有 HTTP 请求排空。systemd 仍以 `KillSignal=SIGINT` 停服务,`TimeoutStopSec=90` 作为长请求排空上限。 - SpacetimeDB 健康检查默认使用 `GENARRATIVE_SPACETIME_HEALTH_CHECK_TIMEOUT_SECONDS=2` 的短等待窗口,和业务 procedure 的 `GENARRATIVE_SPACETIME_PROCEDURE_TIMEOUT_SECONDS` 分开。`/readyz` 失败时 `details.spacetime.stage` 会标出当前卡住阶段:`pool_acquire`、`connect_build`、`connect_handshake`、`read_model_subscribe`、`procedure_result`、`reducer_result` 或 `read_cache`;`elapsedMs` / `timeoutMs` 用于确认是否命中健康检查窗口。业务请求日志也会写入 `operation_kind`、`operation_name`、`spacetime_stage` 和 `elapsed_ms`,后续 45 秒超时不再只靠 Nginx `request_time=45s` 推断。 - `genarrative-api.service` 设置 `LimitNOFILE=65535`、`TasksMax=2048`;上线后用 `systemctl show genarrative-api.service -p LimitNOFILE -p TasksMax -p TimeoutStopUSec` 和 `cat /proc/$(pidof api-server)/limits` 核对。 -- Server provision 不再通过 Windows helper 下载,也不再通过 Linux build 节点中转 SpacetimeDB / otelcol 工具包;Linux build 节点只负责从内网 Git 源准备 provision 脚本和配置并上传给目标 agent。`Prepare Provision Tools` 在目标 dev / release agent 工作区内先检查 `/usr/local/bin/otelcol-contrib` 与 `${SPACETIME_ROOT}/bin/current`:版本已满足时直接复用目标机现有文件生成 `provision-tools/`,只有缺失或版本不匹配时才使用 `PROVISION_DOWNLOADS_DIR` 里的本地包或从配置的下载源准备 SpacetimeDB `2.5.0` / `otelcol-contrib 0.151.0`;如果目标服务器下载需要代理,在 `PROVISION_DOWNLOAD_PROXY` 配置目标机可访问的 HTTP 代理。 +- Server provision 不再通过 Windows helper 下载,也不再通过 Linux build 节点中转 SpacetimeDB / otelcol 工具包;Linux build 节点只负责从内网 Git 源准备 provision 脚本和配置并上传给目标 agent。`Prepare Provision Tools` 在目标 dev / release agent 工作区内先检查 `/usr/local/bin/otelcol-contrib` 与 `${SPACETIME_ROOT}/bin/current`:版本已满足时直接复用目标机现有文件生成 `provision-tools/`,只有缺失或版本不匹配时才使用 `PROVISION_DOWNLOADS_DIR` 里的本地包或从配置的下载源准备 SpacetimeDB `2.6.0` / `otelcol-contrib 0.151.0`;如果目标服务器下载需要代理,在 `PROVISION_DOWNLOAD_PROXY` 配置目标机可访问的 HTTP 代理。 - 除 `Genarrative-Server-Provision` 外,`Genarrative-Stdb-Module-Build`、`Genarrative-Web-Build`、`Genarrative-Api-Build`、`Genarrative-*Deploy`、`Genarrative-Database-Import/Export`、`Genarrative-Full-Build-And-Deploy` 和 `Genarrative-Notify-Email` 的生产流水线现都以 Linux agent 为主,仍按各自 Jenkinsfile 的 checkout 口径执行。Server provision 不使用公网备用 Git 源,目标部署 agent 也不再需要访问源码 Git remote。 - `otelcol-contrib.service` 作为可选系统服务加入 provision,默认监听 `127.0.0.1:4317/4318` 并使用 `deploy/otelcol/genarrative-debug.yaml`。api-server 是否发送 OTLP 仍由 `GENARRATIVE_OTEL_ENABLED` 控制,服务 unit 见 `deploy/systemd/otelcol-contrib.service`。该服务必须存在系统用户 / 组 `otelcol`,并且 `/etc/otelcol/genarrative-debug.yaml` 已安装到目标机;若看到 `status=217/USER` 或 `Failed to determine user credentials`,优先检查 `getent passwd otelcol`,再补齐 `/etc/otelcol` 配置目录并重启服务。 - Nginx `/api/` 与 `/admin/api/` 通过 `genarrative_api` upstream 代理到 `127.0.0.1:8082`,upstream keepalive 为 64;`limit_conn` 负责连接 / 并发保护,`limit_req` 负责入口 RPS 快拒绝。当前模板把公开 gallery list 单独放到 `genarrative_gallery_rps`,默认 `rate=5000r/s`、`burst=4096`、`limit_conn=320`;公开详情和普通 API 放到 `genarrative_api_rps`,后台 API 放到 `genarrative_admin_rps`。通用 `/api` location 设置 `client_max_body_size 64m` 是反代兜底,防止拼图入口页 / 新增关卡本地参考图 Data URL 或旧兼容请求在到达 `api-server` 前被默认 1 MiB 上限拦截;拼图本地参考图前后端统一限制 6MB,历史图片仍提交 `referenceImageAssetObjectId(s)`。若线上出现 `413 Request Entity Too Large` 且 access log 中 `request_time=0.000`、`upstream_status=-`,说明请求在 Nginx 层被拦截,先用 `nginx -T | grep client_max_body_size` 检查 release 模板是否已渲染并 reload,同时检查前端是否超出 6MB 或错误提交了未压缩大图。`limit_conn_status 429` 和 `limit_req_status 429` 必须在 HTTP 与 HTTPS server 中同时生效;若线上压测看到 `limiting connections by zone "genarrative_api_conn"` 却返回 503,优先检查 `nginx -T` 里 HTTPS server 是否缺少这些状态码,以及 `/api/runtime/puzzle/gallery` 是否误落到通用 `location ~ ^/api` 的 `limit_conn=64`。压测时看 `/var/log/nginx/genarrative.access.log` 中的 `request_time`、`upstream_connect_time`、`upstream_header_time`、`upstream_response_time`、`upstream_status`、`request_id`。 diff --git a/docs/【玩法创作】创作主页与项目入口改版计划-2026-06-18.md b/docs/【玩法创作】创作主页与项目入口改版计划-2026-06-18.md index 5d0efaeac..b909b56d4 100644 --- a/docs/【玩法创作】创作主页与项目入口改版计划-2026-06-18.md +++ b/docs/【玩法创作】创作主页与项目入口改版计划-2026-06-18.md @@ -22,10 +22,12 @@ ### 顶部品牌区 -- 标题:`陶泥儿 - 开启全民精品游戏创作` +- 小眉题:`陶泥儿 Genarrative|游戏美术 AI 创作工具`,保持普通小字,不使用 heading 标签。 +- 唯一 H1:`陶泥儿 · 开启全民精品游戏创作`。 +- 产品说明:`面向个人创作者的游戏美术 AI 工作台。用美术 Agent 与无限画布,快速制作角色、场景、UI 与宣发素材。`,作为 H1 下方真实可见的普通正文分两行展示。 - 主按钮:`开始创作` - 社群入口:原地弹出“玩家社区”二维码弹窗,复用现有社区弹层能力,不跳转或切换到“我的 / 用户中心”。 -- 营销点:`登录即送100泥点,可以免费制作50个素材` +- 营销点:`登录即送 100 泥点,可以免费制作 50 个素材`,作为产品说明下方的小字权益提示。 `开始创作` 与“新建项目”使用同一项目创建链路。用户已登录时调用现有 `createEditorProject`,成功后进入 `/editor/canvas?projectid=xxx&guide=toolbar`,画布只消费一次 `guide=toolbar` 并清理 query,用于显示新画布工具栏引导;未登录或登录过期时打开登录弹窗,并在登录后重试创建。 @@ -33,7 +35,7 @@ ### 九大创作工具能力 -创作主页展示九项能力,用于说明陶泥儿创作工具覆盖范围: +分区标题使用 H2 `游戏美术 AI 创作工具`。创作主页展示九项能力,用于说明陶泥儿创作工具覆盖范围,卡片标题继续使用 H3: 1. 游戏视觉规范:轻松约束多类素材视觉一致性。 2. 游戏角色:整套高完成度 2D / 3D 角色素材及动画。 diff --git a/docs/【玩法创作】平台入口与玩法链路-2026-05-15.md b/docs/【玩法创作】平台入口与玩法链路-2026-05-15.md index 9badb0239..3e19ec162 100644 --- a/docs/【玩法创作】平台入口与玩法链路-2026-05-15.md +++ b/docs/【玩法创作】平台入口与玩法链路-2026-05-15.md @@ -12,7 +12,7 @@ 旧库或旧迁移包没有 `event_banners_json` 时,后端读取层必须把 `eventBanners` 归一到 `module-runtime` 默认公告数组,不能把旧结构化 `eventBanner` 当成前端优先数组下发。默认公告引用的背景图必须指向 `public/` 下真实存在的站内静态资源,当前默认使用 `/creation-type-references/puzzle.webp`,避免创作入口顶部 banner 出现失效图片。 -创作页和草稿页顶栏右上角的泥点余额胶囊是补足泥点入口:如果当前运行环境开启充值入口,点击后直接打开账户充值弹窗;否则直接打开运营兑换码弹窗。该入口不再跳到账户面板或泥点账单,头像 / 设置等账号入口继续保留各自语义。 +创作页和草稿页顶栏右上角统一复用公共泥点资产入口,不再把余额区本身作为直接充值按钮。余额区展开后只展示不限时泥点、每日免费泥点及重置口径;会员周期限时泥点仅由后端保留用于存量兼容和结算,当前版本不在前台展示。独立“充值”按钮进入“购买更多泥点”弹窗,“使用详情”进入泥点账单。主站各位置必须保持同一组件、数据口径和交互语义,头像 / 设置等账号入口继续保留各自语义。 创作恢复参数只保留 `sessionId`、`profileId`、`draftId`、`workId` 这四个私有 query。它们只允许在同一条创作链路的结果页、生成页、工作台之间保留;切到首页、公开作品详情、runtime 或另一条玩法链路时必须清掉。平台入口刷新直达时,路径到玩法恢复目标、四个 query 归一化、生成页标记、大鱼吃小鱼 workId 兜底、作品 / 草稿身份匹配和跳一跳 / 敲木鱼恢复阶段落点统一由 `platformCreationUrlStateModel.ts` 解析,壳层只执行读取作品、恢复草稿和切换阶段等副作用。生成页等待时间统一以生成状态里的 `startedAtMs` 为准;创建该状态时优先使用后端 session 下发的时间戳,作品摘要里的 `updatedAt` 仍只用于排序与摘要展示,不作为前端自行推导业务状态的真相。 diff --git a/docs/【项目基线】当前产品与工程约束-2026-05-15.md b/docs/【项目基线】当前产品与工程约束-2026-05-15.md index 8f6ad684c..8665e27cf 100644 --- a/docs/【项目基线】当前产品与工程约束-2026-05-15.md +++ b/docs/【项目基线】当前产品与工程约束-2026-05-15.md @@ -56,14 +56,15 @@ Genarrative / 陶泥儿是一个 AI 原生互动内容与小游戏平台。当 ## 账户与充值 -1. “我的”页账户充值弹窗包含 `泥点充值` 与 `会员卡充值` 两个页签,入口必须打开独立弹窗,不在当前面板下方展开。 -2. 泥点默认档位为 `60 / 180 / 300 / 680 / 1280 / 3280`,会员默认档位为月卡、季卡、年卡;实际展示、下单校验和支付确认都以后端返回的充值商品配置为准。 -3. 首充双倍按泥点商品档位独立计算。用户买过 `points_60` 后,只影响 `points_60` 的首充展示和结算,其它未购买档位仍保留各自首充权益。 -4. 前端不得用 `hasPointsRecharged` 统一隐藏所有泥点档位首充权益;该字段只表示账号是否发生过任一泥点充值。 -5. 充值支付渠道只允许由设备平台隔离层解析为 `wechat_mp`、`wechat_mp_virtual`、`wechat_jsapi`、`wechat_h5` 或 `wechat_native`;生产真实支付不得默认落到 `mock`,缺失或未知 `paymentChannel` 必须拒绝。 -6. 小程序 WebView 充值使用 `wechat_mp_virtual` 调起小程序虚拟支付;微信内浏览器使用 `wechat_jsapi` 调起微信支付 JSAPI;普通 Web 使用 `wechat_native` 二维码支付,避免因移动 UA、触控能力或窄屏误入 `wechat_h5`。只有微信通知或查单确认 `SUCCESS` 后才刷新余额或会员状态。 -7. 后端必须按 access JWT 中的最小设备快照拦截真实微信充值路径,不能只依赖前端隐藏入口或请求体传入的 `paymentChannel`。 -8. 后台“充值商品”页维护泥点和会员商品配置,保存后影响新的充值中心快照、下单和支付确认;历史订单保留下单时快照。 +1. 主站和图片画板统一使用公共泥点资产入口。收起态展示“泥点图标 + 泥点总额 | 充值”;桌面端通过 hover / focus 展开,移动端通过点击展开。展开态只展示不限时泥点、每日免费泥点及“每天重置为 20 泥点”,并提供“使用详情”入口;余额都以后端充值中心 read model 为准,前端不得自行相减推算。 +2. 账户充值弹窗标题统一为“购买更多泥点”,当前版本只展示泥点商品,不展示会员页签、会员商品、购买会员或升级会员入口。底层会员数据与周期刷新能力继续保留用于存量兼容和结算,会员周期限时泥点不在当前版本前台展示。 +3. 泥点默认商品固定为四档:`60 泥点 / ¥6`、`180 + 90 泥点 / ¥18`、`300 + 150 泥点 / ¥30`、`680 + 340 泥点 / ¥68`。`60` 档不加赠,后三档首次购买各加赠基础泥点的 `50%`;实际展示、下单校验和支付确认仍以后端返回的充值商品配置为准。 +4. 首充加赠资格按泥点商品档位独立计算。用户买过 `points_180` 后,只影响 `points_180` 的首充展示和结算,其它未购买档位仍保留各自首充加赠资格。 +5. 前端不得用 `hasPointsRecharged` 统一隐藏所有泥点档位首充权益;该字段只表示账号是否发生过任一泥点充值。 +6. 充值支付渠道只允许由设备平台隔离层解析为 `wechat_mp`、`wechat_mp_virtual`、`wechat_jsapi`、`wechat_h5` 或 `wechat_native`;生产真实支付不得默认落到 `mock`,缺失或未知 `paymentChannel` 必须拒绝。 +7. 小程序 WebView 充值使用 `wechat_mp_virtual` 调起小程序虚拟支付;微信内浏览器使用 `wechat_jsapi` 调起微信支付 JSAPI;普通 Web 使用 `wechat_native` 二维码支付,避免因移动 UA、触控能力或窄屏误入 `wechat_h5`。只有微信通知或查单确认 `SUCCESS` 后才刷新余额或会员状态。 +8. 后端必须按 access JWT 中的最小设备快照拦截真实微信充值路径,不能只依赖前端隐藏入口或请求体传入的 `paymentChannel`。 +9. 后台“充值商品”页继续维护泥点和会员商品配置,保存后影响新的充值中心快照、下单和支付确认;历史订单保留下单时快照。会员商品配置保留不表示当前版本开放公开购买或升级入口。 ## 唯一后端路线 @@ -102,10 +103,10 @@ server-rs + Axum + SpacetimeDB 7. 主站入口已锁定移动端页面级缩放;单个游戏页面不要再重复实现整页缩放锁定。 8. 图像输入通用 UI 统一走 `src/components/common/CreativeImageInputPanel.tsx`。外层页面持有业务状态,组件只承担上传卡、预览、参考图缩略图、AI 重绘开关、错误展示和提交按钮。 9. 发现页 `分类` 子频道的筛选必须打开独立 dialog / drawer / modal,至少支持玩法类型过滤与排序切换;筛选结果为空时显示空状态,不把筛选内容展开在当前列表下方。 -10. 移动端“我的”页顶部品牌行承载扫码和设置入口,正文按参考图顺序组织为头像 / 昵称 / 陶泥号、会员横幅、三张统计卡、每日任务、五项常用功能宫格、通用设置入口和法律信息;`media/profile/` 中的陶泥素材作为该页图形资产。常用功能宫格固定承载泥点充值、邀请好友、兑换码、玩家社区、反馈与建议;当前只展示四项常驻入口时必须按四列铺满整行,不保留五列网格导致左对齐空位。页面不再提供独立存档按钮入口,也不在底部保留旧的填邀请码次级入口;主题设置、账号与安全只作为通用设置弹窗下一级入口,不在“我的”页外层单独占行。填邀请码只由邀请链接 query 或其它明确引导打开独立弹窗,不作为“我的”页常驻按钮。 -11. “我的”页每日任务卡必须展示后端 `/api/profile/tasks` 返回的当前任务摘要,包括奖励泥点数和进度;外层任务卡不展示“去完成”等左右侧行动按钮,领取 / 去完成 / 已完成状态只在任务中心弹窗内表达。任务领取成功后,卡片摘要必须跟随返回的任务中心数据同步刷新,不能继续硬编码 `0 / 1` 或只更新弹窗内任务列表。用户停留在“我的”页跨过北京时间 0 点时,前端必须非阻断刷新登录态以补齐 `daily_login` 埋点,再重拉任务中心,避免继续展示上一自然日已领取状态。 -12. “我的”页泥点余额、累计游玩、已玩游戏三张统计卡只展示各自标签和值,三个统计 icon 使用小尺寸普通 UI 档位,内容不换行,不在统计区底部展示“更新于”时间;移动端昵称、会员卡、每日任务、常用功能和法律信息也应保持 `10px` 到 `14px` 的普通 UI 字号区间,避免展示级字号挤压内容。 -13. 移动端“我的”页需要兼容窄屏:头像 / 昵称 / 陶泥号、三张统计卡、每日任务、五项常用功能和法律信息都必须能在底部固定 TabBar 上方完整滚动露出,不得与底部 dock、刘海 safe-area 或相邻 UI 元素遮挡重叠。 +10. 移动端“我的”页顶部品牌行承载扫码和设置入口,正文按参考图顺序组织为头像 / 昵称 / 陶泥号、三张统计卡、五项常用功能宫格、通用设置入口和法律信息;`media/profile/` 中的陶泥素材作为该页图形资产。常用功能宫格固定承载泥点充值、邀请好友、兑换码、玩家社区、反馈与建议;当前只展示四项常驻入口时必须按四列铺满整行,不保留五列网格导致左对齐空位。页面不再提供会员购买 / 升级横幅、每日任务卡片或任务中心入口,也不提供独立存档按钮入口,不在底部保留旧的填邀请码次级入口;主题设置、账号与安全只作为通用设置弹窗下一级入口,不在“我的”页外层单独占行。填邀请码只由邀请链接 query 或其它明确引导打开独立弹窗,不作为“我的”页常驻按钮。 +11. 每日免费泥点由后端独立余额桶承载,基础额度固定为 `20`,按北京时间每日 `00:00` 重置。跨业务日退款时,原消费中的每日免费泥点部分叠加到退款当日每日免费桶,当日余额允许超过 `20`;到下一业务日仍统一失效并重置为 `20`。主站不得把已隐藏的每日任务入口或 `daily_task_reward` 文案继续当作每日免费泥点入口。 +12. “我的”页泥点余额、累计游玩、已玩游戏三张统计卡只展示各自标签和值,三个统计 icon 使用小尺寸普通 UI 档位,内容不换行,不在统计区底部展示“更新于”时间;移动端昵称、常用功能和法律信息也应保持 `10px` 到 `14px` 的普通 UI 字号区间,避免展示级字号挤压内容。 +13. 移动端“我的”页需要兼容窄屏:头像 / 昵称 / 陶泥号、三张统计卡、五项常用功能和法律信息都必须能在底部固定 TabBar 上方完整滚动露出,不得与底部 dock、刘海 safe-area 或相邻 UI 元素遮挡重叠。 14. RPG 等运行态的战斗飘字、血量变化和即时反馈必须在暗色、噪声高的场景背景上保持可读:使用高亮文字、深色描边、强阴影或小面积半透明底,不只依赖红/绿文字本身表达伤害或治疗。 15. 平台亮色 UI 配色以陶泥儿主视觉为准:暖白 / 米杏底、陶土橙主按钮、深棕正文与浅杏边框;新增界面优先复用 `src/index.css` 的 `--platform-*` 主题变量和 `apps/admin-web/src/styles/admin.css` 的同系色值,不再引入粉红、蓝绿等独立主色方案。 diff --git a/index.html b/index.html index bc00d59ee..1b183cb68 100644 --- a/index.html +++ b/index.html @@ -7,7 +7,45 @@ content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" /> - 陶泥儿 + 陶泥儿 Genarrative|游戏美术AI创作工具与美术Agent工作台 + + + + + + + + + + + +
diff --git a/jenkins/Jenkinsfile.production-api-deploy b/jenkins/Jenkinsfile.production-api-deploy index 8c21fe16f..aef01f183 100644 --- a/jenkins/Jenkinsfile.production-api-deploy +++ b/jenkins/Jenkinsfile.production-api-deploy @@ -17,6 +17,7 @@ pipeline { string(name: 'BUILD_JOB_NAME', defaultValue: 'Genarrative-Api-Build', description: 'API 构建流水线作业名') string(name: 'BUILD_NUMBER_TO_DEPLOY', defaultValue: '', description: '要复制归档产物的上游构建号') booleanParam(name: 'INCLUDE_PINGORA_GATEWAY', defaultValue: true, description: '上游构建是否包含 Pingora 影子网关产物;release 发布默认必须包含') + booleanParam(name: 'KEEP_MAINTENANCE_MODE', defaultValue: false, description: '发布成功且 readiness 通过后是否继续保持维护模式;默认退出维护') string(name: 'RELEASE_ROOT', defaultValue: '/opt/genarrative/releases', description: '生产 release 根目录') string(name: 'CURRENT_LINK', defaultValue: '/opt/genarrative/current', description: '当前版本软链接') string(name: 'SERVICE_NAME', defaultValue: 'genarrative-api.service', description: 'systemd 服务名') @@ -98,9 +99,13 @@ pipeline { set -euo pipefail chmod +x "build/${BUILD_VERSION}/scripts/deploy/production-api-deploy.sh" "build/${BUILD_VERSION}/scripts/deploy/maintenance-on.sh" "build/${BUILD_VERSION}/scripts/deploy/maintenance-off.sh" "build/${BUILD_VERSION}/scripts/deploy/pingora-direct-enable.sh" "build/${BUILD_VERSION}/scripts/deploy/pingora-direct-rollback.sh" "build/${BUILD_VERSION}/scripts/deploy/pingora-realpath-canary-enable.sh" "build/${BUILD_VERSION}/scripts/deploy/pingora-realpath-canary-disable.sh" "build/${BUILD_VERSION}/scripts/deploy/pingora-health-patrol-env-switch.mjs" "build/${BUILD_VERSION}/scripts/deploy/pingora-gateway-env-shadow-switch.mjs" "build/${BUILD_VERSION}/scripts/deploy/pingora-tls-cert-sync.mjs" pingora_deploy_args=() + maintenance_deploy_args=() if [[ "${INCLUDE_PINGORA_GATEWAY:-false}" == "true" ]]; then pingora_deploy_args+=(--require-pingora-gateway) fi + if [[ "${KEEP_MAINTENANCE_MODE:-false}" == "true" ]]; then + maintenance_deploy_args+=(--keep-maintenance-mode) + fi "build/${BUILD_VERSION}/scripts/deploy/production-api-deploy.sh" \ --source-dir "build/${BUILD_VERSION}" \ --version "${BUILD_VERSION}" \ @@ -108,6 +113,7 @@ pipeline { --current-link "${CURRENT_LINK}" \ --service "${SERVICE_NAME}" \ "${pingora_deploy_args[@]}" \ + "${maintenance_deploy_args[@]}" \ --health-url "${HEALTH_URL}" \ --api-env-file "${API_ENV_FILE:-/etc/genarrative/api-server.env}" \ --worker-env-file "${WORKER_ENV_FILE:-/etc/genarrative/external-generation-worker.env}" \ @@ -133,6 +139,7 @@ pipeline { string(name: 'DEPLOY_TARGET', value: params.DEPLOY_TARGET ?: ''), string(name: 'DATABASE', value: params.DATABASE ?: ''), string(name: 'INCLUDE_PINGORA_GATEWAY', value: String.valueOf(params.INCLUDE_PINGORA_GATEWAY)), + string(name: 'KEEP_MAINTENANCE_MODE', value: String.valueOf(params.KEEP_MAINTENANCE_MODE)), string(name: 'SUMMARY', value: 'API 发布流水线结束'), ] def notificationRecipients = params.NOTIFICATION_EMAILS?.trim() diff --git a/jenkins/Jenkinsfile.production-full-build-and-deploy b/jenkins/Jenkinsfile.production-full-build-and-deploy index 23ea50342..476dc1b7e 100644 --- a/jenkins/Jenkinsfile.production-full-build-and-deploy +++ b/jenkins/Jenkinsfile.production-full-build-and-deploy @@ -24,7 +24,7 @@ pipeline { string(name: 'BUILD_VERSION', defaultValue: '', description: '发布版本号,留空则使用 Jenkins BUILD_NUMBER') booleanParam(name: 'RUN_NPM_CI', defaultValue: true, description: 'Web 构建前是否执行 npm ci') string(name: 'NOTIFICATION_EMAILS', defaultValue: '', description: '本次运行追加通知邮箱;会与 Jenkins Secret Text 凭据 genarrative-notification-emails 合并发送') - string(name: 'MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID', defaultValue: '', description: '必填:Stdb module 构建与发布共用的 Jenkins Secret File 凭据 ID') + string(name: 'MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID', defaultValue: 'genarrative-spacetime-bootstrap-secret-dev-file', description: '必填:Stdb module 构建与发布共用的 Jenkins Secret File 凭据 ID') booleanParam(name: 'INCLUDE_PINGORA_GATEWAY', defaultValue: true, description: 'API release 是否构建、归档并部署 Pingora 影子网关;release 默认必须包含') string(name: 'WEB_BUILD_JOB_NAME', defaultValue: 'Genarrative-Web-Build', description: 'Web 构建流水线作业名') string(name: 'API_BUILD_JOB_NAME', defaultValue: 'Genarrative-Api-Build', description: 'API 构建流水线作业名') @@ -40,6 +40,9 @@ pipeline { string(name: 'SPACETIME_RUN_AS_USER', defaultValue: 'spacetimedb', description: 'Stdb 发布使用的本机用户') string(name: 'API_ENV_FILE', defaultValue: '/etc/genarrative/api-server.env', description: 'API 与 Stdb publish 共用的 api-server 环境文件') string(name: 'WORKER_ENV_FILE', defaultValue: '/etc/genarrative/external-generation-worker.env', description: 'API 与 Stdb publish 共用的 worker 环境文件') + choice(name: 'STDB_API_ROLLOUT_MODE', choices: ['normal', 'pause-after-stdb'], description: '定时任务默认 normal 完整发布 dev;人工维护窗口才选择 pause-after-stdb') + string(name: 'STDB_API_ROLLOUT_APPROVERS', defaultValue: '', description: 'pause-after-stdb 必填:允许放行 API 的 Jenkins 用户/组,多个值用逗号分隔') + booleanParam(name: 'EXIT_MAINTENANCE_MODE_AFTER_COMPLETION', defaultValue: true, description: '完整发布成功后是否退出维护模式;取消勾选会保留维护页,便于内网验收后人工恢复公网') } stages { @@ -104,6 +107,9 @@ pipeline { if (params.DEPLOY_TARGET == 'release' && !params.CONFIRM_RELEASE_DEPLOY_AGENT) { error('release 部署需要先配置独立 release 部署 agent,并勾选 CONFIRM_RELEASE_DEPLOY_AGENT。当前 Linux 开发/构建/开发部署 agent 不能执行 release 部署。') } + if (params.STDB_API_ROLLOUT_MODE == 'pause-after-stdb' && !params.STDB_API_ROLLOUT_APPROVERS?.trim()) { + error('pause-after-stdb 必须填写 STDB_API_ROLLOUT_APPROVERS。') + } } } } @@ -124,6 +130,7 @@ pipeline { string(name: 'BUILD_VERSION', value: env.EFFECTIVE_BUILD_VERSION), string(name: 'NOTIFICATION_EMAILS', value: params.NOTIFICATION_EMAILS ?: ''), booleanParam(name: 'RUN_NPM_CI', value: params.RUN_NPM_CI), + booleanParam(name: 'PUBLISH_AFTER_BUILD', value: false), ] env.WEB_BUILD_NUMBER = webRun.number.toString() } @@ -141,6 +148,7 @@ pipeline { string(name: 'BUILD_VERSION', value: env.EFFECTIVE_BUILD_VERSION), string(name: 'NOTIFICATION_EMAILS', value: params.NOTIFICATION_EMAILS ?: ''), booleanParam(name: 'INCLUDE_PINGORA_GATEWAY', value: params.INCLUDE_PINGORA_GATEWAY), + booleanParam(name: 'PUBLISH_AFTER_BUILD', value: false), ] env.API_BUILD_NUMBER = apiRun.number.toString() } @@ -159,6 +167,7 @@ pipeline { string(name: 'NOTIFICATION_EMAILS', value: params.NOTIFICATION_EMAILS ?: ''), string(name: 'MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID', value: params.MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID ?: ''), string(name: 'DATABASE', value: params.DATABASE), + booleanParam(name: 'PUBLISH_AFTER_BUILD', value: false), ] env.STDB_BUILD_NUMBER = stdbRun.number.toString() } @@ -184,6 +193,7 @@ pipeline { string(name: 'SPACETIME_RUN_AS_USER', value: params.SPACETIME_RUN_AS_USER ?: 'spacetimedb'), string(name: 'API_ENV_FILE', value: params.API_ENV_FILE ?: '/etc/genarrative/api-server.env'), string(name: 'WORKER_ENV_FILE', value: params.WORKER_ENV_FILE ?: '/etc/genarrative/external-generation-worker.env'), + booleanParam(name: 'KEEP_MAINTENANCE_MODE', value: true), string(name: 'DEPLOY_TARGET', value: params.DEPLOY_TARGET), booleanParam(name: 'CONFIRM_RELEASE_DEPLOY_AGENT', value: params.CONFIRM_RELEASE_DEPLOY_AGENT), string(name: 'BUILD_JOB_NAME', value: params.STDB_BUILD_JOB_NAME), @@ -192,6 +202,17 @@ pipeline { } } + stage('Stdb / Api Rollout Gate') { + when { + expression { return params.STDB_API_ROLLOUT_MODE == 'pause-after-stdb' } + } + steps { + timeout(time: 4, unit: 'HOURS') { + input message: 'SpacetimeDB module 已发布,站点和外部生成服务保持维护态。请使用与 api-server GENARRATIVE_SPACETIME_TOKEN 不同的 migration operator identity,在目标部署 agent 用本次 Stdb artifact 中的维护脚本完成 dry-run、分批 apply 与摘要回填;确认无 has_more,并确认 API runtime identity 未被授权为 migration operator 后再继续部署 API。', ok: '确认维护完成,继续部署 API', submitter: params.STDB_API_ROLLOUT_APPROVERS.trim(), submitterParameter: 'STDB_API_ROLLOUT_APPROVED_BY' + } + } + } + stage('Deploy Api') { steps { build job: params.API_DEPLOY_JOB_NAME, @@ -207,6 +228,7 @@ pipeline { string(name: 'BUILD_JOB_NAME', value: params.API_BUILD_JOB_NAME), string(name: 'BUILD_NUMBER_TO_DEPLOY', value: env.API_BUILD_NUMBER), booleanParam(name: 'INCLUDE_PINGORA_GATEWAY', value: params.INCLUDE_PINGORA_GATEWAY), + booleanParam(name: 'KEEP_MAINTENANCE_MODE', value: true), string(name: 'API_ENV_FILE', value: params.API_ENV_FILE ?: '/etc/genarrative/api-server.env'), string(name: 'WORKER_ENV_FILE', value: params.WORKER_ENV_FILE ?: '/etc/genarrative/external-generation-worker.env'), string(name: 'DATABASE', value: params.DATABASE), @@ -232,6 +254,28 @@ pipeline { ] } } + + stage('Exit Maintenance') { + when { + expression { return params.EXIT_MAINTENANCE_MODE_AFTER_COMPLETION != false } + } + agent { + label "${params.DEPLOY_TARGET == 'development' ? 'linux && genarrative-dev-deploy' : 'linux && genarrative-release-deploy'}" + } + steps { + sh ''' + bash -lc ' + set -euo pipefail + maintenance_script="/opt/genarrative/current/scripts/deploy/maintenance-off.sh" + if [[ ! -f "${maintenance_script}" ]]; then + echo "Full 发布完成但 current release 缺少维护退出脚本: ${maintenance_script}" >&2 + exit 1 + fi + bash "${maintenance_script}" + ' + ''' + } + } } post { @@ -248,6 +292,7 @@ pipeline { string(name: 'DEPLOY_TARGET', value: params.DEPLOY_TARGET ?: ''), string(name: 'DATABASE', value: params.DATABASE ?: ''), string(name: 'INCLUDE_PINGORA_GATEWAY', value: String.valueOf(params.INCLUDE_PINGORA_GATEWAY)), + string(name: 'EXIT_MAINTENANCE_MODE_AFTER_COMPLETION', value: String.valueOf(params.EXIT_MAINTENANCE_MODE_AFTER_COMPLETION)), string(name: 'SUMMARY', value: '全量构建发布编排结束'), ] def notificationRecipients = params.NOTIFICATION_EMAILS?.trim() diff --git a/jenkins/Jenkinsfile.production-server-provision b/jenkins/Jenkinsfile.production-server-provision index 268eff789..e4d3976b9 100644 --- a/jenkins/Jenkinsfile.production-server-provision +++ b/jenkins/Jenkinsfile.production-server-provision @@ -25,7 +25,7 @@ pipeline { string(name: 'PROVISION_DOWNLOADS_DIR', defaultValue: 'provision-tool-downloads', description: '目标服务器工作区内暂存 SpacetimeDB/otelcol 安装包的相对目录') string(name: 'PROVISION_TOOLS_DIR', defaultValue: 'provision-tools', description: '目标机工作区内由已下载安装包生成的工具包目录') string(name: 'PROVISION_DOWNLOAD_PROXY', defaultValue: '', description: '可选,目标服务器下载 SpacetimeDB 和 otelcol-contrib 时使用的代理地址,例如 http://127.0.0.1:7890;留空不设置代理') - string(name: 'SPACETIME_DOWNLOAD_ROOT', defaultValue: 'https://github.com/clockworklabs/SpacetimeDB/releases/download/v2.5.0', description: '目标服务器使用的 SpacetimeDB Linux release tarball 根地址;默认固定到项目锁定版本') + string(name: 'SPACETIME_DOWNLOAD_ROOT', defaultValue: 'https://github.com/clockworklabs/SpacetimeDB/releases/download/v2.6.0', description: '目标服务器使用的 SpacetimeDB Linux release tarball 根地址;默认固定到项目锁定版本') string(name: 'SPACETIME_TARGET_HOST', defaultValue: 'x86_64-unknown-linux-gnu', description: 'SpacetimeDB 预编译包 host triple,development/release Linux amd64 使用默认值') string(name: 'SPACETIME_ROOT', defaultValue: '/stdb', description: 'SpacetimeDB root-dir') string(name: 'RELEASE_ROOT', defaultValue: '/opt/genarrative/releases', description: 'release 根目录') @@ -199,7 +199,7 @@ BASH OTELCOL_VERSION="${OTELCOL_VERSION:-0.151.0}" \ PREPARE_OTELCOL="${ENABLE_OTELCOL:-true}" \ PROVISION_DOWNLOAD_PROXY="${PROVISION_DOWNLOAD_PROXY:-}" \ - SPACETIME_DOWNLOAD_ROOT="${SPACETIME_DOWNLOAD_ROOT:-https://github.com/clockworklabs/SpacetimeDB/releases/download/v2.5.0}" \ + SPACETIME_DOWNLOAD_ROOT="${SPACETIME_DOWNLOAD_ROOT:-https://github.com/clockworklabs/SpacetimeDB/releases/download/v2.6.0}" \ SPACETIME_TARGET_HOST="${SPACETIME_TARGET_HOST:-x86_64-unknown-linux-gnu}" \ SPACETIME_ROOT="${SPACETIME_ROOT:-/stdb}" \ scripts/prepare-server-provision-tools.sh diff --git a/jenkins/Jenkinsfile.production-stdb-module-build b/jenkins/Jenkinsfile.production-stdb-module-build index a5e7dac66..7eb6971c7 100644 --- a/jenkins/Jenkinsfile.production-stdb-module-build +++ b/jenkins/Jenkinsfile.production-stdb-module-build @@ -25,7 +25,7 @@ pipeline { string(name: 'COMMIT_HASH', defaultValue: '', description: '可选,指定属于 SOURCE_BRANCH 的 Git commit') string(name: 'BUILD_VERSION', defaultValue: '', description: '发布版本号,留空则使用 Jenkins BUILD_NUMBER') string(name: 'NOTIFICATION_EMAILS', defaultValue: '', description: '本次运行追加通知邮箱;会与 Jenkins Secret Text 凭据 genarrative-notification-emails 合并发送') - string(name: 'MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID', defaultValue: '', description: '必填:与生产模块绑定的 Jenkins Secret File 凭据 ID;仅在受控构建和发布阶段短暂挂载') + string(name: 'MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID', defaultValue: 'genarrative-spacetime-bootstrap-secret-dev-file', description: '必填:与生产模块绑定的 Jenkins Secret File 凭据 ID;仅在受控构建和发布阶段短暂挂载') booleanParam(name: 'PUBLISH_AFTER_BUILD', defaultValue: false, description: '构建成功后是否触发 Stdb module 发布') string(name: 'DEPLOY_JOB_NAME', defaultValue: 'Genarrative-Stdb-Module-Publish', description: 'Stdb module 发布流水线作业名') choice(name: 'DEPLOY_TARGET', choices: ['development', 'release'], description: 'PUBLISH_AFTER_BUILD=true 时的逻辑部署目标;development 使用当前 Linux 开发/构建/开发部署 agent') @@ -148,7 +148,7 @@ pipeline { stage('Archive') { steps { - archiveArtifacts artifacts: "build/${env.EFFECTIVE_BUILD_VERSION}/spacetime_module.wasm,build/${env.EFFECTIVE_BUILD_VERSION}/spacetime_module.wasm.sha256,build/${env.EFFECTIVE_BUILD_VERSION}/release-manifest.json,scripts/deploy/production-stdb-publish.sh,scripts/deploy/production-runtime-writer-identity-rotate.mjs,scripts/deploy/maintenance-on.sh,scripts/deploy/maintenance-off.sh,scripts/spacetime-migration-common.mjs,scripts/database-backup-to-oss.mjs", fingerprint: true + archiveArtifacts artifacts: "build/${env.EFFECTIVE_BUILD_VERSION}/spacetime_module.wasm,build/${env.EFFECTIVE_BUILD_VERSION}/spacetime_module.wasm.sha256,build/${env.EFFECTIVE_BUILD_VERSION}/release-manifest.json,scripts/deploy/production-stdb-publish.sh,scripts/deploy/production-runtime-writer-identity-rotate.mjs,scripts/deploy/maintenance-on.sh,scripts/deploy/maintenance-off.sh,scripts/spacetime-migration-common.mjs,scripts/spacetime-maintain-external-generation-jobs.mjs,scripts/database-backup-to-oss.mjs", fingerprint: true } } diff --git a/jenkins/Jenkinsfile.production-stdb-module-publish b/jenkins/Jenkinsfile.production-stdb-module-publish index d7eebc84f..892b87e9f 100644 --- a/jenkins/Jenkinsfile.production-stdb-module-publish +++ b/jenkins/Jenkinsfile.production-stdb-module-publish @@ -13,7 +13,7 @@ pipeline { string(name: 'SOURCE_BRANCH', defaultValue: 'master', description: '上游构建源码分支') string(name: 'COMMIT_HASH', defaultValue: '', description: '上游构建源码 commit') string(name: 'NOTIFICATION_EMAILS', defaultValue: '', description: '本次运行追加通知邮箱;会与 Jenkins Secret Text 凭据 genarrative-notification-emails 合并发送') - string(name: 'MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID', defaultValue: '', description: '必填:与目标 wasm 一致的 Jenkins Secret File 凭据 ID;仅在发布时受保护挂载') + string(name: 'MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID', defaultValue: 'genarrative-spacetime-bootstrap-secret-dev-file', description: '必填:与目标 wasm 一致的 Jenkins Secret File 凭据 ID;仅在发布时受保护挂载') string(name: 'BUILD_VERSION', defaultValue: '', description: '待发布版本号') string(name: 'BUILD_JOB_NAME', defaultValue: 'Genarrative-Stdb-Module-Build', description: 'Stdb module 构建流水线作业名') string(name: 'BUILD_NUMBER_TO_DEPLOY', defaultValue: '', description: '要复制归档产物的上游构建号') @@ -24,7 +24,7 @@ pipeline { string(name: 'SPACETIME_RUN_AS_USER', defaultValue: 'spacetimedb', description: '执行 spacetime publish 的本机用户,默认使用自托管服务用户') string(name: 'API_ENV_FILE', defaultValue: '/etc/genarrative/api-server.env', description: '需补齐 runtime bootstrap secret FILE 的 api-server 环境文件') string(name: 'WORKER_ENV_FILE', defaultValue: '/etc/genarrative/external-generation-worker.env', description: '需补齐 runtime bootstrap secret FILE 的 worker 环境文件;文件不存在时跳过') - booleanParam(name: 'CLEAR_DATABASE', defaultValue: false, description: '是否清空数据库后发布') + booleanParam(name: 'KEEP_MAINTENANCE_MODE', defaultValue: false, description: '发布 module 后保持维护模式并停止旧 API/controller/worker,等待受控维护和后续 API deploy') choice(name: 'DATABASE_BACKUP_MODE', choices: ['async', 'sync', 'skip'], description: '数据库备份策略:async 在 publish 前生成本地冷备份、后台上传 OSS;sync 在 publish 前等待上传完成且失败阻断;skip 跳过') } @@ -93,7 +93,7 @@ pipeline { copyArtifacts( projectName: params.BUILD_JOB_NAME, selector: specific(params.BUILD_NUMBER_TO_DEPLOY), - filter: "build/${params.BUILD_VERSION}/spacetime_module.wasm,build/${params.BUILD_VERSION}/spacetime_module.wasm.sha256,build/${params.BUILD_VERSION}/release-manifest.json,scripts/deploy/production-stdb-publish.sh,scripts/deploy/production-runtime-writer-identity-rotate.mjs,scripts/deploy/maintenance-on.sh,scripts/deploy/maintenance-off.sh,scripts/spacetime-migration-common.mjs,scripts/database-backup-to-oss.mjs", + filter: "build/${params.BUILD_VERSION}/spacetime_module.wasm,build/${params.BUILD_VERSION}/spacetime_module.wasm.sha256,build/${params.BUILD_VERSION}/release-manifest.json,scripts/deploy/production-stdb-publish.sh,scripts/deploy/production-runtime-writer-identity-rotate.mjs,scripts/deploy/maintenance-on.sh,scripts/deploy/maintenance-off.sh,scripts/spacetime-migration-common.mjs,scripts/spacetime-maintain-external-generation-jobs.mjs,scripts/database-backup-to-oss.mjs", target: '.', fingerprintArtifacts: true ) @@ -106,7 +106,7 @@ pipeline { } steps { script { - def clearArg = params.CLEAR_DATABASE ? '--clear-database' : '' + def keepMaintenanceArg = params.KEEP_MAINTENANCE_MODE ? '--keep-maintenance-mode' : '' def backupMode = params.DATABASE_BACKUP_MODE?.trim() ? params.DATABASE_BACKUP_MODE.trim() : 'async' if (!(backupMode in ['async', 'sync', 'skip'])) { error("DATABASE_BACKUP_MODE 只能是 async、sync 或 skip: ${backupMode}") @@ -146,7 +146,7 @@ pipeline { --migration-bootstrap-secret-file "\${MIGRATION_BOOTSTRAP_SECRET_FILE:?MIGRATION_BOOTSTRAP_SECRET_FILE 不能为空}" \\ --api-env-file "${params.API_ENV_FILE}" \\ --worker-env-file "${params.WORKER_ENV_FILE}" \\ - ${clearArg} \\ + ${keepMaintenanceArg} \\ ${backupArg} ' """ diff --git a/package.json b/package.json index 69954509c..65e810076 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "admin-web:typecheck": "node scripts/admin-web-build.mjs typecheck", "admin-web:preview": "npm --prefix apps/admin-web run preview --", "spacetime:generate": "node scripts/generate-spacetime-bindings.mjs", + "spacetime:external-generation:maintain": "node scripts/spacetime-maintain-external-generation-jobs.mjs", "check:api-server-env": "node scripts/check-api-server-env.mjs", "check:spacetime-runtime-access": "node scripts/check-spacetime-runtime-access.mjs", "deploy:rust:remote": "node scripts/run-bash-script.mjs scripts/deploy-rust-remote.sh", @@ -36,6 +37,7 @@ "check:production-api-deploy": "node scripts/check-production-api-deploy.mjs", "check:pingora-gateway-smoke": "node scripts/check-pingora-gateway-smoke.mjs", "check:nginx-pingora-canary": "node scripts/check-nginx-pingora-canary.mjs", + "check:nginx-spa-routes": "node scripts/check-nginx-spa-routes.mjs", "check:pingora-route-parity": "node scripts/check-pingora-route-parity.mjs", "check:pingora-canary-live": "node scripts/check-pingora-canary-live.mjs", "check:pingora-canary-live-guard": "node scripts/check-pingora-canary-live-guard.mjs", diff --git a/packages/shared/src/contracts/runtime.ts b/packages/shared/src/contracts/runtime.ts index 7ce7b2439..a01f70547 100644 --- a/packages/shared/src/contracts/runtime.ts +++ b/packages/shared/src/contracts/runtime.ts @@ -51,6 +51,15 @@ export type ProfileDashboardSummary = { totalPlayTimeMs: number; playedWorldCount: number; updatedAt: string | null; + dailyFreePoints?: ProfileDailyFreePoints; +}; + +export type ProfileDailyFreePoints = { + dayKey: number; + grantedPoints: number; + remainingPoints: number; + resetsAt: string; + updatedAt: string; }; export type ProfileWalletLedgerEntry = { @@ -65,6 +74,8 @@ export type ProfileWalletLedgerEntry = { | 'points_recharge' | 'membership_period_grant' | 'membership_period_reset' + | 'daily_free_grant' + | 'daily_free_reset' | 'asset_operation_consume' | 'asset_operation_refund' | 'redeem_code_reward' @@ -162,6 +173,16 @@ export type ProfileMembership = { cyclePeriodDays: number; }; +export type ProfileMudPointBalance = { + totalPoints: number; + permanentPoints: number; + limitedPoints: number; + limitedExpiresAt: string | null; + dailyFreePoints: number; + dailyFreeResetPoints: number; + dailyFreeResetsAt: string; +}; + export type ProfileRechargeOrder = { orderId: string; productId: string; @@ -183,12 +204,14 @@ export type ProfileRechargeOrder = { export type ProfileRechargeCenterResponse = { walletBalance: number; + mudPointBalance?: ProfileMudPointBalance; membership: ProfileMembership; pointProducts: ProfileRechargeProduct[]; membershipProducts: ProfileRechargeProduct[]; benefits: ProfileMembershipBenefit[]; latestOrder: ProfileRechargeOrder | null; hasPointsRecharged: boolean; + dailyFreePoints?: ProfileDailyFreePoints; }; export type WechatMiniProgramPayParams = { diff --git a/public/maintenance.html b/public/maintenance.html new file mode 100644 index 000000000..1be43ee47 --- /dev/null +++ b/public/maintenance.html @@ -0,0 +1,39 @@ + + + + + + 服务维护中 + + + +
+

服务维护中

+

我们正在更新系统,稍后请重新访问。

+
+ + diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 000000000..5a957b98f --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,21 @@ +User-agent: * +Allow: / + +Disallow: /api +Disallow: /v1 +Disallow: /admin +Disallow: /creation +Disallow: /runtime +Disallow: /editor +Disallow: /project +Disallow: /profile +Disallow: /works/detail +Disallow: /worlds/detail +Disallow: /gallery +Disallow: /puzzle +Disallow: /big-fish +Disallow: /match3d +Disallow: /bark-battle +Disallow: /child-motion-demo + +Sitemap: https://www.genarrative.world/sitemap.xml diff --git a/public/sitemap.xml b/public/sitemap.xml new file mode 100644 index 000000000..4f6156bd4 --- /dev/null +++ b/public/sitemap.xml @@ -0,0 +1,8 @@ + + + + https://www.genarrative.world/ + weekly + 1.0 + + diff --git a/scripts/build-production-release.sh b/scripts/build-production-release.sh index 458b6193f..fb368e3ce 100644 --- a/scripts/build-production-release.sh +++ b/scripts/build-production-release.sh @@ -546,6 +546,7 @@ chmod +x \ copy_required_file "${SCRIPT_DIR}/spacetime-export-migration-json.mjs" "${TARGET_DIR}/scripts/database-export.mjs" "数据库导出脚本" copy_required_file "${SCRIPT_DIR}/spacetime-import-migration-json.mjs" "${TARGET_DIR}/scripts/database-import.mjs" "数据库导入脚本" copy_required_file "${SCRIPT_DIR}/spacetime-migration-common.mjs" "${TARGET_DIR}/scripts/spacetime-migration-common.mjs" "数据库迁移公共脚本" +copy_required_file "${SCRIPT_DIR}/spacetime-maintain-external-generation-jobs.mjs" "${TARGET_DIR}/scripts/spacetime-maintain-external-generation-jobs.mjs" "外部生成任务维护脚本" copy_required_file "${SCRIPT_DIR}/spacetime-authorize-migration-operator.mjs" "${TARGET_DIR}/scripts/spacetime-authorize-migration-operator.mjs" "数据库迁移授权脚本" copy_required_file "${SCRIPT_DIR}/spacetime-revoke-migration-operator.mjs" "${TARGET_DIR}/scripts/spacetime-revoke-migration-operator.mjs" "数据库迁移撤权脚本" copy_required_file "${SCRIPT_DIR}/database-backup-to-oss.mjs" "${TARGET_DIR}/scripts/database-backup-to-oss.mjs" "数据库 OSS 备份脚本" @@ -584,7 +585,7 @@ cat >"${TARGET_DIR}/README.md" < 0) { console.log('[check:database-backup] OK'); -function main() { +async function main() { + assertCanonicalQueryAndAuthorizationIncludeMultipartParameters(); assertInsufficientSpaceStopsBeforeServiceChanges(); assertArchiveFailureStillRestoresDependentServices(); + await assertMultipartUploadRetriesAndVerifiesRemoteLength(); + await assertMissingPartEtagAbortsMultipartUpload(); + await assertCompleteResponseAmbiguityUsesHeadVerification(); + await assertHeadLengthMismatchAbortsMultipartUpload(); +} + +function assertCanonicalQueryAndAuthorizationIncludeMultipartParameters() { + assertEqual(buildCanonicalQuery({uploads: null}), 'uploads', 'InitiateMultipartUpload 必须使用无等号的 uploads 参数。'); + assertEqual( + buildCanonicalQuery({uploadId: 'abc+/= xyz', partNumber: 12}), + 'partNumber=12&uploadId=abc%2B%2F%3D%20xyz', + 'multipart query 必须按 key 排序并使用 RFC3986 编码。', + ); + + const date = new Date('2026-07-13T10:20:30.000Z'); + const headers = { + host: 'genarrative-test.oss-cn-shanghai.aliyuncs.com', + 'content-type': 'application/octet-stream', + 'x-oss-content-sha256': 'UNSIGNED-PAYLOAD', + 'x-oss-date': '20260713T102030Z', + }; + const withoutQuery = buildAuthorization({ + method: 'PUT', + bucket: 'genarrative-test', + endpoint: 'oss-cn-shanghai.aliyuncs.com', + objectKey: 'database-backups/test/archive.tar.gz', + accessKeyId: 'test-access-key', + accessKeySecret: 'test-access-secret', + headers, + date, + }); + const withQuery = buildAuthorization({ + method: 'PUT', + bucket: 'genarrative-test', + endpoint: 'oss-cn-shanghai.aliyuncs.com', + objectKey: 'database-backups/test/archive.tar.gz', + accessKeyId: 'test-access-key', + accessKeySecret: 'test-access-secret', + headers, + date, + queries: {partNumber: 12, uploadId: 'abc+/= xyz'}, + }); + assertNotEqual(withQuery, withoutQuery, 'multipart query 必须参与 V4 Authorization 计算。'); + assertEqual( + withQuery, + 'OSS4-HMAC-SHA256 Credential=test-access-key/20260713/cn-shanghai/oss/aliyun_v4_request,AdditionalHeaders=host,Signature=9323dd3b7272b52f416c4d32115fcc00460eaccdcdaf011575c2502a63a27b1f', + 'multipart V4 Authorization 必须保持固定签名向量。', + ); } function assertInsufficientSpaceStopsBeforeServiceChanges() { @@ -78,6 +129,279 @@ function assertArchiveFailureStillRestoresDependentServices() { } } +async function assertMultipartUploadRetriesAndVerifiesRemoteLength() { + const root = path.join(tmpRoot, 'multipart-success'); + const archivePath = path.join(root, 'backup.tar.gz'); + const partSizeBytes = 100 * 1024; + const payload = Buffer.concat([ + Buffer.alloc(partSizeBytes, 'a'), + Buffer.alloc(partSizeBytes, 'b'), + Buffer.alloc(17, 'c'), + ]); + mkdirSync(root, {recursive: true}); + writeFileSync(archivePath, payload); + + const requests = []; + const retryDelays = []; + let firstPartAttempts = 0; + const uploadId = 'upload+/= id'; + const fetchImpl = async (url, options) => { + const body = await readRequestBody(options.body); + requests.push({url, method: options.method, headers: options.headers, body}); + const parsedUrl = new URL(url); + + if (options.method === 'POST' && parsedUrl.search === '?uploads') { + return new Response(`${uploadId}`, {status: 200}); + } + if (options.method === 'PUT') { + const partNumber = Number(parsedUrl.searchParams.get('partNumber')); + if (partNumber === 1) { + firstPartAttempts += 1; + if (firstPartAttempts === 1) { + return new Response('ServiceUnavailable', {status: 503}); + } + } + return new Response('', {status: 200, headers: {etag: `"etag-${partNumber}"`}}); + } + if (options.method === 'POST' && parsedUrl.searchParams.has('uploadId')) { + return new Response('', {status: 200, headers: {etag: '"complete-etag"'}}); + } + if (options.method === 'HEAD') { + return new Response(null, {status: 200, headers: {'content-length': String(payload.length)}}); + } + throw new Error(`unexpected request: ${options.method} ${url}`); + }; + + const result = await uploadArchive({ + archivePath, + bucket: 'genarrative-test', + endpoint: 'oss-cn-shanghai.aliyuncs.com', + objectKey: 'database-backups/test/backup.tar.gz', + accessKeyId: 'test-access-key', + accessKeySecret: 'test-access-secret', + partSizeBytes, + maxAttempts: 3, + retryBaseDelayMs: 1, + retryMaxDelayMs: 1, + fetchImpl, + nowFn: () => new Date('2026-07-13T10:20:30.000Z'), + sleepImpl: async (delayMs) => retryDelays.push(delayMs), + randomFn: () => 0, + }); + + assertEqual(result.uploadMode, 'multipart', '上传结果必须记录 multipart 模式。'); + assertEqual(result.partCount, 3, 'multipart 应按配置大小切成三段。'); + assertEqual(result.contentLength, payload.length, '上传结果应保留完整归档长度。'); + assertEqual(result.etag, 'complete-etag', '上传结果应保留 CompleteMultipartUpload ETag。'); + assertEqual(firstPartAttempts, 2, '503 后应仅重试失败的第一段。'); + assertEqual(retryDelays.length, 1, '一次可重试失败应触发一次退避。'); + + const initiateRequest = requests[0]; + assertTrue(initiateRequest.url.endsWith('?uploads'), 'InitiateMultipartUpload URL 必须使用裸 uploads 参数。'); + assertTrue(!initiateRequest.url.endsWith('?uploads='), 'InitiateMultipartUpload URL 不能把裸参数写成 uploads=。'); + const firstPartRequests = requests.filter(({method, url}) => method === 'PUT' && new URL(url).searchParams.get('partNumber') === '1'); + assertEqual(firstPartRequests.length, 2, '第一段应产生原请求和一次重试。'); + assertBufferEqual(firstPartRequests[0].body, payload.subarray(0, partSizeBytes), '第一段原请求内容必须完整。'); + assertBufferEqual(firstPartRequests[1].body, payload.subarray(0, partSizeBytes), '第一段重试必须重新创建并完整读取 stream。'); + assertTrue( + firstPartRequests[0].url.includes('?partNumber=1&uploadId=upload%2B%2F%3D%20id'), + 'UploadPart URL 必须使用排序并编码后的 canonical query。', + ); + + const completeRequest = requests.find(({method, url}) => method === 'POST' && new URL(url).searchParams.has('uploadId')); + assertIncludes(completeRequest?.body.toString('utf8') ?? '', '1"etag-1"', 'Complete XML 应包含第一段 ETag。'); + assertIncludes(completeRequest?.body.toString('utf8') ?? '', '3"etag-3"', 'Complete XML 应包含最后一段 ETag。'); + assertTrue(requests.some(({method}) => method === 'HEAD'), 'Complete 后必须执行签名 HEAD 验证。'); + for (const request of requests) { + assertTrue(String(request.headers.authorization ?? '').startsWith('OSS4-HMAC-SHA256 '), `${request.method} 请求必须携带 V4 Authorization。`); + } +} + +async function assertHeadLengthMismatchAbortsMultipartUpload() { + const root = path.join(tmpRoot, 'multipart-head-mismatch'); + const archivePath = path.join(root, 'backup.tar.gz'); + const partSizeBytes = 100 * 1024; + const payload = Buffer.alloc(partSizeBytes + 1, 'x'); + mkdirSync(root, {recursive: true}); + writeFileSync(archivePath, payload); + + const requests = []; + const fetchImpl = async (url, options) => { + await readRequestBody(options.body); + requests.push({url, method: options.method}); + const parsedUrl = new URL(url); + if (options.method === 'POST' && parsedUrl.search === '?uploads') { + return new Response('mismatch-upload', {status: 200}); + } + if (options.method === 'PUT') { + return new Response('', {status: 200, headers: {etag: `"etag-${parsedUrl.searchParams.get('partNumber')}"`}}); + } + if (options.method === 'POST') { + return new Response('', {status: 200, headers: {etag: '"complete-etag"'}}); + } + if (options.method === 'HEAD') { + return new Response(null, {status: 200, headers: {'content-length': String(payload.length - 1)}}); + } + if (options.method === 'DELETE') { + return new Response(null, {status: 204}); + } + throw new Error(`unexpected request: ${options.method} ${url}`); + }; + + let uploadError = null; + try { + await uploadArchive({ + archivePath, + bucket: 'genarrative-test', + endpoint: 'oss-cn-shanghai.aliyuncs.com', + objectKey: 'database-backups/test/mismatch.tar.gz', + accessKeyId: 'test-access-key', + accessKeySecret: 'test-access-secret', + partSizeBytes, + maxAttempts: 2, + retryBaseDelayMs: 1, + retryMaxDelayMs: 1, + fetchImpl, + nowFn: () => new Date('2026-07-13T10:20:30.000Z'), + sleepImpl: async () => {}, + randomFn: () => 0, + }); + } catch (error) { + uploadError = error; + } + + assertTrue(uploadError instanceof Error, 'HEAD 长度不一致时上传必须失败。'); + assertIncludes(uploadError?.message ?? '', 'HEAD 验证长度不一致', 'HEAD 长度不一致错误应保留本地和远端长度。'); + const abortRequest = requests.find(({method}) => method === 'DELETE'); + assertTrue(Boolean(abortRequest), 'HEAD 长度不一致后必须 best-effort AbortMultipartUpload。'); + assertTrue(abortRequest?.url.endsWith('?uploadId=mismatch-upload'), 'AbortMultipartUpload 必须携带同一 uploadId。'); +} + +async function assertMissingPartEtagAbortsMultipartUpload() { + const root = path.join(tmpRoot, 'multipart-missing-etag'); + const archivePath = path.join(root, 'backup.tar.gz'); + mkdirSync(root, {recursive: true}); + writeFileSync(archivePath, Buffer.alloc(100 * 1024, 'e')); + + const requests = []; + const fetchImpl = async (url, options) => { + await readRequestBody(options.body); + requests.push({url, method: options.method}); + const parsedUrl = new URL(url); + if (options.method === 'POST' && parsedUrl.search === '?uploads') { + return new Response('missing-etag-upload', {status: 200}); + } + if (options.method === 'PUT') { + return new Response('', {status: 200}); + } + if (options.method === 'DELETE') { + return new Response(null, {status: 204}); + } + throw new Error(`unexpected request: ${options.method} ${url}`); + }; + + let uploadError = null; + try { + await uploadArchive({ + archivePath, + bucket: 'genarrative-test', + endpoint: 'oss-cn-shanghai.aliyuncs.com', + objectKey: 'database-backups/test/missing-etag.tar.gz', + accessKeyId: 'test-access-key', + accessKeySecret: 'test-access-secret', + partSizeBytes: 100 * 1024, + maxAttempts: 1, + fetchImpl, + nowFn: () => new Date('2026-07-13T10:20:30.000Z'), + sleepImpl: async () => {}, + randomFn: () => 0, + }); + } catch (error) { + uploadError = error; + } + + assertIncludes(uploadError?.message ?? '', '响应缺少 ETag', 'UploadPart 缺少 ETag 时必须失败。'); + assertTrue( + requests.some(({method, url}) => method === 'DELETE' && url.endsWith('?uploadId=missing-etag-upload')), + 'UploadPart 缺少 ETag 后必须 AbortMultipartUpload。', + ); +} + +async function assertCompleteResponseAmbiguityUsesHeadVerification() { + const root = path.join(tmpRoot, 'multipart-complete-ambiguity'); + const archivePath = path.join(root, 'backup.tar.gz'); + const payload = Buffer.alloc(100 * 1024, 'c'); + mkdirSync(root, {recursive: true}); + writeFileSync(archivePath, payload); + + const requests = []; + let completeAttempts = 0; + const fetchImpl = async (url, options) => { + await readRequestBody(options.body); + requests.push({url, method: options.method}); + const parsedUrl = new URL(url); + if (options.method === 'POST' && parsedUrl.search === '?uploads') { + return new Response('ambiguous-upload', {status: 200}); + } + if (options.method === 'PUT') { + return new Response('', {status: 200, headers: {etag: '"part-etag"'}}); + } + if (options.method === 'POST' && parsedUrl.searchParams.has('uploadId')) { + completeAttempts += 1; + if (completeAttempts === 1) { + throw new TypeError('socket closed after remote complete'); + } + return new Response('NoSuchUpload', {status: 404}); + } + if (options.method === 'HEAD') { + return new Response(null, {status: 200, headers: {'content-length': String(payload.length)}}); + } + if (options.method === 'DELETE') { + return new Response(null, {status: 204}); + } + throw new Error(`unexpected request: ${options.method} ${url}`); + }; + + const result = await uploadArchive({ + archivePath, + bucket: 'genarrative-test', + endpoint: 'oss-cn-shanghai.aliyuncs.com', + objectKey: 'database-backups/test/complete-ambiguity.tar.gz', + accessKeyId: 'test-access-key', + accessKeySecret: 'test-access-secret', + partSizeBytes: 100 * 1024, + maxAttempts: 2, + retryBaseDelayMs: 1, + retryMaxDelayMs: 1, + fetchImpl, + nowFn: () => new Date('2026-07-13T10:20:30.000Z'), + sleepImpl: async () => {}, + randomFn: () => 0, + }); + + assertEqual(completeAttempts, 2, 'Complete 网络错误后应按策略重试。'); + assertEqual(result.contentLength, payload.length, 'Complete 结果不确定时应以 HEAD 长度验真收口。'); + assertTrue(requests.some(({method}) => method === 'HEAD'), 'Complete 结果不确定时必须执行 HEAD 验真。'); + assertTrue(!requests.some(({method}) => method === 'DELETE'), 'HEAD 已证实对象完整时不得 Abort 已完成上传。'); +} + +async function readRequestBody(body) { + if (body === undefined || body === null) { + return Buffer.alloc(0); + } + if (typeof body === 'string') { + return Buffer.from(body); + } + if (Buffer.isBuffer(body)) { + return body; + } + const chunks = []; + for await (const chunk of body) { + chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); + } + return Buffer.concat(chunks); +} + function createFixture(name) { const root = path.join(tmpRoot, name); const binDir = path.join(root, 'bin'); @@ -160,6 +484,30 @@ function assertIncludes(content, expected, reason) { } } +function assertEqual(actual, expected, reason) { + if (actual !== expected) { + failures.push(`${reason} 预期: ${String(expected)},实际: ${String(actual)}`); + } +} + +function assertNotEqual(actual, expected, reason) { + if (actual === expected) { + failures.push(`${reason} 两者均为: ${String(actual)}`); + } +} + +function assertTrue(condition, reason) { + if (!condition) { + failures.push(reason); + } +} + +function assertBufferEqual(actual, expected, reason) { + if (!Buffer.isBuffer(actual) || !actual.equals(expected)) { + failures.push(`${reason} 预期 ${expected.length} bytes,实际 ${actual?.length ?? ''} bytes。`); + } +} + function assertFileMissing(filePath, reason) { if (existsSync(filePath)) { failures.push(`${reason} 实际存在: ${filePath}\n${readFile(filePath)}`); diff --git a/scripts/check-nginx-spa-routes.mjs b/scripts/check-nginx-spa-routes.mjs new file mode 100644 index 000000000..3ab5682c0 --- /dev/null +++ b/scripts/check-nginx-spa-routes.mjs @@ -0,0 +1,228 @@ +#!/usr/bin/env node + +import { readFileSync } from 'node:fs'; + +const APP_PAGE_ROUTES_PATH = 'src/routing/appPageRoutes.ts'; +const APP_ROUTES_PATH = 'src/routing/appRoutes.tsx'; +const COMPATIBILITY_ROUTES = ['/creation/rpg/agent']; +const NGINX_PATHS = [ + 'deploy/nginx/genarrative.conf', + 'deploy/nginx/genarrative-dev-http.conf', + 'deploy/container/nginx.conf', +]; +const MAINTENANCE_NGINX_PATHS = [ + 'deploy/nginx/genarrative.conf', + 'deploy/nginx/genarrative-dev-http.conf', +]; +const MAINTENANCE_SNIPPET_PATH = + 'deploy/nginx/snippets/genarrative-maintenance.conf'; +const SPA_BLOCK_START = '# BEGIN GENARRATIVE MAIN SPA ROUTES'; +const SPA_BLOCK_END = '# END GENARRATIVE MAIN SPA ROUTES'; +const UNKNOWN_ROUTE_SAMPLES = [ + '/unknown-root', + '/creation/not-exist', + '/runtime/not-exist', + '/puzzle/not-exist', +]; + +const failures = []; + +function fail(message) { + failures.push(message); +} + +function extractSourceBlock(source, pattern, label) { + const match = source.match(pattern); + if (!match) { + fail(`${label} 未找到。`); + return ''; + } + return match[1]; +} + +function collectExpectedMainSpaRoutes() { + const appPageRoutes = readFileSync(APP_PAGE_ROUTES_PATH, 'utf8'); + const appRoutes = readFileSync(APP_ROUTES_PATH, 'utf8'); + const stageEntries = extractSourceBlock( + appPageRoutes, + /const STAGE_ROUTE_ENTRIES = \[([\s\S]*?)\] as const/u, + `${APP_PAGE_ROUTES_PATH} STAGE_ROUTE_ENTRIES`, + ); + const runtimeEntries = extractSourceBlock( + appPageRoutes, + /export const APP_RUNTIME_ROUTES[^=]*= \{([\s\S]*?)\n\};/u, + `${APP_PAGE_ROUTES_PATH} APP_RUNTIME_ROUTES`, + ); + + const routes = [ + ...Array.from( + stageEntries.matchAll(/\[\s*'[^']+'\s*,\s*'([^']+)'\s*\]/gu), + (match) => match[1], + ), + ...Array.from( + runtimeEntries.matchAll(/'[^']+'\s*:\s*'([^']+)'/gu), + (match) => match[1], + ), + ...Array.from( + appRoutes.matchAll(/normalizedPath === '([^']+)'/gu), + (match) => match[1], + ), + ...COMPATIBILITY_ROUTES, + ]; + + const uniqueRoutes = [...new Set(routes)].sort(); + if (uniqueRoutes.length === 0) { + fail('未从前端路由源提取到主站 SPA 路由。'); + } + for (const route of uniqueRoutes) { + if (!/^\/(?:[a-z0-9-]+(?:\/[a-z0-9-]+)*)?$/u.test(route)) { + fail(`前端路由源包含门禁暂不支持的路径格式: ${route}`); + } + } + return uniqueRoutes; +} + +function compareRouteSets(actualRoutes, expectedRoutes, label) { + const actual = new Set(actualRoutes); + const expected = new Set(expectedRoutes); + const missing = expectedRoutes.filter((route) => !actual.has(route)); + const extra = actualRoutes.filter((route) => !expected.has(route)); + if (missing.length > 0) { + fail(`${label} 缺少 SPA 路由: ${missing.join(', ')}`); + } + if (extra.length > 0) { + fail(`${label} 包含非当前路由: ${extra.join(', ')}`); + } +} + +function validateNginxRoutes(nginxPath, expectedRoutes) { + const source = readFileSync(nginxPath, 'utf8'); + const blockStart = source.indexOf(SPA_BLOCK_START); + const blockEnd = source.indexOf(SPA_BLOCK_END); + if (blockStart < 0 || blockEnd <= blockStart) { + fail(`${nginxPath} 缺少完整 SPA allowlist 标记。`); + return; + } + + const block = source.slice(blockStart, blockEnd + SPA_BLOCK_END.length); + if (!/location\s+=\s+\/\s*\{/u.test(block)) { + fail(`${nginxPath} SPA allowlist 缺少根路径精确 location。`); + } + if (!block.includes('try_files /index.html =404;')) { + fail(`${nginxPath} 根路径没有精确回退 index.html。`); + } + if (!block.includes('try_files $uri /index.html =404;')) { + fail(`${nginxPath} SPA allowlist 没有精确回退 index.html。`); + } + + const regexMatch = block.match(/location\s+~\*\s+"([^"]+)"\s*\{/u); + if (!regexMatch) { + fail(`${nginxPath} 缺少大小写不敏感的 SPA allowlist regex location。`); + return; + } + + const nginxPattern = regexMatch[1]; + const alternativesMatch = nginxPattern.match(/^\^\/\(\?:(.+)\)\/\?\$$/u); + if (!alternativesMatch) { + fail(`${nginxPath} SPA allowlist 必须锚定完整路径并允许一个尾部斜杠。`); + return; + } + + const configuredRoutes = [ + '/', + ...alternativesMatch[1].split('|').map((route) => `/${route}`), + ].sort(); + compareRouteSets(configuredRoutes, expectedRoutes, nginxPath); + + const matcher = new RegExp(nginxPattern, 'iu'); + for (const route of expectedRoutes.filter((candidate) => candidate !== '/')) { + if (!matcher.test(route)) { + fail(`${nginxPath} SPA allowlist 未匹配完整路径: ${route}`); + } + if (!matcher.test(`${route.toUpperCase()}/`)) { + fail(`${nginxPath} SPA allowlist 未允许大小写差异和尾部斜杠: ${route}`); + } + } + for (const route of UNKNOWN_ROUTE_SAMPLES) { + if (matcher.test(route) || matcher.test(`${route}/`)) { + fail(`${nginxPath} SPA allowlist 错误接收未知路径: ${route}`); + } + } + + const defaultLocation = source.slice(blockEnd + SPA_BLOCK_END.length); + if (!defaultLocation.includes('try_files $uri $uri/ =404;')) { + fail( + `${nginxPath} 未命中 SPA allowlist 的路径必须只读真实静态文件并返回 404。`, + ); + } + if (defaultLocation.includes('try_files $uri $uri/ /index.html;')) { + fail(`${nginxPath} 默认 location 仍存在全路径 SPA fallback。`); + } +} + +function validateMaintenanceInternalBypass() { + const snippet = readFileSync(MAINTENANCE_SNIPPET_PATH, 'utf8'); + const internalBypassPattern = + /set \$genarrative_maintenance 0;\s*if \(-f \/var\/lib\/genarrative\/maintenance\/enabled\) \{\s*set \$genarrative_maintenance 1;\s*\}\s*if \(\$genarrative_internal_client\) \{\s*set \$genarrative_maintenance 0;\s*\}/u; + if (!internalBypassPattern.test(snippet)) { + fail( + `${MAINTENANCE_SNIPPET_PATH} 必须在读取维护 marker 后为真实内网来源清除全站维护状态。`, + ); + } + if (snippet.includes('$genarrative_admin_maintenance')) { + fail(`${MAINTENANCE_SNIPPET_PATH} 不应保留仅后台使用的维护变量。`); + } + + for (const nginxPath of MAINTENANCE_NGINX_PATHS) { + const source = readFileSync(nginxPath, 'utf8'); + for (const fragment of [ + 'geo $remote_addr $genarrative_internal_client {', + '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;', + ]) { + if (!source.includes(fragment)) { + fail(`${nginxPath} 缺少内网来源识别片段: ${fragment}`); + } + } + if (source.includes('$genarrative_admin_maintenance')) { + fail(`${nginxPath} 的维护入口必须统一使用全站维护变量。`); + } + const maintenanceChecks = + source.match(/if \(\$genarrative_[a-z_]*maintenance\)/gu) ?? []; + if (maintenanceChecks.length === 0) { + fail(`${nginxPath} 缺少维护状态判断。`); + } + for (const maintenanceCheck of maintenanceChecks) { + if (maintenanceCheck !== 'if ($genarrative_maintenance)') { + fail( + `${nginxPath} 存在未统一到全站维护变量的判断: ${maintenanceCheck}`, + ); + } + } + } +} + +export const expectedMainSpaRoutes = collectExpectedMainSpaRoutes(); + +for (const nginxPath of NGINX_PATHS) { + validateNginxRoutes(nginxPath, expectedMainSpaRoutes); +} +validateMaintenanceInternalBypass(); + +if (failures.length > 0) { + console.error('[check:nginx-spa-routes] FAILED'); + for (const failure of failures) { + console.error(`- ${failure}`); + } + process.exit(1); +} + +console.log( + `[check:nginx-spa-routes] OK (${expectedMainSpaRoutes.length} SPA routes, ${NGINX_PATHS.length} Nginx templates)`, +); diff --git a/scripts/check-pingora-gateway-smoke.mjs b/scripts/check-pingora-gateway-smoke.mjs index ef2aac8c1..96bf12f53 100644 --- a/scripts/check-pingora-gateway-smoke.mjs +++ b/scripts/check-pingora-gateway-smoke.mjs @@ -568,15 +568,36 @@ async function runSmokeCases( ); await expectHttp( baseUrl, - '/some/deep/link', + '/creation/puzzle/result', 200, 'site-shell', - '主站深链回退 index.html', + '主站 allowlist 深链回退 index.html', { validate: (response) => response.headers['cache-control'] === 'no-cache', }, ); + await expectHttp( + baseUrl, + '/CREATION/PUZZLE/RESULT/', + 200, + 'site-shell', + '主站 allowlist 允许大小写差异和尾部斜杠', + ); + for (const unknownPath of [ + '/some/deep/link', + '/creation/not-exist', + '/runtime/not-exist', + '/puzzle/not-exist', + ]) { + await expectHttp( + baseUrl, + unknownPath, + 404, + '', + `主站未知路径返回真实 404: ${unknownPath}`, + ); + } await expectHttp(baseUrl, '/admin', 301, '', '/admin 301 到 /admin/', { validate: (response) => response.headers.location === '/admin/', }); @@ -1104,6 +1125,11 @@ async function runSmokeCases( await mkdir(path.dirname(maintenanceFile), { recursive: true }); await writeFile(maintenanceFile, 'enabled'); + const publicClientHeaders = { + 'X-Real-IP': '203.0.113.50', + 'X-Forwarded-For': '192.168.35.50', + }; + const internalClientHeaders = { 'X-Real-IP': '192.168.35.50' }; const giteaRequestsBeforeMaintenance = gitea.state.requests.length; await expectHttp( baseUrl, @@ -1114,6 +1140,7 @@ async function runSmokeCases( { headers: { Host: 'git.genarrative.world', + ...publicClientHeaders, }, }, ); @@ -1121,14 +1148,73 @@ async function runSmokeCases( gitea.state.requests.length === giteaRequestsBeforeMaintenance + 1, '维护模式 Gitea Host 请求没有打到 Gitea mock', ); + for (const [path, bodyNeedle, label] of [ + ['/', 'maintenance', '公网主站页面'], + ['/api/creation-entry/config', 'MAINTENANCE', '公网普通 API'], + ['/v1/identity', 'maintenance', '公网 SpacetimeDB 路由'], + ['/admin/settings', 'maintenance', '公网后台页面'], + ['/admin/assets/admin.js', 'maintenance', '公网后台静态资源'], + ['/admin/api/users', 'MAINTENANCE', '公网后台 API'], + ]) { + await expectHttp( + baseUrl, + path, + 503, + bodyNeedle, + `维护模式继续拦截${label}`, + { + headers: publicClientHeaders, + }, + ); + } + await expectHttp( + baseUrl, + '/', + 200, + 'site-shell', + '维护模式允许内网主站页面', + { headers: internalClientHeaders }, + ); await expectHttp( baseUrl, '/api/creation-entry/config', - 503, - 'MAINTENANCE', - '维护模式 API JSON', + 200, + '"upstream":"api"', + '维护模式允许内网普通 API', + { headers: internalClientHeaders }, + ); + await expectHttp( + baseUrl, + '/v1/identity', + 200, + '"upstream":"spacetime"', + '维护模式允许内网 SpacetimeDB 路由', + { headers: internalClientHeaders }, + ); + await expectHttp( + baseUrl, + '/admin/settings', + 200, + 'admin-shell', + '维护模式允许内网后台页面', + { headers: internalClientHeaders }, + ); + await expectHttp( + baseUrl, + '/admin/assets/admin.js', + 200, + 'admin asset', + '维护模式允许内网后台静态资源', + { headers: internalClientHeaders }, + ); + await expectHttp( + baseUrl, + '/admin/api/users', + 200, + '"upstream":"api"', + '维护模式允许内网后台 API', + { headers: internalClientHeaders }, ); - await expectHttp(baseUrl, '/', 503, 'maintenance', '维护模式 Web 页面'); await expectAccessLogContains(accessLogFile, [ 'status=503', 'path=/api/creation-entry/config', diff --git a/scripts/check-pingora-route-parity.mjs b/scripts/check-pingora-route-parity.mjs index 78372e43b..cf56ea3dd 100644 --- a/scripts/check-pingora-route-parity.mjs +++ b/scripts/check-pingora-route-parity.mjs @@ -2,6 +2,8 @@ import { readFileSync } from 'node:fs'; +import { expectedMainSpaRoutes } from './check-nginx-spa-routes.mjs'; + const MATRIX_PATH = 'deploy/pingora/nginx-route-parity.matrix.json'; const PRODUCTION_NGINX_PATH = 'deploy/nginx/genarrative.conf'; const DEVELOPMENT_NGINX_PATH = 'deploy/nginx/genarrative-dev-http.conf'; @@ -46,6 +48,11 @@ const REQUIRED_ROUTE_IDS = [ 'readyz_forbidden', 'generated_assets_forbidden', 'web_spa_fallback', + 'web_spa_case_trailing_slash', + 'web_unknown_path_exact', + 'creation_unknown_path_exact', + 'runtime_unknown_path_exact', + 'puzzle_unknown_path_exact', ]; const files = { @@ -221,6 +228,9 @@ function validateRustTestUsesMatrix() { 'serde_json::from_str(ROUTE_PARITY_MATRIX_JSON)', 'protection_class_for_route(&route, &case.sample_path)', 'fn matches_nginx_route_parity_matrix()', + 'fn is_main_spa_path(path: &str)', + "path.strip_suffix('/')", + 'normalized.eq_ignore_ascii_case(candidate)', ]) { if (!pingoraGatewaySource.includes(fragment)) { fail(`Pingora Rust 路由 parity 测试缺少矩阵接入片段: ${fragment}`); @@ -228,8 +238,34 @@ function validateRustTestUsesMatrix() { } } +function validateRustMainSpaRoutes() { + const routeBlock = pingoraGatewaySource.match( + /const MAIN_SPA_PATHS: &\[&str\] = &\[([\s\S]*?)\n\];/u, + ); + if (!routeBlock) { + fail('Pingora Rust 缺少 MAIN_SPA_PATHS allowlist。'); + return; + } + + const rustRoutes = Array.from( + routeBlock[1].matchAll(/"([^"]+)"/gu), + (match) => match[1], + ).sort(); + const expected = new Set(expectedMainSpaRoutes); + const actual = new Set(rustRoutes); + const missing = expectedMainSpaRoutes.filter((route) => !actual.has(route)); + const extra = rustRoutes.filter((route) => !expected.has(route)); + if (missing.length > 0) { + fail(`Pingora MAIN_SPA_PATHS 缺少当前前端路由: ${missing.join(', ')}`); + } + if (extra.length > 0) { + fail(`Pingora MAIN_SPA_PATHS 包含非当前前端路由: ${extra.join(', ')}`); + } +} + validateMatrixShape(); validateRustTestUsesMatrix(); +validateRustMainSpaRoutes(); if (failures.length > 0) { console.error('[check:pingora-route-parity] FAILED'); diff --git a/scripts/check-production-api-deploy.mjs b/scripts/check-production-api-deploy.mjs index f46e6d961..1460189ba 100644 --- a/scripts/check-production-api-deploy.mjs +++ b/scripts/check-production-api-deploy.mjs @@ -36,6 +36,7 @@ console.log('[check:production-api-deploy] OK'); function main() { assertDeployCopiesPingoraDirectReleaseDependencies(); + assertSuccessfulDeployCanKeepMaintenance(); assertDeployRestartsActivePingoraWhenArtifactIncluded(); assertDeployStartsInactivePingoraWhenArtifactIncluded(); assertDeployRejectsPingoraDirectEntryWhenArtifactIncluded(); @@ -79,6 +80,22 @@ function main() { assertMissingPingoraCanaryAccessLogParityFails(); } +function assertSuccessfulDeployCanKeepMaintenance() { + const fixture = prepareFixture('keep-maintenance-after-success'); + const result = runDeploy(fixture, { keepMaintenance: true }); + + assertStatus(result, 0, '显式保持维护时完整 fixture 应部署成功。'); + if (result.status !== 0) { + return; + } + assertMaintenanceKept(fixture, '显式要求成功部署后保持维护'); + assertIncludes( + result.stdout, + '按参数保持维护模式', + '成功部署并保持维护时必须输出明确状态。', + ); +} + function readOptionalCommandsLog(fixture) { if (!existsSync(fixture.commandsLog)) { return ''; @@ -120,6 +137,18 @@ function assertDeployCopiesPingoraDirectReleaseDependencies() { ); } + assertFileExists( + path.join(releaseDir, 'scripts/deploy/production-api-deploy.sh'), + 'current release 必须包含 API 部署脚本。', + ); + assertFileExists( + path.join(releaseDir, 'scripts/deploy/maintenance-on.sh'), + 'current release 必须包含进入维护脚本。', + ); + assertFileExists( + path.join(releaseDir, 'scripts/deploy/maintenance-off.sh'), + 'current release 必须包含退出维护脚本。', + ); assertFileExists( path.join(releaseDir, 'scripts/deploy/pingora-direct-enable.sh'), 'current release 必须包含 Pingora 直连启用脚本。', @@ -316,8 +345,8 @@ function assertDeployCopiesPingoraDirectReleaseDependencies() { ); assertIncludes( commandsLog, - 'curl -fsS http://127.0.0.1:18082/readyz', - '部署脚本必须执行 readiness curl。', + 'curl -fsS --max-time 2 http://127.0.0.1:18082/readyz', + '部署脚本必须为 readiness curl 设置单次超时,避免端口已建立但服务未响应时无限等待。', ); if (existsSync(fixture.maintenanceFile)) { @@ -1666,6 +1695,9 @@ function runDeploy(fixture, options = {}) { if (options.requirePingoraGateway) { args.push('--require-pingora-gateway'); } + if (options.keepMaintenance) { + args.push('--keep-maintenance-mode'); + } return spawnSync( 'bash', args, diff --git a/scripts/check-production-ops-guardrails.mjs b/scripts/check-production-ops-guardrails.mjs index a06678401..4352e947c 100644 --- a/scripts/check-production-ops-guardrails.mjs +++ b/scripts/check-production-ops-guardrails.mjs @@ -135,12 +135,55 @@ const checks = [ reason: 'Stdb 先于 API 发布时必须先补齐 api-server env 的 FILE 路径,保证首次 rollout 重启即可读取 secret。', }, + { + file: 'scripts/deploy/production-stdb-publish.sh', + includes: 'stop_runtime_services_for_rollout_gate', + reason: + 'Stdb 与 API 之间需要人工维护时,必须停止旧 API/controller/worker,不能只保留网关维护文件。', + }, + { + file: 'scripts/deploy/production-stdb-publish.sh', + includes: '按参数保持维护模式和旧运行时服务停止状态', + reason: '受控维护发布成功后不得自动重启旧运行时或退出维护模式。', + }, { file: 'scripts/deploy/production-api-deploy.sh', includes: 'ensure_runtime_bootstrap_secret_file_env', reason: '生产 API/worker env 必须统一指向 Stdb publish 写入的受保护 bootstrap secret 文件。', }, + { + file: 'scripts/deploy/production-api-deploy.sh', + includes: '继承已有维护模式;部署失败时不得误退出上游维护窗口', + reason: 'API deploy 必须区分自己打开的维护模式与 Stdb gate 继承的维护模式。', + }, + { + file: 'scripts/deploy/production-api-deploy.sh', + includes: '--keep-maintenance-mode)', + reason: 'API deploy 必须允许成功发布后按显式参数保留维护模式。', + }, + { + file: 'scripts/deploy/production-api-deploy.sh', + includes: 'readiness 通过,按参数保持维护模式', + reason: 'API deploy 保留维护模式时必须在 readiness 通过后给出明确状态。', + }, + { + file: 'scripts/deploy/production-api-deploy.sh', + includes: 'curl -fsS --max-time 2 "${HEALTH_URL}"', + reason: + 'API readiness 单次请求必须有超时,避免端口已建立但服务尚未响应时绕过重试上限无限挂起。', + }, + { + file: 'jenkins/Jenkinsfile.production-api-deploy', + includes: + "booleanParam(name: 'KEEP_MAINTENANCE_MODE', defaultValue: false", + reason: 'API Deploy Job 必须向独立发布和 Full 编排暴露成功后保持维护的参数。', + }, + { + file: 'jenkins/Jenkinsfile.production-api-deploy', + includes: 'maintenance_deploy_args+=(--keep-maintenance-mode)', + reason: 'API Deploy Job 必须把保持维护参数传给发布产物内的部署脚本。', + }, { file: 'scripts/jenkins-server-provision.sh', includes: 'ensure_runtime_bootstrap_secret_file_env', @@ -186,6 +229,12 @@ const checks = [ includes: 'GENARRATIVE_SPACETIME_MIGRATION_BOOTSTRAP_SECRET_SHA256', reason: 'Stdb Build 只能把 File credential 的 SHA-256 摘要传给 Rust 编译。', }, + { + file: 'jenkins/Jenkinsfile.production-stdb-module-build', + includes: + "string(name: 'MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID', defaultValue: 'genarrative-spacetime-bootstrap-secret-dev-file'", + reason: 'Stdb Build 的 Secret File credential ID 必须由仓库 Jenkinsfile 固定默认值。', + }, { file: 'jenkins/Jenkinsfile.production-stdb-module-build', excludes: 'export GENARRATIVE_SPACETIME_MIGRATION_BOOTSTRAP_SECRET=', @@ -211,6 +260,26 @@ const checks = [ includes: 'Secret File 与构建 WASM 的 bootstrap secret 摘要不一致', reason: '生产 Stdb publish 必须阻断 Secret File 与构建 WASM 摘要不一致。', }, + { + file: 'scripts/deploy/production-stdb-publish.sh', + includes: '--delete-data=never', + reason: '生产 Stdb publish 必须显式禁止 schema 冲突时删除数据。', + }, + { + file: 'scripts/deploy/production-stdb-publish.sh', + includes: '--yes=migrate,break-clients', + reason: '生产 Stdb publish 只能跳过迁移与客户端断开确认,不能使用等价 delete-data 的裸 --yes。', + }, + { + file: 'scripts/deploy/production-stdb-publish.sh', + excludes: '--clear-database', + reason: '生产 Stdb publish 普通入口不得保留清库参数。', + }, + { + file: 'jenkins/Jenkinsfile.production-stdb-module-publish', + excludes: 'CLEAR_DATABASE', + reason: 'Stdb 发布流水线不得向普通构建参数暴露清库开关。', + }, { file: 'scripts/deploy/production-stdb-publish.sh', includes: '[[ ! "${MIGRATION_BOOTSTRAP_SECRET}" =~ ^[0-9a-fA-F]{64}$ ]]', @@ -273,6 +342,12 @@ const checks = [ includes: '--worker-env-file "${params.WORKER_ENV_FILE}"', reason: 'Stdb Publish 必须把 worker env 路径传给随包发布脚本。', }, + { + file: 'jenkins/Jenkinsfile.production-stdb-module-publish', + includes: + "string(name: 'MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID', defaultValue: 'genarrative-spacetime-bootstrap-secret-dev-file'", + reason: 'Stdb Publish 的 Secret File credential ID 必须由仓库 Jenkinsfile 固定默认值。', + }, { file: 'jenkins/Jenkinsfile.production-full-build-and-deploy', includes: "string(name: 'WORKER_ENV_FILE', value: params.WORKER_ENV_FILE", @@ -284,12 +359,86 @@ const checks = [ "error('MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID 必须引用受保护的 Jenkins Secret File 凭据。')", reason: '全量构建必须在启动并行子流水线前拒绝缺失的 Secret File 凭据。', }, + { + file: 'jenkins/Jenkinsfile.production-full-build-and-deploy', + includes: + "string(name: 'MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID', defaultValue: 'genarrative-spacetime-bootstrap-secret-dev-file'", + reason: 'Full Build 的 Secret File credential ID 必须由仓库 Jenkinsfile 固定默认值。', + }, { file: 'jenkins/Jenkinsfile.production-full-build-and-deploy', includes: "string(name: 'MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID', value: params.MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID)", reason: '全量发布必须把与 wasm 构建一致的 Secret File 凭据透传给 Stdb Publish。', }, + { + file: 'jenkins/Jenkinsfile.production-full-build-and-deploy', + includes: + "choice(name: 'STDB_API_ROLLOUT_MODE', choices: ['normal', 'pause-after-stdb']", + reason: 'Full Build 的 04:00 定时任务必须默认 normal 完整发布仅供开发使用的 dev 服务器。', + }, + { + file: 'jenkins/Jenkinsfile.production-full-build-and-deploy', + includes: + "booleanParam(name: 'KEEP_MAINTENANCE_MODE', value: true)", + reason: 'Full Build 必须让 Stdb 与 API Deploy 全程保持维护,直到 Web Deploy 结束。', + }, + { + file: 'jenkins/Jenkinsfile.production-full-build-and-deploy', + includes: + "booleanParam(name: 'EXIT_MAINTENANCE_MODE_AFTER_COMPLETION', defaultValue: true", + reason: 'Full Build 必须显式提供完整发布成功后是否退出维护模式的选项。', + }, + { + file: 'jenkins/Jenkinsfile.production-full-build-and-deploy', + includes: + '确认 API runtime identity 未被授权为 migration operator 后再继续部署 API', + reason: + 'Stdb/API 人工 gate 必须提醒审批人保持 migration operator 与运行时服务 identity 互斥。', + }, + { + file: 'jenkins/Jenkinsfile.production-full-build-and-deploy', + includes: + "stage('Exit Maintenance')", + reason: 'Full Build 必须在 Web Deploy 之后用独立阶段决定是否退出维护。', + }, + { + file: 'jenkins/Jenkinsfile.production-full-build-and-deploy', + includes: + 'expression { return params.EXIT_MAINTENANCE_MODE_AFTER_COMPLETION != false }', + reason: 'Full Build 只有在显式允许时才执行最终维护退出阶段。', + }, + { + file: 'jenkins/Jenkinsfile.production-full-build-and-deploy', + includes: + 'maintenance_script="/opt/genarrative/current/scripts/deploy/maintenance-off.sh"', + reason: 'Full Build 必须使用本次 current release 随包维护脚本退出维护。', + }, + { + file: 'jenkins/Jenkinsfile.production-full-build-and-deploy', + includes: "timeout(time: 4, unit: 'HOURS')", + reason: 'Stdb/API 人工门禁必须有超时,不能永久占用 disableConcurrentBuilds 锁。', + }, + { + file: 'jenkins/Jenkinsfile.production-full-build-and-deploy', + includes: "submitter: params.STDB_API_ROLLOUT_APPROVERS.trim()", + reason: 'Stdb/API 人工门禁必须限制指定 Jenkins 用户或组放行。', + }, + { + file: 'scripts/spacetime-maintain-external-generation-jobs.mjs', + includes: '!options.apply && (result.has_more || pendingApplyCount > 0)', + reason: '历史维护最后一批即使 has_more=false,只要 dry-run 仍命中数据也必须提示同 cursor apply。', + }, + { + file: 'scripts/spacetime-maintain-external-generation-jobs.mjs', + includes: 'cursor_job_id: encodeSpacetimeCliOption(options.cursorJobId || null)', + reason: '历史维护续批游标必须编码为 CLI SATS Option,不能把非空字符串直接传给 procedure。', + }, + { + file: 'scripts/spacetime-maintain-external-generation-jobs.mjs', + includes: 'completed_before_micros: encodeSpacetimeCliOption(', + reason: '历史 payload 截止时间必须编码为 CLI SATS Option,确保事故时间过滤可调用。', + }, { file: 'scripts/deploy-rust-remote.sh', excludes: @@ -6674,6 +6823,30 @@ for (const check of checks) { } } +const fullPipelineContent = readFileSync( + 'jenkins/Jenkinsfile.production-full-build-and-deploy', + 'utf8', +); +const forcedBuildOnlyCalls = fullPipelineContent.match( + /booleanParam\(name: 'PUBLISH_AFTER_BUILD', value: false\)/gu, +); +if ((forcedBuildOnlyCalls?.length ?? 0) !== 3) { + failed = true; + console.error( + '[check:production-ops] Full Build 必须向 Web、API、Stdb 三个 Build Job 显式传 PUBLISH_AFTER_BUILD=false。', + ); +} + +const fullPipelineMaintenanceHoldCalls = fullPipelineContent.match( + /booleanParam\(name: 'KEEP_MAINTENANCE_MODE', value: true\)/gu, +); +if ((fullPipelineMaintenanceHoldCalls?.length ?? 0) !== 2) { + failed = true; + console.error( + '[check:production-ops] Full Build 必须让 Stdb Publish 与 API Deploy 两个下游阶段都保持维护模式。', + ); +} + for (const file of nodeEnvFileCommandFiles) { const content = readFileSync(file, 'utf8'); const commandText = content.replace(/\\\r?\n\s*/g, ' '); diff --git a/scripts/check-server-provision-tools.sh b/scripts/check-server-provision-tools.sh index ec6431338..7d4b9caa8 100755 --- a/scripts/check-server-provision-tools.sh +++ b/scripts/check-server-provision-tools.sh @@ -37,11 +37,11 @@ chmod +x "${TARGET_BIN_DIR}/otelcol-contrib" cat >"${SPACETIME_ROOT_DIR}/bin/current/spacetimedb-cli" <<'EOF' #!/usr/bin/env bash -echo "spacetimedb-cli 2.5.0" +echo "spacetimedb-cli 2.6.0" EOF cat >"${SPACETIME_ROOT_DIR}/bin/current/spacetimedb-standalone" <<'EOF' #!/usr/bin/env bash -echo "spacetimedb-standalone 2.5.0" +echo "spacetimedb-standalone 2.6.0" EOF chmod +x \ "${SPACETIME_ROOT_DIR}/bin/current/spacetimedb-cli" \ @@ -58,7 +58,7 @@ if ! ( OTELCOL_TARGET_BIN="${TARGET_BIN_DIR}/otelcol-contrib" \ OTELCOL_VERSION="0.151.0" \ SPACETIME_ROOT="${SPACETIME_ROOT_DIR}" \ - SPACETIME_EXPECTED_VERSION="2.5.0" \ + SPACETIME_EXPECTED_VERSION="2.6.0" \ "${REPO_ROOT}/scripts/prepare-server-provision-tools.sh" \ >"${OUTPUT_LOG}" 2>&1 ); then diff --git a/scripts/container-worker-smoke.mjs b/scripts/container-worker-smoke.mjs index 261e58288..c7c2c9e5c 100644 --- a/scripts/container-worker-smoke.mjs +++ b/scripts/container-worker-smoke.mjs @@ -761,7 +761,7 @@ function composeEnv() { } function localSpacetimeImageName() { - return `${projectName}-spacetimedb:2.5.0`; + return `${projectName}-spacetimedb:2.6.0`; } function spacetimeServerUrl(state) { diff --git a/scripts/database-backup-to-oss.mjs b/scripts/database-backup-to-oss.mjs index 4d4e969a2..d38a28598 100644 --- a/scripts/database-backup-to-oss.mjs +++ b/scripts/database-backup-to-oss.mjs @@ -1,8 +1,9 @@ #!/usr/bin/env node import {spawnSync} from 'node:child_process'; import {createHash, createHmac} from 'node:crypto'; -import {createReadStream, existsSync, mkdirSync, readFileSync, rmSync, statSync, statfsSync, writeFileSync} from 'node:fs'; +import {createReadStream, existsSync, mkdirSync, readFileSync, realpathSync, rmSync, statSync, statfsSync, writeFileSync} from 'node:fs'; import {basename, dirname, isAbsolute, resolve} from 'node:path'; +import {setTimeout as sleep} from 'node:timers/promises'; import {fileURLToPath} from 'node:url'; const __filename = fileURLToPath(import.meta.url); @@ -18,6 +19,14 @@ const OSS_ALGORITHM = 'OSS4-HMAC-SHA256'; const OSS_SERVICE = 'oss'; const OSS_REQUEST = 'aliyun_v4_request'; const UNSIGNED_PAYLOAD = 'UNSIGNED-PAYLOAD'; +const DEFAULT_OSS_MULTIPART_PART_SIZE_BYTES = 128 * 1024 * 1024; +const OSS_MIN_MULTIPART_PART_SIZE_BYTES = 100 * 1024; +const OSS_MAX_MULTIPART_PART_SIZE_BYTES = 5 * 1024 * 1024 * 1024; +const OSS_MAX_MULTIPART_PARTS = 10_000; +const DEFAULT_OSS_REQUEST_MAX_ATTEMPTS = 5; +const DEFAULT_OSS_RETRY_BASE_DELAY_MS = 1_000; +const DEFAULT_OSS_RETRY_MAX_DELAY_MS = 30_000; +const RETRYABLE_OSS_HTTP_STATUSES = new Set([408, 429, 500, 502, 503, 504]); function usage() { console.log(`用法: @@ -484,11 +493,33 @@ function encodePath(path) { .join('/'); } +function encodeQueryComponent(value) { + return encodeURIComponent(String(value)).replace( + /[!'()*]/gu, + (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`, + ); +} + +export function buildCanonicalQuery(queries = {}) { + return Object.entries(queries) + .map(([key, value]) => [encodeQueryComponent(key), value === null ? null : encodeQueryComponent(value)]) + .sort(([leftKey, leftValue], [rightKey, rightValue]) => { + if (leftKey !== rightKey) { + return leftKey < rightKey ? -1 : 1; + } + const left = leftValue ?? ''; + const right = rightValue ?? ''; + return left === right ? 0 : left < right ? -1 : 1; + }) + .map(([key, value]) => value === null ? key : `${key}=${value}`) + .join('&'); +} + function canonicalHeaderValue(value) { return String(value).trim().replace(/\s+/gu, ' '); } -function buildAuthorization({method, bucket, endpoint, objectKey, accessKeyId, accessKeySecret, headers, date}) { +export function buildAuthorization({method, bucket, endpoint, objectKey, accessKeyId, accessKeySecret, headers, date, queries = {}}) { const region = regionFromEndpoint(endpoint); const scopeDate = formatScopeDate(date); const scope = `${scopeDate}/${region}/${OSS_SERVICE}/${OSS_REQUEST}`; @@ -504,7 +535,7 @@ function buildAuthorization({method, bucket, endpoint, objectKey, accessKeyId, a const canonicalRequest = [ method, canonicalUri, - '', + buildCanonicalQuery(queries), canonicalHeaders, additionalHeaders, UNSIGNED_PAYLOAD, @@ -518,56 +549,304 @@ function buildAuthorization({method, bucket, endpoint, objectKey, accessKeyId, a return `${OSS_ALGORITHM} Credential=${accessKeyId}/${scope},AdditionalHeaders=${additionalHeaders},Signature=${finalSignature}`; } -async function uploadArchive({archivePath, bucket, endpoint, objectKey, accessKeyId, accessKeySecret}) { +function buildOssUrl({bucket, endpoint, objectKey, queries = {}}) { + const canonicalQuery = buildCanonicalQuery(queries); + return `https://${bucket}.${endpoint}/${encodePath(objectKey)}${canonicalQuery ? `?${canonicalQuery}` : ''}`; +} + +function isRetryableOssStatus(status) { + return RETRYABLE_OSS_HTTP_STATUSES.has(status); +} + +function retryDelayMs({attempt, baseDelayMs, maxDelayMs, randomFn}) { + const ceiling = Math.min(maxDelayMs, baseDelayMs * (2 ** Math.max(0, attempt - 1))); + return Math.floor(randomFn() * ceiling); +} + +async function signedOssRequest({ + method, + bucket, + endpoint, + objectKey, + accessKeyId, + accessKeySecret, + queries = {}, + headers = {}, + bodyFactory, + contentLength, + operation, + fetchImpl, + nowFn, + sleepImpl, + randomFn, + maxAttempts, + retryBaseDelayMs, + retryMaxDelayMs, +}) { + const targetUrl = buildOssUrl({bucket, endpoint, objectKey, queries}); + let lastError = null; + + for (let attempt = 1; attempt <= maxAttempts; attempt += 1) { + const now = nowFn(); + const signedHeaders = { + host: `${bucket}.${endpoint}`, + ...headers, + 'x-oss-content-sha256': UNSIGNED_PAYLOAD, + 'x-oss-date': formatOssDate(now), + }; + const authorization = buildAuthorization({ + method, + bucket, + endpoint, + objectKey, + accessKeyId, + accessKeySecret, + headers: signedHeaders, + date: now, + queries, + }); + const requestHeaders = {...signedHeaders, authorization}; + if (contentLength !== undefined) { + requestHeaders['content-length'] = String(contentLength); + } + const body = bodyFactory ? bodyFactory() : undefined; + const requestOptions = {method, headers: requestHeaders}; + if (body !== undefined) { + requestOptions.body = body; + requestOptions.duplex = 'half'; + } + + let response; + try { + response = await fetchImpl(targetUrl, requestOptions); + } catch (error) { + lastError = new Error(`OSS ${operation}请求失败: oss://${bucket}/${objectKey}`, {cause: error}); + } + + if (response?.ok) { + return response; + } + if (response) { + const responseText = await response.text(); + const requestId = response.headers.get('x-oss-request-id'); + lastError = new Error( + `OSS ${operation}失败 HTTP ${response.status}${requestId ? ` requestId=${requestId}` : ''}: ${responseText.slice(0, 500)}`, + ); + lastError.status = response.status; + } + + const retryable = !response || isRetryableOssStatus(response.status); + if (!retryable || attempt >= maxAttempts) { + throw lastError; + } + const delayMs = retryDelayMs({attempt, baseDelayMs: retryBaseDelayMs, maxDelayMs: retryMaxDelayMs, randomFn}); + console.warn(`[database-backup] OSS ${operation}失败,${delayMs}ms 后重试 (${attempt}/${maxAttempts})`); + await sleepImpl(delayMs); + } + + throw lastError ?? new Error(`OSS ${operation}失败: oss://${bucket}/${objectKey}`); +} + +function readXmlTag(xml, tagName) { + const match = new RegExp(`<${tagName}>([\\s\\S]*?)`, 'u').exec(xml); + if (!match) { + return ''; + } + return match[1] + .replace(/</gu, '<') + .replace(/>/gu, '>') + .replace(/"/gu, '"') + .replace(/'/gu, "'") + .replace(/&/gu, '&') + .trim(); +} + +function escapeXml(value) { + return String(value) + .replace(/&/gu, '&') + .replace(//gu, '>') + .replace(/"/gu, '"') + .replace(/'/gu, '''); +} + +function buildCompleteMultipartBody(parts) { + const partXml = parts + .map(({partNumber, etag}) => [ + '', + `${partNumber}`, + `${escapeXml(etag)}`, + '', + ].join('')) + .join(''); + return `${partXml}`; +} + +function resolveMultipartPartSize(fileSize, configuredPartSize) { + if (!Number.isSafeInteger(configuredPartSize) || configuredPartSize < OSS_MIN_MULTIPART_PART_SIZE_BYTES) { + throw new Error(`OSS multipart part size 必须是 >= ${OSS_MIN_MULTIPART_PART_SIZE_BYTES} 的安全整数`); + } + const minimumForPartLimit = Math.ceil(fileSize / OSS_MAX_MULTIPART_PARTS); + const partSize = Math.max(configuredPartSize, minimumForPartLimit); + if (partSize > OSS_MAX_MULTIPART_PART_SIZE_BYTES) { + throw new Error(`OSS multipart part size 超过 5GiB: ${partSize}`); + } + return partSize; +} + +async function verifyUploadedObject({requestOptions, expectedContentLength}) { + const response = await signedOssRequest({ + ...requestOptions, + method: 'HEAD', + operation: 'HEAD 验证', + }); + const contentLengthHeader = response.headers.get('content-length'); + if (!contentLengthHeader || !/^\d+$/u.test(contentLengthHeader)) { + throw new Error(`OSS HEAD 验证缺少有效 content-length: ${contentLengthHeader ?? ''}`); + } + const remoteContentLength = Number(contentLengthHeader); + if (remoteContentLength !== expectedContentLength) { + throw new Error(`OSS HEAD 验证长度不一致: local=${expectedContentLength}, remote=${remoteContentLength}`); + } + return {verifiedAt: new Date().toISOString(), remoteContentLength}; +} + +async function abortMultipartUpload({requestOptions, uploadId}) { + try { + await signedOssRequest({ + ...requestOptions, + method: 'DELETE', + queries: {uploadId}, + operation: 'AbortMultipartUpload', + maxAttempts: Math.min(2, requestOptions.maxAttempts), + }); + console.warn(`[database-backup] 已清理失败的 multipart upload: ${uploadId}`); + } catch (error) { + console.warn(`[database-backup] 清理 multipart upload 失败: ${error.message}`); + } +} + +export async function uploadArchive({ + archivePath, + bucket, + endpoint, + objectKey, + accessKeyId, + accessKeySecret, + partSizeBytes = DEFAULT_OSS_MULTIPART_PART_SIZE_BYTES, + maxAttempts = DEFAULT_OSS_REQUEST_MAX_ATTEMPTS, + retryBaseDelayMs = DEFAULT_OSS_RETRY_BASE_DELAY_MS, + retryMaxDelayMs = DEFAULT_OSS_RETRY_MAX_DELAY_MS, + fetchImpl = globalThis.fetch, + nowFn = () => new Date(), + sleepImpl = sleep, + randomFn = Math.random, +}) { const fileStat = statSync(archivePath); - const now = new Date(); - const targetUrl = `https://${bucket}.${endpoint}/${encodePath(objectKey)}`; - const headers = { - host: `${bucket}.${endpoint}`, - 'content-type': 'application/gzip', - 'x-oss-content-sha256': UNSIGNED_PAYLOAD, - 'x-oss-date': formatOssDate(now), - 'x-oss-meta-backup-kind': 'spacetimedb-data-dir', - }; - const authorization = buildAuthorization({ - method: 'PUT', + if (!fileStat.isFile() || fileStat.size <= 0) { + throw new Error(`待上传备份必须是非空文件: ${archivePath}`); + } + const partSize = resolveMultipartPartSize(fileStat.size, partSizeBytes); + const partCount = Math.ceil(fileStat.size / partSize); + const requestOptions = { bucket, endpoint, objectKey, accessKeyId, accessKeySecret, - headers, - date: now, - }); - - console.log(`[database-backup] 上传 OSS: oss://${bucket}/${objectKey}`); - let response; - try { - response = await fetch(targetUrl, { - method: 'PUT', - headers: { - ...headers, - authorization, - 'content-length': String(fileStat.size), - }, - body: createReadStream(archivePath), - duplex: 'half', - }); - } catch (error) { - throw new Error(`OSS 上传请求失败: oss://${bucket}/${objectKey}`, {cause: error}); - } - - const responseText = await response.text(); - if (!response.ok) { - throw new Error(`OSS 上传失败 HTTP ${response.status}: ${responseText.slice(0, 500)}`); - } - - return { - bucket, - objectKey, - contentLength: fileStat.size, - etag: response.headers.get('etag')?.replace(/^"|"$/gu, '') ?? '', + fetchImpl, + nowFn, + sleepImpl, + randomFn, + maxAttempts, + retryBaseDelayMs, + retryMaxDelayMs, }; + let uploadId = ''; + let uploadCompleted = false; + + console.log(`[database-backup] multipart 上传 OSS: oss://${bucket}/${objectKey} (${partCount} parts)`); + try { + const initiateResponse = await signedOssRequest({ + ...requestOptions, + method: 'POST', + queries: {uploads: null}, + headers: { + 'content-type': 'application/gzip', + 'x-oss-meta-backup-kind': 'spacetimedb-data-dir', + }, + operation: 'InitiateMultipartUpload', + }); + uploadId = readXmlTag(await initiateResponse.text(), 'UploadId'); + if (!uploadId) { + throw new Error('OSS InitiateMultipartUpload 响应缺少 UploadId'); + } + + const parts = []; + for (let partNumber = 1; partNumber <= partCount; partNumber += 1) { + const start = (partNumber - 1) * partSize; + const end = Math.min(fileStat.size, start + partSize) - 1; + const contentLength = end - start + 1; + const response = await signedOssRequest({ + ...requestOptions, + method: 'PUT', + queries: {partNumber, uploadId}, + headers: {'content-type': 'application/octet-stream'}, + contentLength, + bodyFactory: () => createReadStream(archivePath, {start, end}), + operation: `UploadPart ${partNumber}/${partCount}`, + }); + const etag = response.headers.get('etag'); + if (!etag) { + throw new Error(`OSS UploadPart ${partNumber}/${partCount} 响应缺少 ETag`); + } + parts.push({partNumber, etag}); + console.log(`[database-backup] multipart 进度: ${partNumber}/${partCount}`); + } + + const completeBody = buildCompleteMultipartBody(parts); + let completeResponse; + try { + completeResponse = await signedOssRequest({ + ...requestOptions, + method: 'POST', + queries: {uploadId}, + headers: {'content-type': 'application/xml'}, + contentLength: Buffer.byteLength(completeBody), + bodyFactory: () => completeBody, + operation: 'CompleteMultipartUpload', + }); + const completeResponseText = await completeResponse.text(); + if (/)/u.test(completeResponseText)) { + throw new Error(`OSS CompleteMultipartUpload 返回错误: ${completeResponseText.slice(0, 500)}`); + } + } catch (completeError) { + try { + await verifyUploadedObject({requestOptions, expectedContentLength: fileStat.size}); + completeResponse = null; + } catch { + throw completeError; + } + } + + const verification = await verifyUploadedObject({requestOptions, expectedContentLength: fileStat.size}); + uploadCompleted = true; + return { + bucket, + objectKey, + contentLength: fileStat.size, + etag: completeResponse?.headers.get('etag')?.replace(/^"|"$/gu, '') ?? '', + uploadMode: 'multipart', + partCount, + partSizeBytes: partSize, + verifiedAt: verification.verifiedAt, + }; + } catch (error) { + if (uploadId && !uploadCompleted) { + await abortMultipartUpload({requestOptions, uploadId}); + } + throw error; + } } async function uploadExistingArchive({args, env, bucket, endpoint, accessKeyId, accessKeySecret, objectPrefix}) { @@ -603,6 +882,10 @@ async function uploadExistingArchive({args, env, bucket, endpoint, accessKeyId, objectKey: result.objectKey, contentLength: result.contentLength, etag: result.etag, + uploadMode: result.uploadMode, + partCount: result.partCount, + partSizeBytes: result.partSizeBytes, + verifiedAt: result.verifiedAt, uploadedAt, uploadStatus: 'uploaded', }, @@ -736,6 +1019,10 @@ async function main() { archivePath, contentLength: result.contentLength, etag: result.etag, + uploadMode: result.uploadMode, + partCount: result.partCount, + partSizeBytes: result.partSizeBytes, + verifiedAt: result.verifiedAt, uploadedAt: new Date().toISOString(), uploadStatus: 'uploaded', }, @@ -791,9 +1078,11 @@ function describeError(error) { return lines; } -main().catch((error) => { - for (const line of describeError(error)) { - console.error(`[database-backup] ${line}`); - } - process.exit(1); -}); +if (process.argv[1] && realpathSync(resolve(process.argv[1])) === realpathSync(__filename)) { + main().catch((error) => { + for (const line of describeError(error)) { + console.error(`[database-backup] ${line}`); + } + process.exit(1); + }); +} diff --git a/scripts/deploy/production-api-deploy.sh b/scripts/deploy/production-api-deploy.sh index 2b0ced40c..dbddc4331 100644 --- a/scripts/deploy/production-api-deploy.sh +++ b/scripts/deploy/production-api-deploy.sh @@ -5,13 +5,14 @@ set -euo pipefail usage() { cat <<'EOF' 用法: - ./scripts/deploy/production-api-deploy.sh --source-dir build/ [--version ] [--release-root /opt/genarrative/releases] [--current-link /opt/genarrative/current] [--service genarrative-api.service] [--pingora-service genarrative-pingora-gateway.service] [--require-pingora-gateway] [--worker-service-pattern 'genarrative-external-generation-worker@*.service'] [--no-worker-services] [--worker-controller-service genarrative-external-generation-controller.service] [--no-worker-controller] [--health-url http://127.0.0.1:8082/readyz] [--api-env-file /etc/genarrative/api-server.env] [--worker-env-file /etc/genarrative/external-generation-worker.env] [--database genarrative-prod] [--spacetime-server-url http://127.0.0.1:3101] + ./scripts/deploy/production-api-deploy.sh --source-dir build/ [--version ] [--release-root /opt/genarrative/releases] [--current-link /opt/genarrative/current] [--service genarrative-api.service] [--pingora-service genarrative-pingora-gateway.service] [--require-pingora-gateway] [--worker-service-pattern 'genarrative-external-generation-worker@*.service'] [--no-worker-services] [--worker-controller-service genarrative-external-generation-controller.service] [--no-worker-controller] [--health-url http://127.0.0.1:8082/readyz] [--api-env-file /etc/genarrative/api-server.env] [--worker-env-file /etc/genarrative/external-generation-worker.env] [--database genarrative-prod] [--spacetime-server-url http://127.0.0.1:3101] [--keep-maintenance-mode] 说明: 进入维护模式,校验并发布 api-server 单文件,更新 current 链接,重启 systemd 服务并执行 readiness 检查。 默认同时重启外部生成 worker controller 和已加载的 worker 实例;未启用 worker 单元时会自动跳过。 若传入 --database,会在重启前把 GENARRATIVE_SPACETIME_DATABASE 写入 api-server 环境文件,避免服务继续读取旧库。 若发布包包含 pingora-gateway,或传入 --require-pingora-gateway,部署脚本会要求 release manifest、二进制与 checksum 一致,再在 current 链接切换后先复核 systemd/env 仍是本机高端口 shadow 配置,启动或重启 Pingora 影子服务并复核 active。 + 默认在 readiness 通过后退出维护模式;传入 --keep-maintenance-mode 时保留维护文件,供人工验收后再恢复公网。 current 链接切换前失败时会退出本次打开的维护模式;current 链接切换后失败时保留维护模式,避免暴露半发布版本。 EOF } @@ -687,7 +688,9 @@ SPACETIME_SERVER_URL="" DEPLOY_COMPLETED=0 PINGORA_INCLUDED=0 REQUIRE_PINGORA_GATEWAY=0 +KEEP_MAINTENANCE_MODE=0 MAINTENANCE_ENABLED_BY_DEPLOY=0 +MAINTENANCE_FILE="${GENARRATIVE_MAINTENANCE_FILE:-/var/lib/genarrative/maintenance/enabled}" CURRENT_LINK_SWITCHED=0 RELEASE_DIR="" STAGING_RELEASE_DIR="" @@ -726,6 +729,10 @@ while [[ $# -gt 0 ]]; do REQUIRE_PINGORA_GATEWAY=1 shift ;; + --keep-maintenance-mode) + KEEP_MAINTENANCE_MODE=1 + shift + ;; --worker-service-pattern) WORKER_SERVICE_PATTERN="${2:?缺少 --worker-service-pattern 的值}" shift 2 @@ -847,8 +854,12 @@ on_exit() { trap on_exit EXIT +if [[ ! -f "${MAINTENANCE_FILE}" ]]; then + MAINTENANCE_ENABLED_BY_DEPLOY=1 +else + echo "[production-api-deploy] 继承已有维护模式;部署失败时不得误退出上游维护窗口: ${MAINTENANCE_FILE}" +fi bash "${SCRIPT_DIR}/maintenance-on.sh" "api deploy ${VERSION}" -MAINTENANCE_ENABLED_BY_DEPLOY=1 echo "[production-api-deploy] 校验 api-server" ( @@ -885,6 +896,9 @@ if [[ -f "${SOURCE_DIR}/pingora-gateway" ]]; then fi BACKUP_SCRIPT_SOURCE="${SOURCE_DIR}/scripts/database-backup-to-oss.mjs" +API_DEPLOY_SCRIPT_SOURCE="${SOURCE_DIR}/scripts/deploy/production-api-deploy.sh" +MAINTENANCE_ON_SCRIPT_SOURCE="${SOURCE_DIR}/scripts/deploy/maintenance-on.sh" +MAINTENANCE_OFF_SCRIPT_SOURCE="${SOURCE_DIR}/scripts/deploy/maintenance-off.sh" HEALTH_PATROL_SCRIPT_SOURCE="${SOURCE_DIR}/scripts/ops/production-health-patrol.mjs" PINGORA_CURRENT_RELEASE_AUDIT_SCRIPT_SOURCE="${SOURCE_DIR}/scripts/ops/pingora-current-release-audit.mjs" PINGORA_DIRECT_REHEARSAL_STATUS_SCRIPT_SOURCE="${SOURCE_DIR}/scripts/ops/pingora-direct-rehearsal-status.mjs" @@ -911,6 +925,19 @@ SYSTEMD_DEPLOY_DIR_SOURCE="${SOURCE_DIR}/deploy/systemd" NGINX_DEPLOY_DIR_SOURCE="${SOURCE_DIR}/deploy/nginx" ENV_DEPLOY_DIR_SOURCE="${SOURCE_DIR}/deploy/env" mkdir -p "${RELEASE_CONTENT_DIR}/scripts" "${RELEASE_CONTENT_DIR}/scripts/deploy" "${RELEASE_CONTENT_DIR}/scripts/ops" "${RELEASE_CONTENT_DIR}/deploy" +if [[ ! -f "${API_DEPLOY_SCRIPT_SOURCE}" ]]; then + echo "[production-api-deploy] 发布产物缺少 API 部署脚本: ${SOURCE_DIR}/scripts/deploy/production-api-deploy.sh" >&2 + exit 1 +fi +cp "${API_DEPLOY_SCRIPT_SOURCE}" "${RELEASE_CONTENT_DIR}/scripts/deploy/production-api-deploy.sh" +chmod 0755 "${RELEASE_CONTENT_DIR}/scripts/deploy/production-api-deploy.sh" +if [[ ! -f "${MAINTENANCE_ON_SCRIPT_SOURCE}" || ! -f "${MAINTENANCE_OFF_SCRIPT_SOURCE}" ]]; then + echo "[production-api-deploy] 发布产物缺少维护模式脚本: ${SOURCE_DIR}/scripts/deploy" >&2 + exit 1 +fi +cp "${MAINTENANCE_ON_SCRIPT_SOURCE}" "${RELEASE_CONTENT_DIR}/scripts/deploy/maintenance-on.sh" +cp "${MAINTENANCE_OFF_SCRIPT_SOURCE}" "${RELEASE_CONTENT_DIR}/scripts/deploy/maintenance-off.sh" +chmod 0755 "${RELEASE_CONTENT_DIR}/scripts/deploy/maintenance-on.sh" "${RELEASE_CONTENT_DIR}/scripts/deploy/maintenance-off.sh" if [[ ! -f "${BACKUP_SCRIPT_SOURCE}" ]]; then echo "[production-api-deploy] 发布产物缺少数据库备份脚本: ${SOURCE_DIR}/scripts/database-backup-to-oss.mjs" >&2 exit 1 @@ -1115,8 +1142,12 @@ wait_for_worker_controller_service "${WORKER_CONTROLLER_SERVICE}" echo "[production-api-deploy] 等待 readiness: ${HEALTH_URL}" for _ in {1..30}; do - if curl -fsS "${HEALTH_URL}" >/dev/null; then - bash "${SCRIPT_DIR}/maintenance-off.sh" + if curl -fsS --max-time 2 "${HEALTH_URL}" >/dev/null; then + if [[ "${KEEP_MAINTENANCE_MODE}" -eq 1 ]]; then + echo "[production-api-deploy] readiness 通过,按参数保持维护模式: ${MAINTENANCE_FILE}" + else + bash "${SCRIPT_DIR}/maintenance-off.sh" + fi DEPLOY_COMPLETED=1 echo "[production-api-deploy] 完成: ${RELEASE_DIR}/api-server" exit 0 diff --git a/scripts/deploy/production-stdb-publish.sh b/scripts/deploy/production-stdb-publish.sh index a1425646e..96817420b 100644 --- a/scripts/deploy/production-stdb-publish.sh +++ b/scripts/deploy/production-stdb-publish.sh @@ -6,16 +6,18 @@ umask 077 usage() { cat <<'EOF' 用法: - ./scripts/deploy/production-stdb-publish.sh --source-dir build/ --database --migration-bootstrap-secret-file [--server-url http://127.0.0.1:3101] [--server local] [--root-dir /stdb] [--run-as-user spacetimedb] [--api-env-file /etc/genarrative/api-server.env] [--worker-env-file /etc/genarrative/external-generation-worker.env] [--api-health-url http://127.0.0.1:8082/healthz] [--api-readiness-timeout-seconds 60] [--clear-database] [--backup-mode async|sync|skip] + ./scripts/deploy/production-stdb-publish.sh --source-dir build/ --database --migration-bootstrap-secret-file [--server-url http://127.0.0.1:3101] [--server local] [--root-dir /stdb] [--run-as-user spacetimedb] [--api-env-file /etc/genarrative/api-server.env] [--worker-env-file /etc/genarrative/external-generation-worker.env] [--api-health-url http://127.0.0.1:8082/healthz] [--api-readiness-timeout-seconds 60] [--keep-maintenance-mode] [--backup-mode async|sync|skip] 说明: 进入维护模式,校验 spacetime_module.wasm.sha256,并在生产实例本机执行 spacetime publish。 + publish 固定使用 --delete-data=never 与 scoped --yes=migrate,break-clients;任何需要删除数据的 schema 冲突都会阻断发布。 默认使用 http://127.0.0.1:3101,避免与部署机本机 Git/Web 服务的 3000 端口冲突。 默认使用 /stdb 作为 spacetime CLI root-dir,并以 spacetimedb 用户发布,避免 root CLI 身份污染自托管实例。 发布时固定追加 --no-config,只使用显式参数,避免工作区或用户目录里的 spacetime 配置干扰目标。 async 模式会在 publish 前先做本地冷备份,再在 publish 完成后后台上传 OSS,避免低带宽上传阻塞部署。 如需强制等待备份完成并在失败时阻断 publish,传入 --backup-mode sync。 发布成功后会补齐生产 API/worker env 的固定 bootstrap secret FILE 路径,再重启并验活重启前 active 的服务。 + --keep-maintenance-mode 会在 publish 前停止旧 API/controller/worker,并在成功后保持维护态,交由后续 API deploy 恢复服务。 migration bootstrap secret 必须由 Jenkins Secret File credential 或等价的受保护文件提供,不从构建 artifact 读取。 如果 API 重启前为 active,会在退出维护模式前等待本机 /healthz readiness 通过。 失败时保留维护模式。 @@ -51,7 +53,7 @@ RUN_AS_USER="spacetimedb" MIGRATION_BOOTSTRAP_SECRET_FILE="" API_ENV_FILE="${GENARRATIVE_STDB_PUBLISH_API_ENV_FILE:-/etc/genarrative/api-server.env}" WORKER_ENV_FILE="${GENARRATIVE_STDB_PUBLISH_WORKER_ENV_FILE:-/etc/genarrative/external-generation-worker.env}" -CLEAR_DATABASE=0 +KEEP_MAINTENANCE_MODE=0 BACKUP_MODE="${GENARRATIVE_STDB_PUBLISH_BACKUP_MODE:-async}" DEPLOY_COMPLETED=0 PUBLISH_TMP_DIR="" @@ -285,6 +287,54 @@ restart_runtime_services_after_bootstrap_secret_install() { fi } +stop_runtime_services_for_rollout_gate() { + local api_state="" + local controller_state="" + local worker_service="" + local worker_units_output="" + local -a services_to_stop=() + + api_state="$(get_runtime_service_active_state genarrative-api.service)" + controller_state="$(get_runtime_service_active_state genarrative-external-generation-controller.service)" + if ! worker_units_output="$( + run_privileged systemctl list-units \ + --type=service \ + --state=active \ + --no-legend \ + --plain \ + 'genarrative-external-generation-worker@*.service' + )"; then + echo "[production-stdb-publish] 查询 active worker 服务失败,无法建立受控维护窗口。" >&2 + return 1 + fi + + if [[ "${controller_state}" == "active" ]]; then + services_to_stop+=(genarrative-external-generation-controller.service) + fi + while read -r worker_service _; do + if [[ "${worker_service}" =~ ^genarrative-external-generation-worker@[A-Za-z0-9_.@:-]+\.service$ ]]; then + services_to_stop+=("${worker_service}") + fi + done <<<"${worker_units_output}" + if [[ "${api_state}" == "active" ]]; then + services_to_stop+=(genarrative-api.service) + fi + + if [[ "${#services_to_stop[@]}" -eq 0 ]]; then + echo "[production-stdb-publish] 受控维护窗口开始前没有 active API/controller/worker。" + return 0 + fi + + echo "[production-stdb-publish] 停止旧运行时服务并保持维护态: ${services_to_stop[*]}" + run_privileged systemctl stop "${services_to_stop[@]}" + for worker_service in "${services_to_stop[@]}"; do + if [[ "$(get_runtime_service_active_state "${worker_service}")" == "active" ]]; then + echo "[production-stdb-publish] 运行时服务停止后仍为 active: ${worker_service}" >&2 + return 1 + fi + done +} + while [[ $# -gt 0 ]]; do case "$1" in -h|--help) @@ -336,8 +386,8 @@ while [[ $# -gt 0 ]]; do API_READINESS_TIMEOUT_SECONDS="${2:?缺少 --api-readiness-timeout-seconds 的值}" shift 2 ;; - --clear-database) - CLEAR_DATABASE=1 + --keep-maintenance-mode) + KEEP_MAINTENANCE_MODE=1 shift ;; --skip-backup) @@ -455,6 +505,7 @@ on_exit() { trap on_exit EXIT prepare_async_backup() { + local -a restart_service_args=() ASYNC_BACKUP_SCRIPT="${SCRIPT_DIR}/../database-backup-to-oss.mjs" if [[ ! -f "${ASYNC_BACKUP_SCRIPT}" ]]; then ASYNC_BACKUP_SCRIPT="${SOURCE_DIR}/scripts/database-backup-to-oss.mjs" @@ -464,6 +515,10 @@ prepare_async_backup() { exit 1 fi + if [[ "${KEEP_MAINTENANCE_MODE}" -ne 1 ]]; then + restart_service_args+=(--restart-service-after genarrative-api.service) + fi + ASYNC_BACKUP_STATUS_FILE="$(mktemp /tmp/genarrative-stdb-backup-status.XXXXXX.json)" echo "[production-stdb-publish] publish 前生成本地冷备份,随后会异步上传 OSS" node -- "${ASYNC_BACKUP_SCRIPT}" \ @@ -471,7 +526,7 @@ prepare_async_backup() { --data-dir "${SPACETIME_ROOT_DIR}" \ --database "${DATABASE}" \ --stop-service spacetimedb.service \ - --restart-service-after genarrative-api.service \ + "${restart_service_args[@]}" \ --defer-upload \ --result-file "${ASYNC_BACKUP_STATUS_FILE}" } @@ -555,12 +610,16 @@ wait_for_api_healthz_ready() { } "${SCRIPT_DIR}/maintenance-on.sh" "spacetime module publish ${DATABASE}" +if [[ "${KEEP_MAINTENANCE_MODE}" -eq 1 ]]; then + stop_runtime_services_for_rollout_gate +fi case "${BACKUP_MODE}" in async) prepare_async_backup ;; sync) + SYNC_BACKUP_RESTART_SERVICE_ARGS=() BACKUP_SCRIPT="${SCRIPT_DIR}/../database-backup-to-oss.mjs" if [[ ! -f "${BACKUP_SCRIPT}" ]]; then BACKUP_SCRIPT="${SOURCE_DIR}/scripts/database-backup-to-oss.mjs" @@ -569,6 +628,9 @@ case "${BACKUP_MODE}" in echo "[production-stdb-publish] 缺少 publish 前数据库备份脚本: ${BACKUP_SCRIPT}" >&2 exit 1 fi + if [[ "${KEEP_MAINTENANCE_MODE}" -ne 1 ]]; then + SYNC_BACKUP_RESTART_SERVICE_ARGS+=(--restart-service-after genarrative-api.service) + fi echo "[production-stdb-publish] publish 前同步执行 OSS 冷备份,失败会阻断发布" node -- "${BACKUP_SCRIPT}" \ @@ -576,7 +638,7 @@ case "${BACKUP_MODE}" in --data-dir "${SPACETIME_ROOT_DIR}" \ --database "${DATABASE}" \ --stop-service spacetimedb.service \ - --restart-service-after genarrative-api.service + "${SYNC_BACKUP_RESTART_SERVICE_ARGS[@]}" ;; skip) echo "[production-stdb-publish] 已按参数跳过 publish 前数据库备份" @@ -596,7 +658,8 @@ PUBLISH_ARGS=( publish "${DATABASE}" --bin-path "${SOURCE_DIR}/spacetime_module.wasm" - --yes + --delete-data=never + --yes=migrate,break-clients --no-config ) @@ -606,10 +669,6 @@ else PUBLISH_ARGS+=(--server "${SERVER_ALIAS}") fi -if [[ "${CLEAR_DATABASE}" -eq 1 ]]; then - PUBLISH_ARGS+=(--clear-database) -fi - if [[ -n "${SERVER_URL}" ]]; then echo "[production-stdb-publish] 发布 SpacetimeDB module: ${DATABASE} -> ${SERVER_URL}, root=${SPACETIME_ROOT_DIR}" else @@ -629,7 +688,8 @@ if [[ -n "${RUN_AS_USER}" && "$(id -u)" -eq 0 ]]; then publish "${DATABASE}" --bin-path "${PUBLISH_TMP_DIR}/spacetime_module.wasm" - --yes + --delete-data=never + --yes=migrate,break-clients --no-config ) if [[ -n "${SERVER_URL}" ]]; then @@ -637,9 +697,6 @@ if [[ -n "${RUN_AS_USER}" && "$(id -u)" -eq 0 ]]; then else PUBLISH_ARGS+=(--server "${SERVER_ALIAS}") fi - if [[ "${CLEAR_DATABASE}" -eq 1 ]]; then - PUBLISH_ARGS+=(--clear-database) - fi runuser -u "${RUN_AS_USER}" -- spacetime "${PUBLISH_ARGS[@]}" else spacetime "${PUBLISH_ARGS[@]}" @@ -668,6 +725,11 @@ run_privileged runuser -u genarrative -- test -r "${RUNTIME_SERVICE_BOOTSTRAP_SE echo "[production-stdb-publish] 已安装运行时服务身份引导密钥: ${RUNTIME_SERVICE_BOOTSTRAP_SECRET_FILE}" ensure_runtime_bootstrap_secret_env_file "${API_ENV_FILE}" true ensure_runtime_bootstrap_secret_env_file "${WORKER_ENV_FILE}" false +if [[ "${KEEP_MAINTENANCE_MODE}" -eq 1 ]]; then + echo "[production-stdb-publish] module 发布完成;按参数保持维护模式和旧运行时服务停止状态,等待人工维护与 API deploy。" + DEPLOY_COMPLETED=1 + exit 0 +fi restart_runtime_services_after_bootstrap_secret_install wait_for_api_healthz_ready diff --git a/scripts/dev.test.ts b/scripts/dev.test.ts index 8fa92da4f..988d21bf9 100644 --- a/scripts/dev.test.ts +++ b/scripts/dev.test.ts @@ -704,24 +704,24 @@ describe('dev scheduler watch routing', () => { describe('dev scheduler spacetime refresh', () => { test('解析 Cargo 精确版本要求时用于 CLI 校验的版本号不带等号', () => { - expect(normalizeCargoVersionRequirement('=2.5.0')).toBe('2.5.0'); - expect(normalizeCargoVersionRequirement('2.5.0')).toBe('2.5.0'); + expect(normalizeCargoVersionRequirement('=2.6.0')).toBe('2.6.0'); + expect(normalizeCargoVersionRequirement('2.6.0')).toBe('2.6.0'); }); test('解析 spacetime --version 输出里的 tool version', () => { const version = parseSpacetimeToolVersion(` -A new version of SpacetimeDB is available: v2.5.0 (current: v2.4.1) -spacetimedb tool version 2.5.0; spacetimedb-lib version 2.5.0; +A new version of SpacetimeDB is available: v2.6.1 (current: v2.5.0) +spacetimedb tool version 2.6.0; spacetimedb-lib version 2.6.0; `); - expect(version).toBe('2.5.0'); + expect(version).toBe('2.6.0'); }); test('本机 spacetime 版本和 workspace 锁定版本不一致时直接报清楚', () => { expect(() => assertSpacetimeToolVersionMatchesWorkspace({ toolVersion: '2.1.0', - workspaceVersion: '2.5.0', + workspaceVersion: '2.6.0', }), ).toThrow('procedure 返回值 BSATN 反序列化失败'); }); diff --git a/scripts/prepare-server-provision-tools.sh b/scripts/prepare-server-provision-tools.sh index 78ef8bfb6..a1bc2ee70 100755 --- a/scripts/prepare-server-provision-tools.sh +++ b/scripts/prepare-server-provision-tools.sh @@ -9,7 +9,7 @@ OTELCOL_DOWNLOAD_ROOT="${OTELCOL_DOWNLOAD_ROOT:-https://github.com/open-telemetr OTELCOL_ARCHIVE_PATH="${OTELCOL_ARCHIVE_PATH:-}" OTELCOL_TARGET_BIN="${OTELCOL_TARGET_BIN:-/usr/local/bin/otelcol-contrib}" SPACETIME_INSTALLER_URL="${SPACETIME_INSTALLER_URL:-https://install.spacetimedb.com}" -SPACETIME_DOWNLOAD_ROOT="${SPACETIME_DOWNLOAD_ROOT:-https://github.com/clockworklabs/SpacetimeDB/releases/download/v2.5.0}" +SPACETIME_DOWNLOAD_ROOT="${SPACETIME_DOWNLOAD_ROOT:-https://github.com/clockworklabs/SpacetimeDB/releases/download/v2.6.0}" SPACETIME_TARGET_HOST="${SPACETIME_TARGET_HOST:-x86_64-unknown-linux-gnu}" SPACETIME_ROOT="${SPACETIME_ROOT:-/stdb}" SPACETIME_EXPECTED_VERSION="${SPACETIME_EXPECTED_VERSION:-}" diff --git a/scripts/spacetime-maintain-external-generation-jobs.mjs b/scripts/spacetime-maintain-external-generation-jobs.mjs new file mode 100644 index 000000000..69ead2f3c --- /dev/null +++ b/scripts/spacetime-maintain-external-generation-jobs.mjs @@ -0,0 +1,162 @@ +#!/usr/bin/env node + +import { + callSpacetimeProcedureViaCli, + encodeSpacetimeCliOption, + ensureProcedureOk, + parsePositiveInteger, +} from './spacetime-migration-common.mjs'; + +const MAX_BATCH_SIZE = 25; + +function usage() { + return `用法: + node scripts/spacetime-maintain-external-generation-jobs.mjs --database [选项] + +默认只 dry-run 一批历史终态任务 payload 压缩,不修改数据库。 + +公共选项: + --database 目标数据库(必填,也可用 GENARRATIVE_SPACETIME_DATABASE) + --server spacetime CLI server 名或 URL + --server-url 显式 server URL + --limit <1-${MAX_BATCH_SIZE}> 单批任务数,默认 10 + --cursor-job-id 从上一批 next_cursor_job_id 继续 + --apply 执行写入;省略时始终 dry-run + --backfill-summaries 改为回填轻量摘要投影 + --owner-user-id 仅摘要回填可选,限定 owner + --completed-before-micros 仅 payload 压缩可选,限定终态完成时间 + --help 显示帮助 + +必须使用已授权 migration operator 的 spacetime CLI 登录态。脚本每次只处理一批; +根据返回的 next_cursor_job_id 与 has_more 手工继续,避免在生产一次长事务扫完整历史。`; +} + +function parseOptions(argv) { + const options = { + apply: false, + backfillSummaries: false, + completedBeforeMicros: null, + cursorJobId: '', + database: process.env.GENARRATIVE_SPACETIME_DATABASE || '', + limit: 10, + ownerUserId: '', + passthrough: [], + server: process.env.GENARRATIVE_SPACETIME_SERVER || '', + serverUrl: process.env.GENARRATIVE_SPACETIME_SERVER_URL || '', + }; + + for (let index = 0; index < argv.length; index += 1) { + const arg = argv[index]; + const readValue = (name) => { + const value = argv[index + 1]; + if (!value || value.startsWith('--')) { + throw new Error(`${name} 缺少参数值。`); + } + index += 1; + return value; + }; + + if (arg === '--database') { + options.database = readValue(arg); + } else if (arg === '--server') { + options.server = readValue(arg); + } else if (arg === '--server-url') { + options.serverUrl = readValue(arg); + } else if (arg === '--limit') { + options.limit = parsePositiveInteger(readValue(arg), arg); + } else if (arg === '--cursor-job-id') { + options.cursorJobId = readValue(arg).trim(); + } else if (arg === '--completed-before-micros') { + const value = readValue(arg); + if (!/^-?[0-9]+$/u.test(value)) { + throw new Error(`${arg} 必须是整数。`); + } + const parsed = Number.parseInt(value, 10); + if (!Number.isSafeInteger(parsed)) { + throw new Error(`${arg} 超出 JavaScript 安全整数范围。`); + } + options.completedBeforeMicros = parsed; + } else if (arg === '--owner-user-id') { + options.ownerUserId = readValue(arg).trim(); + } else if (arg === '--apply') { + options.apply = true; + } else if (arg === '--backfill-summaries') { + options.backfillSummaries = true; + } else if (arg === '--help' || arg === '-h') { + options.help = true; + } else { + throw new Error(`未知参数: ${arg}`); + } + } + + if (options.limit > MAX_BATCH_SIZE) { + throw new Error(`--limit 不能超过 ${MAX_BATCH_SIZE}。`); + } + if (options.ownerUserId && !options.backfillSummaries) { + throw new Error('--owner-user-id 只能与 --backfill-summaries 一起使用。'); + } + if (options.completedBeforeMicros !== null && options.backfillSummaries) { + throw new Error('--completed-before-micros 不能用于摘要回填。'); + } + return options; +} + +try { + const options = parseOptions(process.argv.slice(2)); + if (options.help) { + console.log(usage()); + process.exit(0); + } + if (!options.database) { + throw new Error( + '必须传入 --database,或设置 GENARRATIVE_SPACETIME_DATABASE。', + ); + } + + const procedureName = options.backfillSummaries + ? 'backfill_external_generation_job_summaries_and_return' + : 'compact_external_generation_job_payloads_and_return'; + const input = options.backfillSummaries + ? { + owner_user_id: encodeSpacetimeCliOption(options.ownerUserId || null), + limit: options.limit, + cursor_job_id: encodeSpacetimeCliOption(options.cursorJobId || null), + dry_run: !options.apply, + } + : { + dry_run: !options.apply, + limit: options.limit, + cursor_job_id: encodeSpacetimeCliOption(options.cursorJobId || null), + completed_before_micros: encodeSpacetimeCliOption( + options.completedBeforeMicros, + ), + }; + const result = await callSpacetimeProcedureViaCli( + options, + procedureName, + input, + ); + ensureProcedureOk(result); + + console.log(JSON.stringify({ procedure: procedureName, ...result }, null, 2)); + const pendingApplyCount = options.backfillSummaries + ? Number(result.selected_count ?? 0) + : Number(result.matched_count ?? 0); + if (result.has_more && options.apply) { + console.log( + `仍有后续批次;下一次追加 --cursor-job-id ${result.next_cursor_job_id ?? ''}。`, + ); + } else if (!options.apply && (result.has_more || pendingApplyCount > 0)) { + const currentCursor = options.cursorJobId + ? `保留 --cursor-job-id ${options.cursorJobId}` + : '仍从首批开始'; + console.log( + `当前仅 dry-run;请${currentCursor}并追加 --apply 重跑同一批。apply 成功后再使用其 next_cursor_job_id 进入下一批。`, + ); + } +} catch (error) { + console.error( + `[spacetime:external-generation:maintenance] ${error instanceof Error ? error.message : String(error)}`, + ); + process.exit(1); +} diff --git a/scripts/spacetime-migration-common.mjs b/scripts/spacetime-migration-common.mjs index 62d7d0037..0dc8d3fd1 100644 --- a/scripts/spacetime-migration-common.mjs +++ b/scripts/spacetime-migration-common.mjs @@ -109,6 +109,10 @@ export function parsePositiveInteger(value, name) { return parsed; } +export function encodeSpacetimeCliOption(value) { + return value === null || value === undefined ? null : [0, value]; +} + function parseOptionalPositiveInteger(value, name) { if (!value) { return 0; @@ -171,7 +175,7 @@ export async function callSpacetimeProcedure(options, procedureName, input) { ); } - return parseProcedureResult(text); + return parseProcedureResult(text, procedureName); } export async function createSpacetimeWebIdentity(options) { @@ -225,7 +229,7 @@ export async function callSpacetimeProcedureAuto(options, procedureName, input) export async function callSpacetimeProcedureViaCli(options, procedureName, input) { const args = buildSpacetimeCallArgs(options, procedureName, input); const output = await runSpacetimeCli(args); - return parseProcedureResult(output); + return parseProcedureResult(output, procedureName); } export function validateSpacetimeDatabaseName(database) { @@ -236,7 +240,7 @@ export function validateSpacetimeDatabaseName(database) { } } -export function parseProcedureResult(output) { +export function parseProcedureResult(output, procedureName = '') { const candidates = []; const trimmed = output.trim(); if (trimmed) { @@ -252,7 +256,7 @@ export function parseProcedureResult(output) { for (const candidate of candidates) { try { - return normalizeProcedureResult(JSON.parse(candidate)); + return normalizeProcedureResult(JSON.parse(candidate), procedureName); } catch { // SpacetimeDB CLI 在不同版本中可能附带说明文本,继续尝试后续候选。 } @@ -275,19 +279,55 @@ export async function assertReadableFile(filePath) { await access(path.resolve(filePath)); } -function normalizeProcedureResult(value) { +function normalizeProcedureResult(value, procedureName) { if (value && typeof value === 'object' && !Array.isArray(value)) { return value; } if (Array.isArray(value)) { - return normalizeSatsProduct(value); + return normalizeSatsProduct(value, procedureName); } throw new Error('procedure 返回值不是对象。'); } -function normalizeSatsProduct(value) { +function normalizeSatsProduct(value, procedureName) { + if ( + procedureName === 'backfill_external_generation_job_summaries_and_return' && + value.length === 8 + ) { + return { + ok: normalizeSatsValue(value[0]), + dry_run: normalizeSatsValue(value[1]), + scanned_count: normalizeSatsValue(value[2]), + selected_count: normalizeSatsValue(value[3]), + upserted_count: normalizeSatsValue(value[4]), + next_cursor_job_id: normalizeSatsOption(value[5]), + has_more: normalizeSatsValue(value[6]), + error_message: normalizeSatsOption(value[7]), + }; + } + + if ( + procedureName === 'compact_external_generation_job_payloads_and_return' && + value.length === 12 + ) { + return { + ok: normalizeSatsValue(value[0]), + dry_run: normalizeSatsValue(value[1]), + scanned_count: normalizeSatsValue(value[2]), + matched_count: normalizeSatsValue(value[3]), + updated_count: normalizeSatsValue(value[4]), + before_bytes: normalizeSatsValue(value[5]), + after_bytes: normalizeSatsValue(value[6]), + inline_media_count: normalizeSatsValue(value[7]), + invalid_json_count: normalizeSatsValue(value[8]), + next_cursor_job_id: normalizeSatsOption(value[9]), + has_more: normalizeSatsValue(value[10]), + error_message: normalizeSatsOption(value[11]), + }; + } + if (value.length === 3) { return { ok: normalizeSatsValue(value[0]), diff --git a/scripts/spacetime-migration-common.test.ts b/scripts/spacetime-migration-common.test.ts new file mode 100644 index 000000000..02219e50b --- /dev/null +++ b/scripts/spacetime-migration-common.test.ts @@ -0,0 +1,35 @@ +import { describe, expect, it } from 'vitest'; +import { + buildSpacetimeCallArgs, + encodeSpacetimeCliOption, +} from './spacetime-migration-common.mjs'; + +describe('SpacetimeDB CLI SATS option encoding', () => { + it('keeps absent options null and wraps present values as Some', () => { + expect(encodeSpacetimeCliOption(null)).toBeNull(); + expect(encodeSpacetimeCliOption(undefined)).toBeNull(); + expect(encodeSpacetimeCliOption('job-2')).toEqual([0, 'job-2']); + expect(encodeSpacetimeCliOption(123)).toEqual([0, 123]); + }); + + it('serializes non-empty maintenance cursors in the CLI procedure input', () => { + const args = buildSpacetimeCallArgs( + { + database: 'genarrative-prod', + passthrough: [], + serverUrl: 'http://127.0.0.1:3311', + }, + 'compact_external_generation_job_payloads_and_return', + { + dry_run: true, + limit: 1, + cursor_job_id: encodeSpacetimeCliOption('job-2'), + completed_before_micros: encodeSpacetimeCliOption(123), + }, + ); + + const input = JSON.parse(args.at(-2) ?? 'null'); + expect(input.cursor_job_id).toEqual([0, 'job-2']); + expect(input.completed_before_micros).toEqual([0, 123]); + }); +}); diff --git a/server-rs/Cargo.lock b/server-rs/Cargo.lock index ae19afaf0..8333ba60b 100644 --- a/server-rs/Cargo.lock +++ b/server-rs/Cargo.lock @@ -4550,6 +4550,7 @@ dependencies = [ "tracing", "url", "urlencoding", + "x509-parser", ] [[package]] @@ -5869,9 +5870,9 @@ dependencies = [ [[package]] name = "spacetimedb" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbe68c40e700df6586b1d6a94e52baaa9203d6425b50b0ac5870fe0f543d94d" +checksum = "8037ee5a6fa7348bf0392c728e4de4a0ba6f89c967eff80b666ac3e1e8f2b612" dependencies = [ "anyhow", "bytemuck", @@ -5892,9 +5893,9 @@ dependencies = [ [[package]] name = "spacetimedb-bindings-macro" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3001a940fc424e322f2512ef9a81374ba5da8ea42735ccef7fcce480927bbff1" +checksum = "202fb591c7d18fbbd4aec24a34ad6f03e3accc41580aee0fd2f9ad0514681917" dependencies = [ "heck 0.4.1", "humantime", @@ -5906,18 +5907,18 @@ dependencies = [ [[package]] name = "spacetimedb-bindings-sys" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c418591c1da58ab6cfacdc57077996fe4a101b05fcd06889ab86d1cbc718216a" +checksum = "60869317019bbc8fa571e5c8ff21fe72abf9e5bcb579531efdded4e93a76e827" dependencies = [ "spacetimedb-primitives", ] [[package]] name = "spacetimedb-client-api-messages" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3042c18f2b424fc7786b5bd5af59275b903c251ba41d48f11bef28c49f77f73" +checksum = "ad481e05f959aa646e36e3e2e8b7581da58ca8f8a1b5e643122fc2bd4a4ff0df" dependencies = [ "bytes", "bytestring", @@ -5937,9 +5938,9 @@ dependencies = [ [[package]] name = "spacetimedb-data-structures" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c1d60cf81d56be3801c0398b701051d9319f6c38e5ec0f9282b29a2c1b2dab" +checksum = "9ec3aa387bfa45691c11b57e9072522dde3bbb554f7e017b57025b591556dbfb" dependencies = [ "ahash", "crossbeam-queue", @@ -5952,9 +5953,9 @@ dependencies = [ [[package]] name = "spacetimedb-lib" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523a8d4a746bb4403fe3e5241e3a72204fc1358e3b118b4f827de7673b6aabcb" +checksum = "7d05a4bef4619afe949f7eac9c3a31f06f21c5139c89920a8f4c7dd7b09c24a8" dependencies = [ "anyhow", "bitflags 2.13.0", @@ -5977,9 +5978,9 @@ dependencies = [ [[package]] name = "spacetimedb-memory-usage" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfa4e78b522fc9ee6e5dbd49c579d42584d6d7d6ce91d02c30471c085265f7df" +checksum = "064ae4130ddc47e1b87b59252d041cf929ffbdc53686af80dff290f819e523ef" dependencies = [ "decorum", "ethnum", @@ -5987,9 +5988,9 @@ dependencies = [ [[package]] name = "spacetimedb-metrics" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226d91f133dcb792dd04ec3870828c4c1d7815a33646e8226894087f1680f8a9" +checksum = "65b36210b2cdde2784e22e7182717bf1ab06a431f54bf9662e38bd1bcb8681c5" dependencies = [ "arrayvec", "itertools 0.12.1", @@ -5999,9 +6000,9 @@ dependencies = [ [[package]] name = "spacetimedb-primitives" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f625481d6715f9b0aba612599be6c4ab1028ab99425d23d75a268a49628a43f8" +checksum = "9c1e4745d32aacb71d29050f1ff6fa24fa149d939b8787f90ec28d2cac3c107b" dependencies = [ "bitflags 2.13.0", "either", @@ -6013,18 +6014,18 @@ dependencies = [ [[package]] name = "spacetimedb-query-builder" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cf1d3fb9e170fbbfdf804414ffbb1aa4b4d913684cd68fd5b36ece561479b3c" +checksum = "79849bd28750b3351d54f3df4b37c5454cae3c62d0184bc9855650048a80c032" dependencies = [ "spacetimedb-lib", ] [[package]] name = "spacetimedb-sats" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "449ff63e22853eeaf903563f3cfaf8557ba0c84d0a62abcc388ac429670d321c" +checksum = "6ca3327e8cd735f347ef5ff08a61b9806418acdcc0394ddd35a212c0849e4295" dependencies = [ "anyhow", "arrayvec", @@ -6055,9 +6056,9 @@ dependencies = [ [[package]] name = "spacetimedb-schema" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e1d892e7d7fdaa297c565fba749f9a925fc102931187c98976c7c7ad97f80c" +checksum = "cb81b65cfa1da17f5f032f9566086670fb68a27f14f194a4dad76a21cc79f490" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -6086,9 +6087,9 @@ dependencies = [ [[package]] name = "spacetimedb-sdk" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115835ba9f558e43781aa6059247157f97f37d1968292bc866a03da906be4258" +checksum = "ceb50bcefcb70ae517c672a7ac21cbaf7affd44f0d9991968869eff566d83ba5" dependencies = [ "anymap3", "base64 0.21.7", @@ -6118,9 +6119,9 @@ dependencies = [ [[package]] name = "spacetimedb-sql-parser" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47a4ab48f838f62e93a593309963862494961339fd60b1555abe48792c6c50bb" +checksum = "86cbda18ee77e497b698b22377750228b1e88ead4dcb79fc3fddb78cccab0a77" dependencies = [ "derive_more", "spacetimedb-lib", diff --git a/server-rs/Cargo.toml b/server-rs/Cargo.toml index 64ce7ba85..fd22210ca 100644 --- a/server-rs/Cargo.toml +++ b/server-rs/Cargo.toml @@ -132,9 +132,9 @@ serde_urlencoded = "0.7" sha1 = "0.10" sha2 = "0.10" socket2 = "0.6" -spacetimedb = "=2.5.0" -spacetimedb-sdk = "=2.5.0" -spacetimedb-lib = { version = "=2.5.0", default-features = false } +spacetimedb = "=2.6.0" +spacetimedb-sdk = "=2.6.0" +spacetimedb-lib = { version = "=2.6.0", default-features = false } time = "0.3" tokio = "1" tokio-stream = "0.1" @@ -153,6 +153,7 @@ url = "2" urlencoding = "2" uuid = "1" webp = "0.3" +x509-parser = "0.16" zip = { version = "2", default-features = false } [profile.dev] diff --git a/server-rs/crates/api-server/src/editor_generation_queue.rs b/server-rs/crates/api-server/src/editor_generation_queue.rs index 3d6a24b52..e58ea8dc9 100644 --- a/server-rs/crates/api-server/src/editor_generation_queue.rs +++ b/server-rs/crates/api-server/src/editor_generation_queue.rs @@ -1,6 +1,6 @@ use axum::http::StatusCode; use serde::Serialize; -use serde_json::json; +use serde_json::{Value, json}; use shared_contracts::external_generation::{ ExternalGenerationJobStatus, ExternalGenerationJobStatusRecord, }; @@ -25,6 +25,7 @@ pub(crate) const EDITOR_BACKGROUND_MUSIC_GENERATION_JOB_KIND: &str = pub(crate) const EDITOR_GENERATION_QUEUE_SOURCE_MODULE: &str = "editor-canvas"; const EDITOR_GENERATION_QUEUE_PROVIDER: &str = "editor-generation-worker"; +const MAX_EDITOR_GENERATION_JOB_PAYLOAD_BYTES: usize = 512 * 1024; #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] @@ -46,12 +47,7 @@ where T: Serialize, { let job_id = build_prefixed_uuid_id("task-"); - let request_payload_json = serde_json::to_string(payload).map_err(|error| { - AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_details(json!({ - "provider": EDITOR_GENERATION_QUEUE_PROVIDER, - "message": format!("编辑器 worker 任务参数序列化失败:{error}"), - })) - })?; + let request_payload_json = serialize_editor_generation_job_payload(payload)?; let now_micros = current_utc_micros(); state .spacetime_client() @@ -78,6 +74,64 @@ where }) } +fn serialize_editor_generation_job_payload(payload: &T) -> Result +where + T: Serialize + ?Sized, +{ + let payload_value = serde_json::to_value(payload).map_err(payload_serialization_error)?; + if contains_inline_media_reference(&payload_value) { + return Err( + AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({ + "provider": EDITOR_GENERATION_QUEUE_PROVIDER, + "message": "编辑器生成任务参数禁止包含 data: 或 blob: 内联媒体引用,请先将媒体上传到对象存储并改传 objectKey 或 resourceId。", + })), + ); + } + + let request_payload_json = + serde_json::to_string(&payload_value).map_err(payload_serialization_error)?; + let payload_bytes = request_payload_json.len(); + if payload_bytes > MAX_EDITOR_GENERATION_JOB_PAYLOAD_BYTES { + return Err( + AppError::from_status(StatusCode::PAYLOAD_TOO_LARGE).with_details(json!({ + "provider": EDITOR_GENERATION_QUEUE_PROVIDER, + "message": format!( + "编辑器生成任务 JSON 大小为 {payload_bytes} 字节,超过持久化上限 {MAX_EDITOR_GENERATION_JOB_PAYLOAD_BYTES} 字节;请移除冗余数据并改传 objectKey 或 resourceId。" + ), + "actualBytes": payload_bytes, + "maxBytes": MAX_EDITOR_GENERATION_JOB_PAYLOAD_BYTES, + })), + ); + } + + Ok(request_payload_json) +} + +fn payload_serialization_error(error: serde_json::Error) -> AppError { + AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_details(json!({ + "provider": EDITOR_GENERATION_QUEUE_PROVIDER, + "message": format!("编辑器 worker 任务参数序列化失败:{error}"), + })) +} + +fn contains_inline_media_reference(value: &Value) -> bool { + match value { + Value::String(value) => is_inline_media_reference(value), + Value::Array(values) => values.iter().any(contains_inline_media_reference), + Value::Object(values) => values.iter().any(|(key, value)| { + is_inline_media_reference(key) || contains_inline_media_reference(value) + }), + Value::Null | Value::Bool(_) | Value::Number(_) => false, + } +} + +fn is_inline_media_reference(value: &str) -> bool { + let prefix = value.trim_start().as_bytes().get(..5); + prefix.is_some_and(|prefix| { + prefix.eq_ignore_ascii_case(b"data:") || prefix.eq_ignore_ascii_case(b"blob:") + }) +} + pub(crate) fn editor_generation_queue_state( job: ExternalGenerationJobRecord, ) -> ExternalGenerationJobStatusRecord { @@ -106,3 +160,68 @@ pub(crate) fn editor_generation_source_entity_id( fn current_utc_micros() -> i64 { offset_datetime_to_unix_micros(time::OffsetDateTime::now_utc()) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn serialize_payload_accepts_persistable_media_references() { + let payload = json!({ + "sourceImageObjectKey": "users/user-1/editor/source.png", + "resourceId": "resource-1", + "nested": [{ "prompt": "保留 data 与 blob 这两个普通单词" }], + }); + + let serialized = serialize_editor_generation_job_payload(&payload) + .expect("objectKey 和 resourceId 应允许进入持久任务 JSON"); + + assert_eq!( + serde_json::from_str::(&serialized).expect("应生成有效 JSON"), + payload + ); + } + + #[test] + fn serialize_payload_rejects_nested_data_url_case_insensitively() { + let payload = json!({ + "input": { + "references": [ + { "url": " \nDaTa:image/png;base64,AAAA" } + ] + } + }); + + let error = serialize_editor_generation_job_payload(&payload) + .expect_err("任意层级的 Data URL 都必须被拒绝"); + + assert_eq!(error.status_code(), StatusCode::BAD_REQUEST); + assert!(error.body_text().contains("禁止包含 data: 或 blob:")); + } + + #[test] + fn serialize_payload_rejects_nested_blob_url_case_insensitively() { + let payload = json!({ + "input": [{ "source": { "url": "\tBLOB:https://example.test/id" } }] + }); + + let error = serialize_editor_generation_job_payload(&payload) + .expect_err("任意层级的 Blob URL 都必须被拒绝"); + + assert_eq!(error.status_code(), StatusCode::BAD_REQUEST); + assert!(error.body_text().contains("禁止包含 data: 或 blob:")); + } + + #[test] + fn serialize_payload_rejects_json_larger_than_persistence_limit() { + let payload = json!({ + "prompt": "x".repeat(MAX_EDITOR_GENERATION_JOB_PAYLOAD_BYTES), + }); + + let error = serialize_editor_generation_job_payload(&payload) + .expect_err("超过上限的任务 JSON 必须被拒绝"); + + assert_eq!(error.status_code().as_u16(), 413); + assert!(error.body_text().contains("超过持久化上限")); + } +} diff --git a/server-rs/crates/api-server/src/editor_project.rs b/server-rs/crates/api-server/src/editor_project.rs index 233aab6c9..e13c8f1da 100644 --- a/server-rs/crates/api-server/src/editor_project.rs +++ b/server-rs/crates/api-server/src/editor_project.rs @@ -1913,6 +1913,148 @@ fn editor_generation_provider_image_size_for_model( image_size } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct EditorImageEditDimensions { + target_width: u32, + target_height: u32, + provider_width: u32, + provider_height: u32, +} + +fn align_editor_image_edit_dimension(value: u32) -> u32 { + value.saturating_add(15) / 16 * 16 +} + +fn encode_editor_image_edit_png( + image: image::DynamicImage, + failure_status: StatusCode, + failure_message: &str, +) -> Result, AppError> { + let mut bytes = Cursor::new(Vec::new()); + image + .write_to(&mut bytes, image::ImageFormat::Png) + .map_err(|error| { + AppError::from_status(failure_status).with_details(json!({ + "provider": "editor-image-edit", + "message": format!("{failure_message}:{error}"), + })) + })?; + Ok(bytes.into_inner()) +} + +fn prepare_editor_image_edit_references( + reference_images: &mut [OpenAiReferenceImage], + target_size: &str, +) -> Result { + let (target_width, target_height) = target_size.split_once('x').ok_or_else(|| { + AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({ + "provider": "editor-image-edit", + "message": "图片改造目标尺寸无效", + })) + })?; + let target_width = target_width.parse::().map_err(|_| { + AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({ + "provider": "editor-image-edit", + "message": "图片改造目标宽度无效", + })) + })?; + let target_height = target_height.parse::().map_err(|_| { + AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({ + "provider": "editor-image-edit", + "message": "图片改造目标高度无效", + })) + })?; + let provider_width = align_editor_image_edit_dimension(target_width); + let provider_height = align_editor_image_edit_dimension(target_height); + if reference_images.is_empty() { + return Err( + AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({ + "provider": "editor-image-edit", + "message": "图片改造缺少原图", + })), + ); + } + for (index, reference) in reference_images.iter_mut().enumerate() { + let decoded = image::load_from_memory(reference.bytes.as_slice()).map_err(|error| { + AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({ + "provider": "editor-image-edit", + "message": format!("图片改造参考图不是有效图片:{error}"), + "referenceIndex": index, + })) + })?; + let width = decoded.width(); + let height = decoded.height(); + let reference_provider_width = align_editor_image_edit_dimension(width); + let reference_provider_height = align_editor_image_edit_dimension(height); + if reference_provider_width == width && reference_provider_height == height { + continue; + } + + // 中文注释:只在 provider 边界向右、向下复制边缘像素补齐到 16 倍数, + // 不缩放、不裁切用户原图;生成结果持久化前会恢复到业务目标精确尺寸。 + let source = decoded.to_rgba8(); + let mut aligned = + image::RgbaImage::new(reference_provider_width, reference_provider_height); + for y in 0..reference_provider_height { + let source_y = y.min(height.saturating_sub(1)); + for x in 0..reference_provider_width { + let source_x = x.min(width.saturating_sub(1)); + aligned.put_pixel(x, y, *source.get_pixel(source_x, source_y)); + } + } + reference.bytes = encode_editor_image_edit_png( + image::DynamicImage::ImageRgba8(aligned), + StatusCode::BAD_REQUEST, + "图片改造参考图 16 对齐失败", + )?; + reference.mime_type = "image/png".to_string(); + reference.file_name = format!("editor-image-edit-reference-{}.png", index + 1); + } + + Ok(EditorImageEditDimensions { + target_width, + target_height, + provider_width, + provider_height, + }) +} + +fn restore_editor_image_edit_output_dimensions( + output: DownloadedOpenAiImage, + dimensions: &EditorImageEditDimensions, +) -> Result { + let decoded = image::load_from_memory(output.bytes.as_slice()).map_err(|error| { + AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({ + "provider": "vector-engine", + "message": format!("图片改造结果不是有效图片:{error}"), + })) + })?; + if decoded.width() == dimensions.target_width && decoded.height() == dimensions.target_height { + return Ok(output); + } + + let restored = if decoded.width() == dimensions.provider_width + && decoded.height() == dimensions.provider_height + { + decoded.crop_imm(0, 0, dimensions.target_width, dimensions.target_height) + } else { + decoded.resize_to_fill( + dimensions.target_width, + dimensions.target_height, + image::imageops::FilterType::Lanczos3, + ) + }; + Ok(DownloadedOpenAiImage { + bytes: encode_editor_image_edit_png( + restored, + StatusCode::BAD_GATEWAY, + "恢复图片改造结果目标尺寸失败", + )?, + mime_type: "image/png".to_string(), + extension: "png".to_string(), + }) +} + pub async fn edit_editor_image( State(state): State, Extension(request_context): Extension, @@ -1968,7 +2110,7 @@ pub(crate) async fn edit_editor_image_for_owner( } let generation_options = normalize_editor_generation_options(payload.model.as_deref(), None, None); - let image_size = normalize_editor_image_generation_size(payload.size.as_deref()); + let requested_image_size = normalize_editor_image_generation_size(payload.size.as_deref()); let mut reference_images = Vec::with_capacity(1 + payload.reference_image_srcs.as_ref().map_or(0, Vec::len)); reference_images.push( @@ -1986,10 +2128,6 @@ pub(crate) async fn edit_editor_image_for_owner( parse_editor_reference_image(state, caller.owner_user_id.as_str(), source).await?, ); } - let expected_price_mud_points = - resolve_editor_image_edit_price(state, generation_options.model, image_size.as_ref()) - .await?; - let settings = require_openai_image_settings(state)?.with_external_api_audit_context( request_context, caller.audit_subject_user_id.clone(), @@ -1999,6 +2137,15 @@ pub(crate) async fn edit_editor_image_for_owner( .or_else(|| payload.project_id.clone()), ); let http_client = build_openai_image_http_client(&settings)?; + let edit_dimensions = + prepare_editor_image_edit_references(&mut reference_images, requested_image_size.as_ref())?; + let image_size = format!( + "{}x{}", + edit_dimensions.provider_width, edit_dimensions.provider_height + ); + let expected_price_mud_points = + resolve_editor_image_edit_price(state, generation_options.model, image_size.as_ref()) + .await?; let generated = execute_billable_asset_operation_with_cost( state, caller.owner_user_id.as_str(), @@ -2024,6 +2171,7 @@ pub(crate) async fn edit_editor_image_for_owner( "message": "VectorEngine 未返回图片", })) })?; + let image = restore_editor_image_edit_output_dimensions(image, &edit_dimensions)?; let (width, height) = image::load_from_memory(image.bytes.as_slice()) .map(|image| (image.width(), image.height())) .unwrap_or((1024, 1024)); @@ -6465,6 +6613,59 @@ mod tests { ); } + #[test] + fn editor_image_edit_aligns_provider_images_and_restores_source_dimensions() { + fn encode_png(width: u32, height: u32) -> Vec { + let image = image::DynamicImage::new_rgba8(width, height); + let mut bytes = Cursor::new(Vec::new()); + image + .write_to(&mut bytes, image::ImageFormat::Png) + .expect("test image should encode"); + bytes.into_inner() + } + + let mut references = vec![ + OpenAiReferenceImage { + bytes: encode_png(1537, 1025), + mime_type: "image/png".to_string(), + file_name: "source.png".to_string(), + }, + OpenAiReferenceImage { + bytes: encode_png(501, 333), + mime_type: "image/png".to_string(), + file_name: "style.png".to_string(), + }, + ]; + + let dimensions = prepare_editor_image_edit_references(&mut references, "1537x1025") + .expect("references should align for provider"); + assert_eq!(dimensions.target_width, 1537); + assert_eq!(dimensions.target_height, 1025); + assert_eq!(dimensions.provider_width, 1552); + assert_eq!(dimensions.provider_height, 1040); + let source = image::load_from_memory(references[0].bytes.as_slice()).unwrap(); + assert_eq!((source.width(), source.height()), (1552, 1040)); + let extra = image::load_from_memory(references[1].bytes.as_slice()).unwrap(); + assert_eq!((extra.width(), extra.height()), (512, 336)); + + let restored = restore_editor_image_edit_output_dimensions( + DownloadedOpenAiImage { + bytes: encode_png(1552, 1040), + mime_type: "image/png".to_string(), + extension: "png".to_string(), + }, + &dimensions, + ) + .expect("provider output should restore source dimensions"); + let restored_image = image::load_from_memory(restored.bytes.as_slice()).unwrap(); + assert_eq!( + (restored_image.width(), restored_image.height()), + (1537, 1025) + ); + assert_eq!(restored.mime_type, "image/png"); + assert_eq!(restored.extension, "png"); + } + #[test] fn editor_generation_dimensions_follow_model_options() { let default_generation = normalize_editor_generation_options(None, Some("1:1"), Some("1K")); diff --git a/server-rs/crates/api-server/src/external_generation.rs b/server-rs/crates/api-server/src/external_generation.rs index 0757cbc5a..5d3bacc4b 100644 --- a/server-rs/crates/api-server/src/external_generation.rs +++ b/server-rs/crates/api-server/src/external_generation.rs @@ -5,7 +5,7 @@ use axum::{ response::Response, }; use serde::Deserialize; -use serde_json::{Value, json}; +use serde_json::json; use shared_contracts::external_generation::{ ExternalGenerationJobStatus, ExternalGenerationJobStatusRecord, ExternalGenerationJobStatusResponse, ExternalGenerationQueueOverview, @@ -15,8 +15,8 @@ use shared_contracts::external_generation::{ }; use spacetime_client::{ ExternalGenerationJobAcknowledgeRecordInput, ExternalGenerationJobGetRecordInput, - ExternalGenerationJobListRecord, ExternalGenerationJobListRecordInput, - ExternalGenerationJobRecord, SpacetimeClientError, + ExternalGenerationJobListRecordInput, ExternalGenerationJobSummaryListRecord, + ExternalGenerationJobSummaryRecord, SpacetimeClientError, }; use crate::{ @@ -42,7 +42,7 @@ pub async fn get_external_generation_queue_overview( let owner_user_id = authenticated.claims().user_id().to_string(); let list = state .spacetime_client() - .list_external_generation_jobs(ExternalGenerationJobListRecordInput { + .list_external_generation_job_summaries(ExternalGenerationJobListRecordInput { owner_user_id, limit: 1, include_acknowledged_terminal: false, @@ -71,7 +71,7 @@ pub async fn list_external_generation_tasks( let statuses = external_generation_status_filter_input(&status_filter); let list = state .spacetime_client() - .list_external_generation_jobs(ExternalGenerationJobListRecordInput { + .list_external_generation_job_summaries(ExternalGenerationJobListRecordInput { owner_user_id, limit: requested_limit, include_acknowledged_terminal: query.include_acknowledged_terminal.unwrap_or(false), @@ -105,11 +105,13 @@ pub async fn acknowledge_external_generation_tasks( let owner_user_id = authenticated.claims().user_id().to_string(); let acknowledged = state .spacetime_client() - .acknowledge_external_generation_jobs(ExternalGenerationJobAcknowledgeRecordInput { - owner_user_id, - job_ids: payload.job_ids, - acknowledged_at_micros: current_utc_micros(), - }) + .acknowledge_external_generation_job_summaries( + ExternalGenerationJobAcknowledgeRecordInput { + owner_user_id, + job_ids: payload.job_ids, + acknowledged_at_micros: current_utc_micros(), + }, + ) .await .map_err(|error| external_generation_error_response(&request_context, error))?; @@ -134,7 +136,7 @@ pub async fn get_external_generation_job_status( let owner_user_id = authenticated.claims().user_id().to_string(); let job = state .spacetime_client() - .get_external_generation_job(ExternalGenerationJobGetRecordInput { + .get_external_generation_job_summary(ExternalGenerationJobGetRecordInput { job_id, owner_user_id, }) @@ -150,7 +152,7 @@ pub async fn get_external_generation_job_status( } fn map_external_generation_queue_overview( - list: &ExternalGenerationJobListRecord, + list: &ExternalGenerationJobSummaryListRecord, ) -> ExternalGenerationQueueOverview { ExternalGenerationQueueOverview { pending_count: list.pending_count, @@ -161,7 +163,7 @@ fn map_external_generation_queue_overview( } fn map_external_generation_job_status( - job: ExternalGenerationJobRecord, + job: ExternalGenerationJobSummaryRecord, ) -> ExternalGenerationJobStatusRecord { let (status, phase_detail, progress) = match job.status.as_str() { "completed" => (ExternalGenerationJobStatus::Completed, "生成已完成。", 100), @@ -182,17 +184,16 @@ fn map_external_generation_job_status( } fn map_external_generation_task_record( - job: ExternalGenerationJobRecord, + job: ExternalGenerationJobSummaryRecord, ) -> ExternalGenerationTaskRecord { let status_record = map_external_generation_job_status(job.clone()); - let request_prompt = extract_external_generation_request_prompt(&job.request_payload_json); ExternalGenerationTaskRecord { job_id: job.job_id, job_kind: job.job_kind, source_module: job.source_module, source_entity_id: job.source_entity_id, request_label: job.request_label, - request_prompt, + request_prompt: job.request_prompt, status: status_record.status, phase_label: status_record.phase_label, phase_detail: status_record.phase_detail, @@ -209,59 +210,6 @@ fn map_external_generation_task_record( } } -fn extract_external_generation_request_prompt(request_payload_json: &str) -> Option { - let payload: Value = serde_json::from_str(request_payload_json).ok()?; - for key in ["prompt", "promptText", "spritesheetLabel"] { - if let Some(prompt) = payload - .get(key) - .and_then(Value::as_str) - .and_then(normalize_external_generation_prompt_text) - { - return Some(prompt); - } - } - if let Some(prompt) = payload - .get("iconDescriptions") - .and_then(Value::as_array) - .map(|items| { - items - .iter() - .filter_map(Value::as_str) - .filter_map(normalize_external_generation_prompt_text) - .collect::>() - .join("、") - }) - .and_then(|value| normalize_external_generation_prompt_text(&value)) - { - return Some(prompt); - } - payload - .get("generationInputs") - .and_then(|value| value.get("fields")) - .and_then(Value::as_array) - .and_then(|fields| { - fields.iter().find_map(|field| { - let title = field.get("title").and_then(Value::as_str)?.trim(); - if !matches!(title, "prompt" | "gpt_description_prompt") { - return None; - } - field - .get("value") - .and_then(Value::as_str) - .and_then(normalize_external_generation_prompt_text) - }) - }) -} - -fn normalize_external_generation_prompt_text(value: &str) -> Option { - let trimmed = value.trim(); - if trimmed.is_empty() { - None - } else { - Some(trimmed.to_string()) - } -} - fn parse_external_generation_status_filter( statuses: Option<&str>, ) -> Vec { @@ -325,20 +273,29 @@ mod tests { } #[test] - fn extracts_external_generation_request_prompt_from_payload() { - assert_eq!( - extract_external_generation_request_prompt( - r#"{"prompt":" 发光主视觉 ","sourceImageSrc":"data:image/png;base64,secret"}"#, - ) - .as_deref(), - Some("发光主视觉"), - ); - assert_eq!( - extract_external_generation_request_prompt( - r#"{"iconDescriptions":["返回按钮"," 设置按钮 "],"referenceImageSrc":"data:image/png;base64,secret"}"#, - ) - .as_deref(), - Some("返回按钮、设置按钮"), - ); + fn maps_task_from_payload_free_summary_projection() { + let task = map_external_generation_task_record(ExternalGenerationJobSummaryRecord { + job_id: "task-1".to_string(), + job_kind: "editor_image_generation".to_string(), + owner_user_id: "user-1".to_string(), + source_module: "editor-canvas".to_string(), + source_entity_id: "project-1".to_string(), + request_label: "图片生成".to_string(), + request_prompt: Some("发光主视觉".to_string()), + status: "completed".to_string(), + last_error_message: None, + created_at: "2026-07-10T08:00:00Z".to_string(), + started_at: Some("2026-07-10T08:00:01Z".to_string()), + completed_at: Some("2026-07-10T08:00:10Z".to_string()), + updated_at: "2026-07-10T08:00:10Z".to_string(), + updated_at_micros: 1_000, + price_mud_points: 4, + refund_ledger_id: None, + notification_acknowledged_at: None, + notification_acknowledged_at_micros: None, + }); + + assert_eq!(task.request_prompt.as_deref(), Some("发光主视觉")); + assert_eq!(task.status, ExternalGenerationJobStatus::Completed); } } diff --git a/server-rs/crates/api-server/src/profile_recharge_expiration_listener.rs b/server-rs/crates/api-server/src/profile_recharge_expiration_listener.rs index 370c0de27..976870934 100644 --- a/server-rs/crates/api-server/src/profile_recharge_expiration_listener.rs +++ b/server-rs/crates/api-server/src/profile_recharge_expiration_listener.rs @@ -38,7 +38,22 @@ async fn run_profile_recharge_expiration_listener(state: AppState) { loop { match subscription.recv().await { - Ok(order) => { + Ok(order_id) => { + let order = match state + .spacetime_client() + .get_profile_recharge_order(order_id.clone()) + .await + { + Ok((_, order)) => order, + Err(error) => { + warn!( + order_id, + error = %error, + "profile recharge expiration listener failed to read signaled order" + ); + break; + } + }; let state = state.clone(); tokio::spawn(async move { process_expired_profile_recharge_order_with_retries(state, order) diff --git a/server-rs/crates/api-server/src/runtime_profile.rs b/server-rs/crates/api-server/src/runtime_profile.rs index 0593e9507..10601bf1d 100644 --- a/server-rs/crates/api-server/src/runtime_profile.rs +++ b/server-rs/crates/api-server/src/runtime_profile.rs @@ -9,22 +9,23 @@ use axum::{ }; use hmac::{Hmac, Mac}; use module_runtime::{ - AnalyticsGranularity, PROFILE_RECHARGE_PAYMENT_CHANNEL_MOCK, + AnalyticsGranularity, PROFILE_DAILY_FREE_POINTS_PER_DAY, PROFILE_RECHARGE_PAYMENT_CHANNEL_MOCK, PROFILE_RECHARGE_PAYMENT_CHANNEL_WECHAT_H5, PROFILE_RECHARGE_PAYMENT_CHANNEL_WECHAT_JSAPI, PROFILE_RECHARGE_PAYMENT_CHANNEL_WECHAT_MINI_PROGRAM, PROFILE_RECHARGE_PAYMENT_CHANNEL_WECHAT_MINI_PROGRAM_VIRTUAL, PROFILE_RECHARGE_PAYMENT_CHANNEL_WECHAT_NATIVE, RuntimeProfileCodeOperationRecord, - RuntimeProfileFeedbackEvidenceRecord, RuntimeProfileFeedbackEvidenceSnapshot, - RuntimeProfileFeedbackSubmissionRecord, RuntimeProfileInviteCodeRecord, - RuntimeProfileMembershipBenefitRecord, RuntimeProfileMembershipTier, - RuntimeProfileRechargeCenterRecord, RuntimeProfileRechargeOrderRecord, - RuntimeProfileRechargeOrderStatus, RuntimeProfileRechargeProductConfigRecord, - RuntimeProfileRechargeProductKind, RuntimeProfileRechargeProductRecord, - RuntimeProfileRedeemCodeMode, RuntimeProfileRedeemCodeRecord, - RuntimeProfileRewardCodeRedeemRecord, RuntimeProfileTaskCenterRecord, - RuntimeProfileTaskClaimRecord, RuntimeProfileTaskConfigRecord, RuntimeProfileTaskCycle, - RuntimeProfileTaskItemRecord, RuntimeProfileTaskStatus, RuntimeProfileWalletLedgerSourceType, - RuntimeReferralInviteCenterRecord, RuntimeTrackingScopeKind, + RuntimeProfileDailyFreePointsRecord, RuntimeProfileFeedbackEvidenceRecord, + RuntimeProfileFeedbackEvidenceSnapshot, RuntimeProfileFeedbackSubmissionRecord, + RuntimeProfileInviteCodeRecord, RuntimeProfileMembershipBenefitRecord, + RuntimeProfileMembershipTier, RuntimeProfileRechargeCenterRecord, + RuntimeProfileRechargeOrderRecord, RuntimeProfileRechargeOrderStatus, + RuntimeProfileRechargeProductConfigRecord, RuntimeProfileRechargeProductKind, + RuntimeProfileRechargeProductRecord, RuntimeProfileRedeemCodeMode, + RuntimeProfileRedeemCodeRecord, RuntimeProfileRewardCodeRedeemRecord, + RuntimeProfileTaskCenterRecord, RuntimeProfileTaskClaimRecord, RuntimeProfileTaskConfigRecord, + RuntimeProfileTaskCycle, RuntimeProfileTaskItemRecord, RuntimeProfileTaskStatus, + RuntimeProfileWalletLedgerSourceType, RuntimeReferralInviteCenterRecord, + RuntimeTrackingScopeKind, }; use platform_wechat::pay::{WechatPayError, WechatPayNotifyOrder}; use serde::{Deserialize, Serialize}; @@ -47,6 +48,8 @@ use shared_contracts::runtime::{ PROFILE_TASK_STATUS_DISABLED, PROFILE_TASK_STATUS_INCOMPLETE, PROFILE_WALLET_LEDGER_SOURCE_TYPE_ASSET_OPERATION_CONSUME, PROFILE_WALLET_LEDGER_SOURCE_TYPE_ASSET_OPERATION_REFUND, + PROFILE_WALLET_LEDGER_SOURCE_TYPE_DAILY_FREE_GRANT, + PROFILE_WALLET_LEDGER_SOURCE_TYPE_DAILY_FREE_RESET, PROFILE_WALLET_LEDGER_SOURCE_TYPE_DAILY_TASK_REWARD, PROFILE_WALLET_LEDGER_SOURCE_TYPE_INVITE_INVITEE_REWARD, PROFILE_WALLET_LEDGER_SOURCE_TYPE_INVITE_INVITER_REWARD, @@ -57,9 +60,10 @@ use shared_contracts::runtime::{ PROFILE_WALLET_LEDGER_SOURCE_TYPE_PUZZLE_AUTHOR_INCENTIVE_CLAIM, PROFILE_WALLET_LEDGER_SOURCE_TYPE_REDEEM_CODE_REWARD, PROFILE_WALLET_LEDGER_SOURCE_TYPE_SNAPSHOT_SYNC, ProfileCodeOperationAdminResponse, - ProfileDashboardSummaryResponse, ProfileFeedbackEvidenceItemResponse, - ProfileFeedbackSubmissionResponse, ProfileInviteCodeAdminListResponse, - ProfileInviteCodeAdminResponse, ProfileMembershipBenefitResponse, ProfileMembershipResponse, + ProfileDailyFreePointsResponse, ProfileDashboardSummaryResponse, + ProfileFeedbackEvidenceItemResponse, ProfileFeedbackSubmissionResponse, + ProfileInviteCodeAdminListResponse, ProfileInviteCodeAdminResponse, + ProfileMembershipBenefitResponse, ProfileMembershipResponse, ProfileMudPointBalanceResponse, ProfilePlayStatsResponse, ProfilePlayedWorkSummaryResponse, ProfileRechargeCenterResponse, ProfileRechargeOrderResponse, ProfileRechargeProductConfigAdminListResponse, ProfileRechargeProductConfigAdminResponse, ProfileRechargeProductResponse, @@ -119,6 +123,7 @@ pub async fn get_profile_dashboard( total_play_time_ms: record.total_play_time_ms, played_world_count: record.played_world_count, updated_at: record.updated_at, + daily_free_points: build_profile_daily_free_points_response(record.daily_free_points), }, )) } @@ -191,6 +196,12 @@ fn format_profile_wallet_ledger_source_type( RuntimeProfileWalletLedgerSourceType::DailyTaskReward => { PROFILE_WALLET_LEDGER_SOURCE_TYPE_DAILY_TASK_REWARD } + RuntimeProfileWalletLedgerSourceType::DailyFreeGrant => { + PROFILE_WALLET_LEDGER_SOURCE_TYPE_DAILY_FREE_GRANT + } + RuntimeProfileWalletLedgerSourceType::DailyFreeReset => { + PROFILE_WALLET_LEDGER_SOURCE_TYPE_DAILY_FREE_RESET + } } } @@ -1635,8 +1646,10 @@ fn paid_at_micros_from_wechat_order(order: &WechatPayNotifyOrder) -> i64 { fn build_profile_recharge_center_response( record: RuntimeProfileRechargeCenterRecord, ) -> ProfileRechargeCenterResponse { + let mud_point_balance = build_profile_mud_point_balance_response(&record); ProfileRechargeCenterResponse { wallet_balance: record.wallet_balance, + mud_point_balance, membership: ProfileMembershipResponse { status: record.membership.status.as_str().to_string(), tier: record.membership.tier.as_str().to_string(), @@ -1668,6 +1681,49 @@ fn build_profile_recharge_center_response( .latest_order .map(build_profile_recharge_order_response), has_points_recharged: record.has_points_recharged, + daily_free_points: build_profile_daily_free_points_response(record.daily_free_points), + } +} + +fn build_profile_mud_point_balance_response( + record: &RuntimeProfileRechargeCenterRecord, +) -> ProfileMudPointBalanceResponse { + let daily_free_points = record.daily_free_points.remaining_points; + let limited_points = record.membership.cycle_remaining_points; + let permanent_points = record + .wallet_balance + .saturating_sub(daily_free_points) + .saturating_sub(limited_points); + let limited_expires_at = (limited_points > 0) + .then(|| { + record + .membership + .cycle_resets_at + .clone() + .or_else(|| record.membership.expires_at.clone()) + }) + .flatten(); + + ProfileMudPointBalanceResponse { + total_points: record.wallet_balance, + permanent_points, + limited_points, + limited_expires_at, + daily_free_points, + daily_free_reset_points: PROFILE_DAILY_FREE_POINTS_PER_DAY, + daily_free_resets_at: record.daily_free_points.resets_at.clone(), + } +} + +fn build_profile_daily_free_points_response( + record: RuntimeProfileDailyFreePointsRecord, +) -> ProfileDailyFreePointsResponse { + ProfileDailyFreePointsResponse { + day_key: record.day_key, + granted_points: record.granted_points, + remaining_points: record.remaining_points, + resets_at: record.resets_at, + updated_at: record.updated_at, } } @@ -2152,15 +2208,16 @@ mod tests { use module_auth::{ResolveWechatLoginInput, WechatIdentityProfile}; use module_runtime::{ PROFILE_RECHARGE_PAYMENT_CHANNEL_WECHAT_MINI_PROGRAM_VIRTUAL, - RuntimeProfileMembershipRecord, RuntimeProfileMembershipStatus, - RuntimeProfileMembershipTier, RuntimeProfileRechargeCenterRecord, - RuntimeProfileRechargeOrderRecord, RuntimeProfileRechargeOrderStatus, - RuntimeProfileRechargeProductKind, RuntimeProfileRechargeProductRecord, - RuntimeProfileWalletLedgerSourceType, + RuntimeProfileDailyFreePointsRecord, RuntimeProfileMembershipRecord, + RuntimeProfileMembershipStatus, RuntimeProfileMembershipTier, + RuntimeProfileRechargeCenterRecord, RuntimeProfileRechargeOrderRecord, + RuntimeProfileRechargeOrderStatus, RuntimeProfileRechargeProductKind, + RuntimeProfileRechargeProductRecord, RuntimeProfileWalletLedgerSourceType, }; use super::{ - build_wechat_virtual_pay_params, calc_wechat_virtual_payment_pay_signature_with_key, + build_profile_mud_point_balance_response, build_wechat_virtual_pay_params, + calc_wechat_virtual_payment_pay_signature_with_key, calc_wechat_virtual_payment_user_signature_with_key, format_profile_wallet_ledger_source_type, is_wechat_profile_recharge_order_terminal_for_confirmation, @@ -2182,6 +2239,63 @@ mod tests { use crate::{app::build_router, config::AppConfig, state::AppState}; + fn daily_free_points_record() -> RuntimeProfileDailyFreePointsRecord { + RuntimeProfileDailyFreePointsRecord { + day_key: 20_646, + granted_points: 20, + remaining_points: 20, + resets_at: "2026-07-12T16:00:00Z".to_string(), + resets_at_micros: 1_783_872_000_000_000, + updated_at: "2026-07-12T08:00:00Z".to_string(), + updated_at_micros: 1_783_843_200_000_000, + } + } + + #[test] + fn mud_point_balance_is_split_by_backend_wallet_buckets() { + let record = RuntimeProfileRechargeCenterRecord { + user_id: "user-1".to_string(), + wallet_balance: 200, + membership: RuntimeProfileMembershipRecord { + user_id: "user-1".to_string(), + status: RuntimeProfileMembershipStatus::Active, + tier: RuntimeProfileMembershipTier::Starter, + started_at: Some("2026-07-01T00:00:00Z".to_string()), + started_at_micros: Some(1_783_036_800_000_000), + expires_at: Some("2026-08-01T00:00:00Z".to_string()), + expires_at_micros: Some(1_785_542_400_000_000), + updated_at: Some("2026-07-12T08:00:00Z".to_string()), + updated_at_micros: Some(1_783_843_200_000_000), + cycle_started_at: Some("2026-07-08T00:00:00Z".to_string()), + cycle_started_at_micros: Some(1_783_468_800_000_000), + cycle_resets_at: Some("2026-07-15T00:00:00Z".to_string()), + cycle_resets_at_micros: Some(1_784_073_600_000_000), + cycle_granted_points: 80, + cycle_remaining_points: 80, + cycle_period_days: 7, + }, + point_products: vec![], + membership_products: vec![], + benefits: vec![], + latest_order: None, + has_points_recharged: false, + daily_free_points: daily_free_points_record(), + }; + + let balance = build_profile_mud_point_balance_response(&record); + + assert_eq!(balance.total_points, 200); + assert_eq!(balance.permanent_points, 100); + assert_eq!(balance.limited_points, 80); + assert_eq!(balance.daily_free_points, 20); + assert_eq!(balance.daily_free_reset_points, 20); + assert_eq!( + balance.limited_expires_at.as_deref(), + Some("2026-07-15T00:00:00Z"), + ); + assert_eq!(balance.daily_free_resets_at, "2026-07-12T16:00:00Z"); + } + #[test] fn profile_wallet_ledger_source_type_formats_backend_values() { assert_eq!( @@ -2214,6 +2328,18 @@ mod tests { ), shared_contracts::runtime::PROFILE_WALLET_LEDGER_SOURCE_TYPE_DAILY_TASK_REWARD ); + assert_eq!( + format_profile_wallet_ledger_source_type( + RuntimeProfileWalletLedgerSourceType::DailyFreeGrant + ), + shared_contracts::runtime::PROFILE_WALLET_LEDGER_SOURCE_TYPE_DAILY_FREE_GRANT + ); + assert_eq!( + format_profile_wallet_ledger_source_type( + RuntimeProfileWalletLedgerSourceType::DailyFreeReset + ), + shared_contracts::runtime::PROFILE_WALLET_LEDGER_SOURCE_TYPE_DAILY_FREE_RESET + ); } #[tokio::test] @@ -2983,6 +3109,7 @@ mod tests { benefits: vec![], latest_order: None, has_points_recharged: false, + daily_free_points: daily_free_points_record(), }; let params = @@ -3098,6 +3225,7 @@ mod tests { benefits: vec![], latest_order: None, has_points_recharged: true, + daily_free_points: daily_free_points_record(), }; let params = @@ -3211,6 +3339,7 @@ mod tests { benefits: vec![], latest_order: None, has_points_recharged: false, + daily_free_points: daily_free_points_record(), }; let params = build_wechat_virtual_pay_params(&state, ¢er, &order, "openid-user-item01") @@ -3389,6 +3518,7 @@ mod tests { benefits: vec![], latest_order: None, has_points_recharged: false, + daily_free_points: daily_free_points_record(), }; let error = build_wechat_virtual_pay_params(&state, ¢er, &order, "openid-sandbox-1") diff --git a/server-rs/crates/module-runtime/src/application.rs b/server-rs/crates/module-runtime/src/application.rs index cb8bf1f6f..a123970e4 100644 --- a/server-rs/crates/module-runtime/src/application.rs +++ b/server-rs/crates/module-runtime/src/application.rs @@ -981,6 +981,23 @@ pub fn build_runtime_profile_dashboard_record( played_world_count: snapshot.played_world_count, updated_at: snapshot.updated_at_micros.map(format_utc_micros), updated_at_micros: snapshot.updated_at_micros, + daily_free_points: build_runtime_profile_daily_free_points_record( + snapshot.daily_free_points, + ), + } +} + +pub fn build_runtime_profile_daily_free_points_record( + snapshot: RuntimeProfileDailyFreePointsSnapshot, +) -> RuntimeProfileDailyFreePointsRecord { + RuntimeProfileDailyFreePointsRecord { + day_key: snapshot.day_key, + granted_points: snapshot.granted_points, + remaining_points: snapshot.remaining_points, + resets_at: format_utc_micros(snapshot.resets_at_micros), + resets_at_micros: snapshot.resets_at_micros, + updated_at: format_utc_micros(snapshot.updated_at_micros), + updated_at_micros: snapshot.updated_at_micros, } } @@ -1047,6 +1064,9 @@ pub fn build_runtime_profile_recharge_center_record( .latest_order .map(build_runtime_profile_recharge_order_record), has_points_recharged: snapshot.has_points_recharged, + daily_free_points: build_runtime_profile_daily_free_points_record( + snapshot.daily_free_points, + ), } } diff --git a/server-rs/crates/module-runtime/src/domain.rs b/server-rs/crates/module-runtime/src/domain.rs index 602ce1f7b..215b80ea1 100644 --- a/server-rs/crates/module-runtime/src/domain.rs +++ b/server-rs/crates/module-runtime/src/domain.rs @@ -31,6 +31,7 @@ pub const PROFILE_TASK_EVENT_KEY_DAILY_LOGIN: &str = "daily_login"; pub const PROFILE_TASK_DEFAULT_TITLE_DAILY_LOGIN: &str = "每日登录"; pub const PROFILE_TASK_DEFAULT_REWARD_POINTS: u64 = 10; pub const PROFILE_TASK_DEFAULT_THRESHOLD: u32 = 1; +pub const PROFILE_DAILY_FREE_POINTS_PER_DAY: u64 = 20; pub const SAVE_SNAPSHOT_VERSION: u32 = 2; pub const DEFAULT_SAVE_ARCHIVE_SUMMARY_TEXT: &str = "继续推进上一次保存的故事。"; pub const PROFILE_RECHARGE_PAYMENT_CHANNEL_MOCK: &str = "mock"; @@ -674,6 +675,17 @@ pub struct RuntimeProfileDashboardSnapshot { pub total_play_time_ms: u64, pub played_world_count: u32, pub updated_at_micros: Option, + pub daily_free_points: RuntimeProfileDailyFreePointsSnapshot, +} + +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeProfileDailyFreePointsSnapshot { + pub day_key: i64, + pub granted_points: u64, + pub remaining_points: u64, + pub resets_at_micros: i64, + pub updated_at_micros: i64, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] @@ -1077,6 +1089,8 @@ pub enum RuntimeProfileWalletLedgerSourceType { DailyTaskReward, MembershipPeriodGrant, MembershipPeriodReset, + DailyFreeGrant, + DailyFreeReset, } impl RuntimeProfileWalletLedgerSourceType { @@ -1094,6 +1108,8 @@ impl RuntimeProfileWalletLedgerSourceType { Self::RedeemCodeReward => "redeem_code_reward", Self::PuzzleAuthorIncentiveClaim => "puzzle_author_incentive_claim", Self::DailyTaskReward => "daily_task_reward", + Self::DailyFreeGrant => "daily_free_grant", + Self::DailyFreeReset => "daily_free_reset", } } } @@ -1307,6 +1323,7 @@ pub struct RuntimeProfileRechargeCenterSnapshot { pub benefits: Vec, pub latest_order: Option, pub has_points_recharged: bool, + pub daily_free_points: RuntimeProfileDailyFreePointsSnapshot, } #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] @@ -1821,6 +1838,18 @@ pub struct RuntimeProfileDashboardRecord { pub played_world_count: u32, pub updated_at: Option, pub updated_at_micros: Option, + pub daily_free_points: RuntimeProfileDailyFreePointsRecord, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RuntimeProfileDailyFreePointsRecord { + pub day_key: i64, + pub granted_points: u64, + pub remaining_points: u64, + pub resets_at: String, + pub resets_at_micros: i64, + pub updated_at: String, + pub updated_at_micros: i64, } #[derive(Clone, Debug, PartialEq)] @@ -1986,6 +2015,7 @@ pub struct RuntimeProfileRechargeCenterRecord { pub benefits: Vec, pub latest_order: Option, pub has_points_recharged: bool, + pub daily_free_points: RuntimeProfileDailyFreePointsRecord, } #[derive(Clone, Debug, PartialEq)] diff --git a/server-rs/crates/module-runtime/src/lib.rs b/server-rs/crates/module-runtime/src/lib.rs index 3d43315dc..409bf2356 100644 --- a/server-rs/crates/module-runtime/src/lib.rs +++ b/server-rs/crates/module-runtime/src/lib.rs @@ -22,59 +22,33 @@ pub fn format_utc_micros(micros: i64) -> String { pub fn runtime_profile_recharge_point_products() -> Vec { vec![ - build_points_recharge_product( - "points_60", - "60泥点", - 600, - 60, - 60, - "首充双倍", - "首充送60泥点", - ), + build_points_recharge_product("points_60", "60泥点", 600, 60, 0, "", "60泥点"), build_points_recharge_product( "points_180", "180泥点", 1800, 180, - 180, - "首充双倍", - "首充送180泥点", + 90, + "首充加赠", + "首充加赠90泥点", ), build_points_recharge_product( "points_300", "300泥点", 3000, 300, - 300, - "首充双倍", - "首充送300泥点", + 150, + "首充加赠", + "首充加赠150泥点", ), build_points_recharge_product( "points_680", "680泥点", 6800, 680, - 680, - "首充双倍", - "首充送680泥点", - ), - build_points_recharge_product( - "points_1280", - "1280泥点", - 12800, - 1280, - 1280, - "首充双倍", - "首充送1280泥点", - ), - build_points_recharge_product( - "points_3280", - "3280泥点", - 32800, - 3280, - 3280, - "首充双倍", - "首充送3280泥点", + 340, + "首充加赠", + "首充加赠340泥点", ), ] } @@ -878,6 +852,13 @@ mod tests { total_play_time_ms: 12, played_world_count: 2, updated_at_micros: Some(1_713_680_000_000_000), + daily_free_points: RuntimeProfileDailyFreePointsSnapshot { + day_key: 19_834, + granted_points: PROFILE_DAILY_FREE_POINTS_PER_DAY, + remaining_points: 12, + resets_at_micros: 1_713_715_200_000_000, + updated_at_micros: 1_713_680_000_000_000, + }, }); assert_eq!(record.updated_at, Some("2024-04-21T06:13:20Z".to_string())); @@ -917,6 +898,14 @@ mod tests { RuntimeProfileWalletLedgerSourceType::DailyTaskReward.as_str(), "daily_task_reward" ); + assert_eq!( + RuntimeProfileWalletLedgerSourceType::DailyFreeGrant.as_str(), + "daily_free_grant" + ); + assert_eq!( + RuntimeProfileWalletLedgerSourceType::DailyFreeReset.as_str(), + "daily_free_reset" + ); } #[test] @@ -1105,16 +1094,20 @@ mod tests { let point_products = runtime_profile_recharge_point_products(); let membership_products = runtime_profile_recharge_membership_products(); - assert_eq!(point_products.len(), 6); + assert_eq!(point_products.len(), 4); assert_eq!(point_products[0].product_id, "points_60"); assert_eq!(point_products[0].title, "60泥点"); assert_eq!(point_products[0].price_cents, 600); - assert_eq!(point_products[0].bonus_points, 60); - assert_eq!(point_products[0].description, "首充送60泥点"); - assert_eq!(point_products[5].product_id, "points_3280"); - assert_eq!(point_products[5].price_cents, 32800); - assert_eq!(point_products[5].bonus_points, 3280); - assert_eq!(point_products[5].description, "首充送3280泥点"); + assert_eq!(point_products[0].bonus_points, 0); + assert_eq!(point_products[0].badge_label, ""); + assert_eq!(point_products[0].description, "60泥点"); + assert_eq!(point_products[1].product_id, "points_180"); + assert_eq!(point_products[1].bonus_points, 90); + assert_eq!(point_products[2].bonus_points, 150); + assert_eq!(point_products[3].product_id, "points_680"); + assert_eq!(point_products[3].price_cents, 6800); + assert_eq!(point_products[3].bonus_points, 340); + assert_eq!(point_products[3].description, "首充加赠340泥点"); assert_eq!(membership_products.len(), 4); assert_eq!(membership_products[0].product_id, "member_starter"); assert_eq!(membership_products[0].title, "Starter"); @@ -1139,17 +1132,19 @@ mod tests { #[test] fn recharge_point_products_do_not_hide_all_first_bonus_by_account_flag() { let first_recharge_products = resolve_runtime_profile_recharge_point_products(false); - assert_eq!(first_recharge_products[0].bonus_points, 60); - assert_eq!(first_recharge_products[0].badge_label, "首充双倍"); - assert_eq!(first_recharge_products[0].description, "首充送60泥点"); + assert_eq!(first_recharge_products[0].bonus_points, 0); + assert_eq!(first_recharge_products[0].badge_label, ""); + assert_eq!(first_recharge_products[0].description, "60泥点"); + assert_eq!(first_recharge_products[1].bonus_points, 90); + assert_eq!(first_recharge_products[1].badge_label, "首充加赠"); + assert_eq!(first_recharge_products[1].description, "首充加赠90泥点"); let repeated_recharge_products = resolve_runtime_profile_recharge_point_products(true); - assert_eq!(repeated_recharge_products[0].bonus_points, 60); - assert_eq!(repeated_recharge_products[0].badge_label, "首充双倍"); - assert_eq!(repeated_recharge_products[0].description, "首充送60泥点"); - assert_eq!(repeated_recharge_products[5].bonus_points, 3280); - assert_eq!(repeated_recharge_products[5].badge_label, "首充双倍"); - assert_eq!(repeated_recharge_products[5].description, "首充送3280泥点"); + assert_eq!(repeated_recharge_products[0].bonus_points, 0); + assert_eq!(repeated_recharge_products[1].bonus_points, 90); + assert_eq!(repeated_recharge_products[3].bonus_points, 340); + assert_eq!(repeated_recharge_products[3].badge_label, "首充加赠"); + assert_eq!(repeated_recharge_products[3].description, "首充加赠340泥点"); } #[test] diff --git a/server-rs/crates/pingora-gateway/src/main.rs b/server-rs/crates/pingora-gateway/src/main.rs index 4978a84c4..8c080f789 100644 --- a/server-rs/crates/pingora-gateway/src/main.rs +++ b/server-rs/crates/pingora-gateway/src/main.rs @@ -5,7 +5,7 @@ use std::{ io, io::SeekFrom, io::Write, - net::SocketAddr, + net::{IpAddr, SocketAddr}, path::{Path, PathBuf}, sync::{Arc, Mutex}, time::{Duration, Instant, SystemTime, UNIX_EPOCH}, @@ -54,6 +54,70 @@ const SHADOW_PROBE_HEADER: &str = "x-genarrative-pingora-probe"; const PAYLOAD_TOO_LARGE_CONTEXT: &str = "genarrative_payload_too_large"; const PROTECTION_STATE_TTL: Duration = Duration::from_secs(600); const PROTECTION_CLEANUP_INTERVAL: Duration = Duration::from_secs(60); +const MAIN_SPA_PATHS: &[&str] = &[ + "/", + "/bark-battle", + "/big-fish", + "/child-motion-demo", + "/creation", + "/creation/baby-object-match", + "/creation/baby-object-match/generating", + "/creation/baby-object-match/result", + "/creation/bark-battle", + "/creation/bark-battle/generating", + "/creation/bark-battle/result", + "/creation/big-fish", + "/creation/big-fish/generating", + "/creation/big-fish/result", + "/creation/creative-agent", + "/creation/jump-hop", + "/creation/jump-hop/generating", + "/creation/jump-hop/result", + "/creation/match3d", + "/creation/match3d/generating", + "/creation/match3d/result", + "/creation/puzzle", + "/creation/puzzle-clear", + "/creation/puzzle-clear/generating", + "/creation/puzzle-clear/result", + "/creation/puzzle/generating", + "/creation/puzzle/result", + "/creation/rpg", + "/creation/rpg/agent", + "/creation/rpg/generating", + "/creation/rpg/result", + "/creation/square-hole", + "/creation/square-hole/generating", + "/creation/square-hole/result", + "/creation/visual-novel", + "/creation/visual-novel/generating", + "/creation/visual-novel/result", + "/creation/wooden-fish", + "/creation/wooden-fish/generating", + "/creation/wooden-fish/result", + "/editor/canvas", + "/gallery/jump-hop/detail", + "/gallery/puzzle/detail", + "/gallery/visual-novel/detail", + "/match3d", + "/project", + "/puzzle", + "/runtime/baby-love-drawing", + "/runtime/baby-object-match", + "/runtime/bark-battle", + "/runtime/big-fish", + "/runtime/jump-hop", + "/runtime/match3d", + "/runtime/puzzle", + "/runtime/puzzle-clear", + "/runtime/rpg/adventure", + "/runtime/rpg/characters", + "/runtime/square-hole", + "/runtime/visual-novel", + "/runtime/wooden-fish", + "/works/detail", + "/worlds/detail", +]; #[derive(Clone, Debug)] struct GatewayConfig { @@ -1128,9 +1192,16 @@ impl ProxyHttp for GenarrativeGateway { apply_configured_body_limit(&mut ctx.route, self.config.max_api_body_bytes); ctx.request_id = resolve_request_id(session); - if self.is_maintenance_enabled() && ctx.route.applies_maintenance_gate() { - respond_maintenance(session, ctx.route.is_api_like(), &self.config.web_root).await?; - return Ok(true); + if self.is_maintenance_enabled() { + let internal_bypass = + allows_internal_maintenance_bypass(request_source_ip(session).as_ref()); + let should_apply_maintenance = !internal_bypass + && (is_admin_request_path(path) || ctx.route.applies_maintenance_gate()); + if should_apply_maintenance { + respond_maintenance(session, ctx.route.is_api_like(), &self.config.web_root) + .await?; + return Ok(true); + } } if let RouteDecision::Proxy { body_limit, .. } = ctx.route @@ -1887,12 +1958,31 @@ fn classify_path(path: &str) -> RouteDecision { }); } + if is_main_spa_path(path) { + return RouteDecision::Local(LocalResponse::Static { + root: StaticRoot::Web, + mode: StaticMode::SpaFallback, + }); + } + RouteDecision::Local(LocalResponse::Static { root: StaticRoot::Web, - mode: StaticMode::SpaFallback, + mode: StaticMode::Exact, }) } +fn is_main_spa_path(path: &str) -> bool { + let normalized = if path.len() > 1 { + path.strip_suffix('/').unwrap_or(path) + } else { + path + }; + + MAIN_SPA_PATHS + .iter() + .any(|candidate| normalized.eq_ignore_ascii_case(candidate)) +} + fn classify_http_redirect_path(path: &str) -> RouteDecision { if path.starts_with("/.well-known/acme-challenge/") { return RouteDecision::Local(LocalResponse::Static { @@ -2707,6 +2797,36 @@ fn client_ip(session: &Session) -> Option { .map(|addr| addr.ip().to_string()) } +fn request_source_ip(session: &Session) -> Option { + let peer_ip = session + .as_downstream() + .client_addr() + .and_then(|addr| addr.as_inet()) + .map(|addr| addr.ip())?; + if peer_ip.is_loopback() + && let Some(real_ip) = + header_value(session, "x-real-ip").and_then(|value| value.trim().parse::().ok()) + { + return Some(real_ip); + } + Some(peer_ip) +} + +fn is_admin_request_path(path: &str) -> bool { + path == "/admin" || path.starts_with("/admin/") +} + +fn is_internal_network_ip(ip: &IpAddr) -> bool { + match ip { + IpAddr::V4(ip) => ip.is_private() || ip.is_loopback() || ip.is_link_local(), + IpAddr::V6(ip) => ip.is_loopback() || ip.is_unique_local() || ip.is_unicast_link_local(), + } +} + +fn allows_internal_maintenance_bypass(client_ip: Option<&IpAddr>) -> bool { + client_ip.is_some_and(is_internal_network_ip) +} + fn append_forwarded_for(session: &Session, client_ip: &str) -> String { session .req_header() @@ -3252,6 +3372,28 @@ mod tests { assert!(should_disable_accel_buffering(&route)); } + #[test] + fn maintenance_allows_internal_clients_to_bypass_all_gated_routes() { + for value in [ + "127.0.0.1", + "10.35.0.50", + "172.16.0.50", + "192.168.35.50", + "169.254.0.50", + "::1", + "fd00::50", + "fe80::50", + ] { + let internal_ip: IpAddr = value.parse().unwrap(); + assert!(allows_internal_maintenance_bypass(Some(&internal_ip))); + } + for value in ["203.0.113.50", "2001:db8::50"] { + let public_ip: IpAddr = value.parse().unwrap(); + assert!(!allows_internal_maintenance_bypass(Some(&public_ip))); + } + assert!(!allows_internal_maintenance_bypass(None)); + } + #[test] fn normalizes_gateway_hosts_for_matching() { assert_eq!( diff --git a/server-rs/crates/platform-wechat/Cargo.toml b/server-rs/crates/platform-wechat/Cargo.toml index 760bb2387..821b11968 100644 --- a/server-rs/crates/platform-wechat/Cargo.toml +++ b/server-rs/crates/platform-wechat/Cargo.toml @@ -20,3 +20,4 @@ time = { workspace = true, features = ["formatting"] } tracing = { workspace = true } url = { workspace = true } urlencoding = { workspace = true } +x509-parser = { workspace = true } diff --git a/server-rs/crates/platform-wechat/src/pay.rs b/server-rs/crates/platform-wechat/src/pay.rs index 193ba4dde..ea8b77083 100644 --- a/server-rs/crates/platform-wechat/src/pay.rs +++ b/server-rs/crates/platform-wechat/src/pay.rs @@ -27,6 +27,11 @@ use std::convert::TryInto; use time::{Duration as TimeDuration, OffsetDateTime, format_description::well_known::Rfc3339}; use tracing::warn; use url::Url; +use x509_parser::{ + prelude::FromDer, + public_key::{PublicKey, RSAPublicKey}, + x509::SubjectPublicKeyInfo, +}; const WECHAT_PAY_PROVIDER_MOCK: &str = "mock"; const WECHAT_PAY_PROVIDER_REAL: &str = "real"; @@ -874,13 +879,7 @@ impl RealWechatPayClient { let signature_bytes = BASE64_STANDARD.decode(signature).map_err(|_| { WechatPayError::InvalidSignature("微信支付通知签名 base64 无效".to_string()) })?; - let public_key = signature::UnparsedPublicKey::new( - &signature::RSA_PKCS1_2048_8192_SHA256, - &self.platform_public_key_der, - ); - public_key - .verify(&message, &signature_bytes) - .map_err(|_| WechatPayError::InvalidSignature("微信支付通知签名验签失败".to_string())) + verify_rsa_sha256_signature(&self.platform_public_key_der, &message, &signature_bytes) } fn sign_message(&self, message: &str) -> Result { @@ -1539,7 +1538,40 @@ fn parse_public_key_pem(pem: &str) -> Result, WechatPayError> { "微信支付平台公钥必须是 PUBLIC KEY PEM".to_string(), )); } - Ok(der) + let (remaining, subject_public_key_info) = + SubjectPublicKeyInfo::from_der(&der).map_err(|_| { + WechatPayError::InvalidConfig("微信支付平台 PUBLIC KEY SPKI 解析失败".to_string()) + })?; + if !remaining.is_empty() || subject_public_key_info.subject_public_key.unused_bits != 0 { + return Err(WechatPayError::InvalidConfig( + "微信支付平台 PUBLIC KEY SPKI 格式非法".to_string(), + )); + } + if !matches!(subject_public_key_info.parsed(), Ok(PublicKey::RSA(_))) { + return Err(WechatPayError::InvalidConfig( + "微信支付平台 PUBLIC KEY 必须使用 RSA 算法".to_string(), + )); + } + let rsa_public_key_der = subject_public_key_info.subject_public_key.data.as_ref(); + let (remaining, _) = RSAPublicKey::from_der(rsa_public_key_der).map_err(|_| { + WechatPayError::InvalidConfig("微信支付平台公钥不是有效的 RSA 公钥".to_string()) + })?; + if !remaining.is_empty() { + return Err(WechatPayError::InvalidConfig( + "微信支付平台 RSA 公钥包含多余数据".to_string(), + )); + } + Ok(rsa_public_key_der.to_vec()) +} + +fn verify_rsa_sha256_signature( + public_key_der: &[u8], + message: &[u8], + signature_bytes: &[u8], +) -> Result<(), WechatPayError> { + signature::UnparsedPublicKey::new(&signature::RSA_PKCS1_2048_8192_SHA256, public_key_der) + .verify(message, signature_bytes) + .map_err(|_| WechatPayError::InvalidSignature("微信支付通知签名验签失败".to_string())) } fn parse_single_pem_block(pem: &str) -> Result<(String, Vec), WechatPayError> { @@ -1666,6 +1698,18 @@ mod tests { use cbc::cipher::{BlockEncryptMut, block_padding::NoPadding}; use serde_json::json; + const TEST_RSA_PUBLIC_KEY_SPKI_PEM: &str = r#"-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0MGrqlP1eWB08o5BsF1y +cMjr6oicQQjO3veTfYRdBPJYmluoVAM8Q6bRvRuayqmhKt0/HeKbFwm4hbXqyvlE +yyNbG/OQpcq++bS+2FBlCrr5j/G+YpltOmuIZbo5vmvuWxjoxpf5zvdvTPtonahJ +VprIAzq8+NsTpgRbBI/GI1KaFEg12pk/vjCMKgVLTH76bYjhAXaIvYpYJaDxzTlC +VxGe8+KWe/e3brg2CP84sFgCw4JRBRkrRqoJKhWnaj1CbaMEkWmebP48tWa/SMpk +QDeqguoVrBMCVVV75GSCSaGlxT8XxPr03T+H1M+xnQZaqD+0EyN5CABmuMisP9ql +KwIDAQAB +-----END PUBLIC KEY-----"#; + + const TEST_RSA_SIGNATURE_BASE64: &str = "cuwiNfJ/Ck0UiG1xZl6T1tyAtap91hAXrfpH4FPI+8nzrbnX9NHj5T5DiUdGeuR+Y1BQ+N+y4M4SOih4g2oArdmWeGgfoKE/N7O61fN3SaEGfhSqSedrtTc02j3Yvk/2HeBtsdcgaLG8xb/ZFGifVTHAeGaHpT4Yy1tmP6V+Kd6FUoMVJsXdyDBxRRzWqssIKfEIfBO0gCZ/j34Hqrt6KLYeBu6hMW77YCe0ShpZO4An3MxpjcYAlkeF8fuhWdQsPz4DcF00mtoJWYg2ncTb6OCsCc4YeUC2dKHWo6S7vxsnr9qwp2XvRnRuYp9kHN7oTOCfs851lYmTYJJfMvjlLg=="; + #[test] fn mock_pay_params_use_request_payment_shape() { let params = build_mock_pay_params("recharge:user:1:points_60"); @@ -1897,6 +1941,31 @@ mod tests { ); } + #[test] + fn spki_platform_public_key_verifies_real_rsa_sha256_signature() { + let public_key_der = parse_public_key_pem(TEST_RSA_PUBLIC_KEY_SPKI_PEM) + .expect("SPKI platform public key should parse as PKCS#1 RSA key"); + let message = build_notify_signature_message( + b"1778759600", + b"nonce-real-signature", + br#"{"id":"notify-1","event_type":"TRANSACTION.SUCCESS"}"#, + ); + let signature_bytes = BASE64_STANDARD + .decode(TEST_RSA_SIGNATURE_BASE64) + .expect("test RSA signature should decode"); + + verify_rsa_sha256_signature(&public_key_der, &message, &signature_bytes) + .expect("SPKI-derived PKCS#1 public key should verify notification signature"); + + let mut tampered_message = message; + tampered_message.push(b' '); + assert!( + verify_rsa_sha256_signature(&public_key_der, &tampered_message, &signature_bytes) + .is_err(), + "tampered notification must fail signature verification" + ); + } + #[test] fn parse_mock_notify_defaults_success_state() { let notify = diff --git a/server-rs/crates/shared-contracts/src/runtime.rs b/server-rs/crates/shared-contracts/src/runtime.rs index 2949d5d69..98674d92b 100644 --- a/server-rs/crates/shared-contracts/src/runtime.rs +++ b/server-rs/crates/shared-contracts/src/runtime.rs @@ -11,6 +11,8 @@ pub const PROFILE_WALLET_LEDGER_SOURCE_TYPE_MEMBERSHIP_PERIOD_GRANT: &str = "membership_period_grant"; pub const PROFILE_WALLET_LEDGER_SOURCE_TYPE_MEMBERSHIP_PERIOD_RESET: &str = "membership_period_reset"; +pub const PROFILE_WALLET_LEDGER_SOURCE_TYPE_DAILY_FREE_GRANT: &str = "daily_free_grant"; +pub const PROFILE_WALLET_LEDGER_SOURCE_TYPE_DAILY_FREE_RESET: &str = "daily_free_reset"; pub const PROFILE_WALLET_LEDGER_SOURCE_TYPE_INVITE_INVITER_REWARD: &str = "invite_inviter_reward"; pub const PROFILE_WALLET_LEDGER_SOURCE_TYPE_INVITE_INVITEE_REWARD: &str = "invite_invitee_reward"; pub const PROFILE_WALLET_LEDGER_SOURCE_TYPE_ASSET_OPERATION_CONSUME: &str = @@ -173,6 +175,29 @@ pub struct ProfileDashboardSummaryResponse { pub total_play_time_ms: u64, pub played_world_count: u32, pub updated_at: Option, + pub daily_free_points: ProfileDailyFreePointsResponse, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct ProfileDailyFreePointsResponse { + pub day_key: i64, + pub granted_points: u64, + pub remaining_points: u64, + pub resets_at: String, + pub updated_at: String, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct ProfileMudPointBalanceResponse { + pub total_points: u64, + pub permanent_points: u64, + pub limited_points: u64, + pub limited_expires_at: Option, + pub daily_free_points: u64, + pub daily_free_reset_points: u64, + pub daily_free_resets_at: String, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] @@ -305,12 +330,14 @@ pub struct WechatNativePaymentResponse { #[serde(rename_all = "camelCase")] pub struct ProfileRechargeCenterResponse { pub wallet_balance: u64, + pub mud_point_balance: ProfileMudPointBalanceResponse, pub membership: ProfileMembershipResponse, pub point_products: Vec, pub membership_products: Vec, pub benefits: Vec, pub latest_order: Option, pub has_points_recharged: bool, + pub daily_free_points: ProfileDailyFreePointsResponse, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] @@ -1192,6 +1219,18 @@ mod tests { use super::*; use serde_json::json; + fn empty_mud_point_balance() -> ProfileMudPointBalanceResponse { + ProfileMudPointBalanceResponse { + total_points: 0, + permanent_points: 0, + limited_points: 0, + limited_expires_at: None, + daily_free_points: 0, + daily_free_reset_points: 20, + daily_free_resets_at: "2026-07-12T16:00:00Z".to_string(), + } + } + #[test] fn runtime_settings_request_uses_camel_case_fields() { let payload = serde_json::to_value(PutRuntimeSettingsRequest { @@ -1260,6 +1299,13 @@ mod tests { total_play_time_ms: 16, played_world_count: 3, updated_at: Some("2026-04-22T10:00:00Z".to_string()), + daily_free_points: ProfileDailyFreePointsResponse { + day_key: 20_646, + granted_points: 20, + remaining_points: 12, + resets_at: "2026-07-12T16:00:00Z".to_string(), + updated_at: "2026-07-12T08:00:00Z".to_string(), + }, }) .expect("payload should serialize"); @@ -1267,6 +1313,7 @@ mod tests { assert_eq!(payload["totalPlayTimeMs"], json!(16)); assert_eq!(payload["playedWorldCount"], json!(3)); assert_eq!(payload["updatedAt"], json!("2026-04-22T10:00:00Z")); + assert_eq!(payload["dailyFreePoints"]["remainingPoints"], json!(12)); } #[test] @@ -1444,6 +1491,15 @@ mod tests { fn profile_recharge_center_response_uses_camel_case_fields() { let payload = serde_json::to_value(ProfileRechargeCenterResponse { wallet_balance: 29, + mud_point_balance: ProfileMudPointBalanceResponse { + total_points: 29, + permanent_points: 4, + limited_points: 5, + limited_expires_at: Some("2026-05-25T10:00:00Z".to_string()), + daily_free_points: 20, + daily_free_reset_points: 20, + daily_free_resets_at: "2026-07-12T16:00:00Z".to_string(), + }, membership: ProfileMembershipResponse { status: "active".to_string(), tier: "month".to_string(), @@ -1476,10 +1532,20 @@ mod tests { benefits: vec![], latest_order: None, has_points_recharged: false, + daily_free_points: ProfileDailyFreePointsResponse { + day_key: 20_646, + granted_points: 20, + remaining_points: 20, + resets_at: "2026-07-12T16:00:00Z".to_string(), + updated_at: "2026-07-12T08:00:00Z".to_string(), + }, }) .expect("payload should serialize"); assert_eq!(payload["walletBalance"], json!(29)); + assert_eq!(payload["mudPointBalance"]["permanentPoints"], json!(4)); + assert_eq!(payload["mudPointBalance"]["limitedPoints"], json!(5)); + assert_eq!(payload["mudPointBalance"]["dailyFreePoints"], json!(20)); assert_eq!( payload["membership"]["expiresAt"], json!("2026-05-25T10:00:00Z") @@ -1493,6 +1559,7 @@ mod tests { json!("首充送60泥点") ); assert_eq!(payload["hasPointsRecharged"], json!(false)); + assert_eq!(payload["dailyFreePoints"]["grantedPoints"], json!(20)); } #[test] @@ -1528,6 +1595,7 @@ mod tests { }; let center = ProfileRechargeCenterResponse { wallet_balance: 0, + mud_point_balance: empty_mud_point_balance(), membership: ProfileMembershipResponse { status: "normal".to_string(), tier: "normal".to_string(), @@ -1545,6 +1613,13 @@ mod tests { benefits: vec![], latest_order: None, has_points_recharged: false, + daily_free_points: ProfileDailyFreePointsResponse { + day_key: 20_646, + granted_points: 20, + remaining_points: 20, + resets_at: "2026-07-12T16:00:00Z".to_string(), + updated_at: "2026-07-12T08:00:00Z".to_string(), + }, }; let payload = serde_json::to_value(CreateProfileRechargeOrderResponse { order, @@ -1596,6 +1671,7 @@ mod tests { }; let center = ProfileRechargeCenterResponse { wallet_balance: 0, + mud_point_balance: empty_mud_point_balance(), membership: ProfileMembershipResponse { status: "normal".to_string(), tier: "normal".to_string(), @@ -1613,6 +1689,13 @@ mod tests { benefits: vec![], latest_order: None, has_points_recharged: false, + daily_free_points: ProfileDailyFreePointsResponse { + day_key: 20_646, + granted_points: 20, + remaining_points: 20, + resets_at: "2026-07-12T16:00:00Z".to_string(), + updated_at: "2026-07-12T08:00:00Z".to_string(), + }, }; let payload = serde_json::to_value(CreateProfileRechargeOrderResponse { order, diff --git a/server-rs/crates/spacetime-client/src/external_generation.rs b/server-rs/crates/spacetime-client/src/external_generation.rs index fd217fb35..6d70087ea 100644 --- a/server-rs/crates/spacetime-client/src/external_generation.rs +++ b/server-rs/crates/spacetime-client/src/external_generation.rs @@ -307,6 +307,31 @@ impl SpacetimeClient { .await } + pub async fn get_external_generation_job_summary( + &self, + input: ExternalGenerationJobGetRecordInput, + ) -> Result { + let procedure_input = input.into(); + + self.call_after_connect( + "get_external_generation_job_summary_and_return", + move |connection, sender| { + connection + .procedures() + .get_external_generation_job_summary_and_return_then( + procedure_input, + move |_, result| { + let mapped = result + .map_err(SpacetimeClientError::from_sdk_error) + .and_then(map_external_generation_job_summary_procedure_result); + send_once(&sender, mapped); + }, + ); + }, + ) + .await + } + pub async fn list_external_generation_jobs( &self, input: ExternalGenerationJobListRecordInput, @@ -332,6 +357,31 @@ impl SpacetimeClient { .await } + pub async fn list_external_generation_job_summaries( + &self, + input: ExternalGenerationJobListRecordInput, + ) -> Result { + let procedure_input = input.into(); + + self.call_after_connect( + "list_external_generation_job_summaries_and_return", + move |connection, sender| { + connection + .procedures() + .list_external_generation_job_summaries_and_return_then( + procedure_input, + move |_, result| { + let mapped = result + .map_err(SpacetimeClientError::from_sdk_error) + .and_then(map_external_generation_job_summary_list_result); + send_once(&sender, mapped); + }, + ); + }, + ) + .await + } + pub async fn acknowledge_external_generation_jobs( &self, input: ExternalGenerationJobAcknowledgeRecordInput, @@ -357,6 +407,31 @@ impl SpacetimeClient { .await } + pub async fn acknowledge_external_generation_job_summaries( + &self, + input: ExternalGenerationJobAcknowledgeRecordInput, + ) -> Result { + let procedure_input = input.into(); + + self.call_after_connect( + "acknowledge_external_generation_job_summaries_and_return", + move |connection, sender| { + connection + .procedures() + .acknowledge_external_generation_job_summaries_and_return_then( + procedure_input, + move |_, result| { + let mapped = result + .map_err(SpacetimeClientError::from_sdk_error) + .and_then(map_external_generation_job_summary_list_result); + send_once(&sender, mapped); + }, + ); + }, + ) + .await + } + pub async fn get_external_generation_queue_stats( &self, ) -> Result { diff --git a/server-rs/crates/spacetime-client/src/lib.rs b/server-rs/crates/spacetime-client/src/lib.rs index dbbf9f247..2ae6f57ca 100644 --- a/server-rs/crates/spacetime-client/src/lib.rs +++ b/server-rs/crates/spacetime-client/src/lib.rs @@ -57,7 +57,8 @@ pub use mapper::{ ExternalGenerationJobEnqueueRecordInput, ExternalGenerationJobFailRecordInput, ExternalGenerationJobGetRecordInput, ExternalGenerationJobListRecord, ExternalGenerationJobListRecordInput, ExternalGenerationJobRecord, - ExternalGenerationJobRenewLeaseRecordInput, ExternalGenerationQueueStatsRecord, + ExternalGenerationJobRenewLeaseRecordInput, ExternalGenerationJobSummaryListRecord, + ExternalGenerationJobSummaryRecord, ExternalGenerationQueueStatsRecord, FeatureGateConfigRecord, JumpHopActionRequest, JumpHopActionResponse, JumpHopActionType, JumpHopCharacterAsset, JumpHopDifficulty, JumpHopDraftResponse, JumpHopGalleryCardResponse, JumpHopGalleryDetailResponse, JumpHopGalleryResponse, JumpHopGenerationStatus, diff --git a/server-rs/crates/spacetime-client/src/mapper.rs b/server-rs/crates/spacetime-client/src/mapper.rs index 36e68f966..c29d71cde 100644 --- a/server-rs/crates/spacetime-client/src/mapper.rs +++ b/server-rs/crates/spacetime-client/src/mapper.rs @@ -107,6 +107,7 @@ pub use self::external_generation::{ ExternalGenerationJobFailRecordInput, ExternalGenerationJobGetRecordInput, ExternalGenerationJobListRecord, ExternalGenerationJobListRecordInput, ExternalGenerationJobRecord, ExternalGenerationJobRenewLeaseRecordInput, + ExternalGenerationJobSummaryListRecord, ExternalGenerationJobSummaryRecord, ExternalGenerationQueueStatsRecord, }; pub use self::jump_hop::{ @@ -244,7 +245,9 @@ pub(crate) use self::external_api_key::{ }; pub(crate) use self::external_generation::{ map_external_generation_job_claim_result, map_external_generation_job_list_result, - map_external_generation_job_procedure_result, map_external_generation_queue_stats_result, + map_external_generation_job_procedure_result, map_external_generation_job_summary_list_result, + map_external_generation_job_summary_procedure_result, + map_external_generation_queue_stats_result, }; pub(crate) use self::inventory::{ map_runtime_inventory_state_procedure_result, map_runtime_item_reward_item_snapshot, @@ -301,7 +304,6 @@ pub(crate) use self::runtime_profile::{ map_runtime_profile_recharge_order_expiration_claim_procedure_result, map_runtime_profile_recharge_order_expiration_complete_procedure_result, map_runtime_profile_recharge_order_procedure_result, - map_runtime_profile_recharge_order_table_row, map_runtime_profile_recharge_product_admin_list_procedure_result, map_runtime_profile_recharge_product_admin_procedure_result, map_runtime_profile_redeem_code_admin_list_procedure_result, diff --git a/server-rs/crates/spacetime-client/src/mapper/external_generation.rs b/server-rs/crates/spacetime-client/src/mapper/external_generation.rs index bbb775796..fa1901aac 100644 --- a/server-rs/crates/spacetime-client/src/mapper/external_generation.rs +++ b/server-rs/crates/spacetime-client/src/mapper/external_generation.rs @@ -146,6 +146,39 @@ pub(crate) fn map_external_generation_job_list_result( }) } +pub(crate) fn map_external_generation_job_summary_procedure_result( + result: ExternalGenerationJobSummaryProcedureResult, +) -> Result { + if !result.ok { + return Err(SpacetimeClientError::procedure_failed(result.error_message)); + } + + let job = result.job.ok_or_else(|| { + SpacetimeClientError::missing_snapshot("external_generation_job_summary 快照") + })?; + Ok(map_external_generation_job_summary_snapshot(job)) +} + +pub(crate) fn map_external_generation_job_summary_list_result( + result: ExternalGenerationJobSummaryProcedureResult, +) -> Result { + if !result.ok { + return Err(SpacetimeClientError::procedure_failed(result.error_message)); + } + + Ok(ExternalGenerationJobSummaryListRecord { + jobs: result + .jobs + .into_iter() + .map(map_external_generation_job_summary_snapshot) + .collect(), + pending_count: result.pending_count, + running_count: result.running_count, + unacknowledged_terminal_count: result.unacknowledged_terminal_count, + now_micros: result.now_micros, + }) +} + pub(crate) fn map_external_generation_queue_stats_result( result: ExternalGenerationQueueStatsProcedureResult, ) -> Result { @@ -207,6 +240,33 @@ fn map_external_generation_job_snapshot( } } +fn map_external_generation_job_summary_snapshot( + snapshot: ExternalGenerationJobSummarySnapshot, +) -> ExternalGenerationJobSummaryRecord { + ExternalGenerationJobSummaryRecord { + job_id: snapshot.job_id, + job_kind: snapshot.job_kind, + owner_user_id: snapshot.owner_user_id, + source_module: snapshot.source_module, + source_entity_id: snapshot.source_entity_id, + request_label: snapshot.request_label, + request_prompt: snapshot.request_prompt, + status: snapshot.status, + last_error_message: snapshot.last_error_message, + created_at: format_timestamp_micros(snapshot.created_at_micros), + started_at: snapshot.started_at_micros.map(format_timestamp_micros), + completed_at: snapshot.completed_at_micros.map(format_timestamp_micros), + updated_at: format_timestamp_micros(snapshot.updated_at_micros), + updated_at_micros: snapshot.updated_at_micros, + price_mud_points: snapshot.price_mud_points, + refund_ledger_id: snapshot.refund_ledger_id, + notification_acknowledged_at: snapshot + .notification_acknowledged_at_micros + .map(format_timestamp_micros), + notification_acknowledged_at_micros: snapshot.notification_acknowledged_at_micros, + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct ExternalGenerationJobEnqueueRecordInput { pub job_id: String, @@ -320,6 +380,37 @@ pub struct ExternalGenerationJobListRecord { pub now_micros: i64, } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ExternalGenerationJobSummaryRecord { + pub job_id: String, + pub job_kind: String, + pub owner_user_id: String, + pub source_module: String, + pub source_entity_id: String, + pub request_label: String, + pub request_prompt: Option, + pub status: String, + pub last_error_message: Option, + pub created_at: String, + pub started_at: Option, + pub completed_at: Option, + pub updated_at: String, + pub updated_at_micros: i64, + pub price_mud_points: u64, + pub refund_ledger_id: Option, + pub notification_acknowledged_at: Option, + pub notification_acknowledged_at_micros: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ExternalGenerationJobSummaryListRecord { + pub jobs: Vec, + pub pending_count: u32, + pub running_count: u32, + pub unacknowledged_terminal_count: u32, + pub now_micros: i64, +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct ExternalGenerationQueueStatsRecord { pub pending_count: u32, diff --git a/server-rs/crates/spacetime-client/src/mapper/puzzle.rs b/server-rs/crates/spacetime-client/src/mapper/puzzle.rs index 376861b01..47bdfc651 100644 --- a/server-rs/crates/spacetime-client/src/mapper/puzzle.rs +++ b/server-rs/crates/spacetime-client/src/mapper/puzzle.rs @@ -609,6 +609,12 @@ pub(crate) fn map_runtime_profile_wallet_ledger_source_type_back( crate::module_bindings::RuntimeProfileWalletLedgerSourceType::DailyTaskReward => { module_runtime::RuntimeProfileWalletLedgerSourceType::DailyTaskReward } + crate::module_bindings::RuntimeProfileWalletLedgerSourceType::DailyFreeGrant => { + module_runtime::RuntimeProfileWalletLedgerSourceType::DailyFreeGrant + } + crate::module_bindings::RuntimeProfileWalletLedgerSourceType::DailyFreeReset => { + module_runtime::RuntimeProfileWalletLedgerSourceType::DailyFreeReset + } } } diff --git a/server-rs/crates/spacetime-client/src/mapper/runtime_profile.rs b/server-rs/crates/spacetime-client/src/mapper/runtime_profile.rs index 323f67ad7..74f898096 100644 --- a/server-rs/crates/spacetime-client/src/mapper/runtime_profile.rs +++ b/server-rs/crates/spacetime-client/src/mapper/runtime_profile.rs @@ -928,6 +928,21 @@ pub(crate) fn map_runtime_profile_dashboard_snapshot( total_play_time_ms: snapshot.total_play_time_ms, played_world_count: snapshot.played_world_count, updated_at_micros: snapshot.updated_at_micros, + daily_free_points: map_runtime_profile_daily_free_points_snapshot( + snapshot.daily_free_points, + ), + } +} + +pub(crate) fn map_runtime_profile_daily_free_points_snapshot( + snapshot: RuntimeProfileDailyFreePointsSnapshot, +) -> module_runtime::RuntimeProfileDailyFreePointsSnapshot { + module_runtime::RuntimeProfileDailyFreePointsSnapshot { + day_key: snapshot.day_key, + granted_points: snapshot.granted_points, + remaining_points: snapshot.remaining_points, + resets_at_micros: snapshot.resets_at_micros, + updated_at_micros: snapshot.updated_at_micros, } } @@ -995,6 +1010,9 @@ pub(crate) fn map_runtime_profile_recharge_center_snapshot( .latest_order .map(map_runtime_profile_recharge_order_snapshot), has_points_recharged: snapshot.has_points_recharged, + daily_free_points: map_runtime_profile_daily_free_points_snapshot( + snapshot.daily_free_points, + ), } } @@ -1104,40 +1122,6 @@ pub(crate) fn map_runtime_profile_recharge_order_snapshot( } } -pub(crate) fn map_runtime_profile_recharge_order_table_row( - row: ProfileRechargeOrder, -) -> RuntimeProfileRechargeOrderRecord { - module_runtime::build_runtime_profile_recharge_order_record( - module_runtime::RuntimeProfileRechargeOrderSnapshot { - order_id: row.order_id, - user_id: row.user_id, - product_id: row.product_id, - product_title: row.product_title, - kind: map_runtime_profile_recharge_product_kind_back(row.kind), - amount_cents: row.amount_cents, - status: map_runtime_profile_recharge_order_status_back(row.status), - payment_channel: row.payment_channel, - paid_at_micros: row - .paid_at - .map(|value| value.to_micros_since_unix_epoch()), - provider_transaction_id: row.provider_transaction_id, - created_at_micros: row.created_at.to_micros_since_unix_epoch(), - points_delta: row.points_delta, - membership_expires_at_micros: row - .membership_expires_at - .map(|value| value.to_micros_since_unix_epoch()), - expired_at_micros: row - .expired_at - .map(|value| value.to_micros_since_unix_epoch()), - expiration_checked_at_micros: row - .expiration_checked_at - .map(|value| value.to_micros_since_unix_epoch()), - expiration_provider_state: row.expiration_provider_state, - expiration_last_error: row.expiration_last_error, - }, - ) -} - pub(crate) fn map_runtime_profile_recharge_order_expiration_schedule_snapshot( snapshot: RuntimeProfileRechargeOrderExpirationScheduleSnapshot, ) -> module_runtime::RuntimeProfileRechargeOrderExpirationScheduleSnapshot { diff --git a/server-rs/crates/spacetime-client/src/module_bindings.rs b/server-rs/crates/spacetime-client/src/module_bindings.rs index eb9675a45..c0db8c832 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings.rs @@ -1,12 +1,13 @@ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. -// This was generated using spacetimedb cli version 2.5.0 (commit ca16958ef0a5f8c816700d2255a0b20ecacff901). +// This was generated using spacetimedb cli version 2.6.0 (commit 31fd1c8c3346dfec38dfcc2e89c2ecf457cf26ff). #![allow(unused, clippy::all)] use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; pub mod accept_quest_reducer; +pub mod acknowledge_external_generation_job_summaries_and_return_procedure; pub mod acknowledge_external_generation_jobs_and_return_procedure; pub mod acknowledge_quest_completion_reducer; pub mod admin_disable_profile_redeem_code_procedure; @@ -114,6 +115,7 @@ pub mod auth_store_projection_user_type; pub mod auth_store_projection_view_type; pub mod authenticate_external_api_key_and_return_procedure; pub mod authorize_database_migration_operator_procedure; +pub mod backfill_external_generation_job_summaries_and_return_procedure; pub mod bark_battle_draft_config_row_type; pub mod bark_battle_draft_config_snapshot_type; pub mod bark_battle_draft_config_table; @@ -225,6 +227,7 @@ pub mod clear_platform_browse_history_and_return_procedure; pub mod click_match_3_d_item_procedure; pub mod close_profile_recharge_order_and_return_procedure; pub mod combat_outcome_type; +pub mod compact_external_generation_job_payloads_and_return_procedure; pub mod compile_big_fish_draft_procedure; pub mod compile_custom_world_published_profile_procedure; pub mod compile_jump_hop_draft_procedure; @@ -480,9 +483,17 @@ pub mod external_generation_job_event_type; pub mod external_generation_job_fail_input_type; pub mod external_generation_job_get_input_type; pub mod external_generation_job_list_input_type; +pub mod external_generation_job_payload_compaction_input_type; +pub mod external_generation_job_payload_compaction_procedure_result_type; pub mod external_generation_job_procedure_result_type; pub mod external_generation_job_renew_lease_input_type; pub mod external_generation_job_snapshot_type; +pub mod external_generation_job_summary_backfill_input_type; +pub mod external_generation_job_summary_backfill_procedure_result_type; +pub mod external_generation_job_summary_procedure_result_type; +pub mod external_generation_job_summary_snapshot_type; +pub mod external_generation_job_summary_table; +pub mod external_generation_job_summary_type; pub mod external_generation_job_table; pub mod external_generation_job_type; pub mod external_generation_queue_stats_procedure_result_type; @@ -524,6 +535,7 @@ pub mod get_editor_generation_pricing_config_and_return_procedure; pub mod get_editor_project_and_return_procedure; pub mod get_editor_showcase_campaign_config_and_return_procedure; pub mod get_external_generation_job_and_return_procedure; +pub mod get_external_generation_job_summary_and_return_procedure; pub mod get_external_generation_queue_stats_and_return_procedure; pub mod get_feature_gate_config_procedure; pub mod get_jump_hop_agent_session_procedure; @@ -639,6 +651,7 @@ pub mod list_custom_world_works_procedure; pub mod list_editor_agent_conversations_and_return_procedure; pub mod list_editor_projects_and_return_procedure; pub mod list_external_api_keys_and_return_procedure; +pub mod list_external_generation_job_summaries_and_return_procedure; pub mod list_external_generation_jobs_and_return_procedure; pub mod list_jump_hop_works_procedure; pub mod list_match_3_d_works_procedure; @@ -724,6 +737,8 @@ pub mod player_progression_table; pub mod player_progression_type; pub mod profile_code_operation_table; pub mod profile_code_operation_type; +pub mod profile_daily_free_points_table; +pub mod profile_daily_free_points_type; pub mod profile_dashboard_state_table; pub mod profile_dashboard_state_type; pub mod profile_feedback_submission_table; @@ -998,6 +1013,7 @@ pub mod runtime_item_reward_item_rarity_type; pub mod runtime_item_reward_item_snapshot_type; pub mod runtime_platform_theme_type; pub mod runtime_profile_code_operation_snapshot_type; +pub mod runtime_profile_daily_free_points_snapshot_type; pub mod runtime_profile_dashboard_get_input_type; pub mod runtime_profile_dashboard_procedure_result_type; pub mod runtime_profile_dashboard_snapshot_type; @@ -1329,6 +1345,7 @@ pub mod wooden_fish_works_list_input_type; pub mod wooden_fish_works_procedure_result_type; pub use accept_quest_reducer::accept_quest; +pub use acknowledge_external_generation_job_summaries_and_return_procedure::acknowledge_external_generation_job_summaries_and_return; pub use acknowledge_external_generation_jobs_and_return_procedure::acknowledge_external_generation_jobs_and_return; pub use acknowledge_quest_completion_reducer::acknowledge_quest_completion; pub use admin_disable_profile_redeem_code_procedure::admin_disable_profile_redeem_code; @@ -1436,6 +1453,7 @@ pub use auth_store_projection_user_type::AuthStoreProjectionUser; pub use auth_store_projection_view_type::AuthStoreProjectionView; pub use authenticate_external_api_key_and_return_procedure::authenticate_external_api_key_and_return; pub use authorize_database_migration_operator_procedure::authorize_database_migration_operator; +pub use backfill_external_generation_job_summaries_and_return_procedure::backfill_external_generation_job_summaries_and_return; pub use bark_battle_draft_config_row_type::BarkBattleDraftConfigRow; pub use bark_battle_draft_config_snapshot_type::BarkBattleDraftConfigSnapshot; pub use bark_battle_draft_config_table::*; @@ -1547,6 +1565,7 @@ pub use clear_platform_browse_history_and_return_procedure::clear_platform_brows pub use click_match_3_d_item_procedure::click_match_3_d_item; pub use close_profile_recharge_order_and_return_procedure::close_profile_recharge_order_and_return; pub use combat_outcome_type::CombatOutcome; +pub use compact_external_generation_job_payloads_and_return_procedure::compact_external_generation_job_payloads_and_return; pub use compile_big_fish_draft_procedure::compile_big_fish_draft; pub use compile_custom_world_published_profile_procedure::compile_custom_world_published_profile; pub use compile_jump_hop_draft_procedure::compile_jump_hop_draft; @@ -1802,9 +1821,17 @@ pub use external_generation_job_event_type::ExternalGenerationJobEvent; pub use external_generation_job_fail_input_type::ExternalGenerationJobFailInput; pub use external_generation_job_get_input_type::ExternalGenerationJobGetInput; pub use external_generation_job_list_input_type::ExternalGenerationJobListInput; +pub use external_generation_job_payload_compaction_input_type::ExternalGenerationJobPayloadCompactionInput; +pub use external_generation_job_payload_compaction_procedure_result_type::ExternalGenerationJobPayloadCompactionProcedureResult; pub use external_generation_job_procedure_result_type::ExternalGenerationJobProcedureResult; pub use external_generation_job_renew_lease_input_type::ExternalGenerationJobRenewLeaseInput; pub use external_generation_job_snapshot_type::ExternalGenerationJobSnapshot; +pub use external_generation_job_summary_backfill_input_type::ExternalGenerationJobSummaryBackfillInput; +pub use external_generation_job_summary_backfill_procedure_result_type::ExternalGenerationJobSummaryBackfillProcedureResult; +pub use external_generation_job_summary_procedure_result_type::ExternalGenerationJobSummaryProcedureResult; +pub use external_generation_job_summary_snapshot_type::ExternalGenerationJobSummarySnapshot; +pub use external_generation_job_summary_table::*; +pub use external_generation_job_summary_type::ExternalGenerationJobSummary; pub use external_generation_job_table::*; pub use external_generation_job_type::ExternalGenerationJob; pub use external_generation_queue_stats_procedure_result_type::ExternalGenerationQueueStatsProcedureResult; @@ -1846,6 +1873,7 @@ pub use get_editor_generation_pricing_config_and_return_procedure::get_editor_ge pub use get_editor_project_and_return_procedure::get_editor_project_and_return; pub use get_editor_showcase_campaign_config_and_return_procedure::get_editor_showcase_campaign_config_and_return; pub use get_external_generation_job_and_return_procedure::get_external_generation_job_and_return; +pub use get_external_generation_job_summary_and_return_procedure::get_external_generation_job_summary_and_return; pub use get_external_generation_queue_stats_and_return_procedure::get_external_generation_queue_stats_and_return; pub use get_feature_gate_config_procedure::get_feature_gate_config; pub use get_jump_hop_agent_session_procedure::get_jump_hop_agent_session; @@ -1961,6 +1989,7 @@ pub use list_custom_world_works_procedure::list_custom_world_works; pub use list_editor_agent_conversations_and_return_procedure::list_editor_agent_conversations_and_return; pub use list_editor_projects_and_return_procedure::list_editor_projects_and_return; pub use list_external_api_keys_and_return_procedure::list_external_api_keys_and_return; +pub use list_external_generation_job_summaries_and_return_procedure::list_external_generation_job_summaries_and_return; pub use list_external_generation_jobs_and_return_procedure::list_external_generation_jobs_and_return; pub use list_jump_hop_works_procedure::list_jump_hop_works; pub use list_match_3_d_works_procedure::list_match_3_d_works; @@ -2046,6 +2075,8 @@ pub use player_progression_table::*; pub use player_progression_type::PlayerProgression; pub use profile_code_operation_table::*; pub use profile_code_operation_type::ProfileCodeOperation; +pub use profile_daily_free_points_table::*; +pub use profile_daily_free_points_type::ProfileDailyFreePoints; pub use profile_dashboard_state_table::*; pub use profile_dashboard_state_type::ProfileDashboardState; pub use profile_feedback_submission_table::*; @@ -2320,6 +2351,7 @@ pub use runtime_item_reward_item_rarity_type::RuntimeItemRewardItemRarity; pub use runtime_item_reward_item_snapshot_type::RuntimeItemRewardItemSnapshot; pub use runtime_platform_theme_type::RuntimePlatformTheme; pub use runtime_profile_code_operation_snapshot_type::RuntimeProfileCodeOperationSnapshot; +pub use runtime_profile_daily_free_points_snapshot_type::RuntimeProfileDailyFreePointsSnapshot; pub use runtime_profile_dashboard_get_input_type::RuntimeProfileDashboardGetInput; pub use runtime_profile_dashboard_procedure_result_type::RuntimeProfileDashboardProcedureResult; pub use runtime_profile_dashboard_snapshot_type::RuntimeProfileDashboardSnapshot; @@ -2987,6 +3019,7 @@ pub struct DbUpdate { external_api_key: __sdk::TableUpdate, external_generation_job: __sdk::TableUpdate, external_generation_job_event: __sdk::TableUpdate, + external_generation_job_summary: __sdk::TableUpdate, feature_gate_config: __sdk::TableUpdate, inventory_slot: __sdk::TableUpdate, jump_hop_agent_session: __sdk::TableUpdate, @@ -3004,6 +3037,7 @@ pub struct DbUpdate { npc_state: __sdk::TableUpdate, player_progression: __sdk::TableUpdate, profile_code_operation: __sdk::TableUpdate, + profile_daily_free_points: __sdk::TableUpdate, profile_dashboard_state: __sdk::TableUpdate, profile_feedback_submission: __sdk::TableUpdate, profile_invite_code: __sdk::TableUpdate, @@ -3258,6 +3292,11 @@ impl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate { "external_generation_job_event" => db_update.external_generation_job_event.append( external_generation_job_event_table::parse_table_update(table_update)?, ), + "external_generation_job_summary" => { + db_update.external_generation_job_summary.append( + external_generation_job_summary_table::parse_table_update(table_update)?, + ) + } "feature_gate_config" => db_update .feature_gate_config .append(feature_gate_config_table::parse_table_update(table_update)?), @@ -3309,6 +3348,9 @@ impl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate { "profile_code_operation" => db_update.profile_code_operation.append( profile_code_operation_table::parse_table_update(table_update)?, ), + "profile_daily_free_points" => db_update.profile_daily_free_points.append( + profile_daily_free_points_table::parse_table_update(table_update)?, + ), "profile_dashboard_state" => db_update.profile_dashboard_state.append( profile_dashboard_state_table::parse_table_update(table_update)?, ), @@ -3813,6 +3855,12 @@ impl __sdk::DbUpdate for DbUpdate { &self.external_generation_job_event, ) .with_updates_by_pk(|row| &row.event_id); + diff.external_generation_job_summary = cache + .apply_diff_to_table::( + "external_generation_job_summary", + &self.external_generation_job_summary, + ) + .with_updates_by_pk(|row| &row.job_id); diff.feature_gate_config = cache .apply_diff_to_table::( "feature_gate_config", @@ -3888,6 +3936,12 @@ impl __sdk::DbUpdate for DbUpdate { &self.profile_code_operation, ) .with_updates_by_pk(|row| &row.operation_id); + diff.profile_daily_free_points = cache + .apply_diff_to_table::( + "profile_daily_free_points", + &self.profile_daily_free_points, + ) + .with_updates_by_pk(|row| &row.user_id); diff.profile_dashboard_state = cache .apply_diff_to_table::( "profile_dashboard_state", @@ -4412,6 +4466,9 @@ impl __sdk::DbUpdate for DbUpdate { "external_generation_job_event" => db_update .external_generation_job_event .append(__sdk::parse_row_list_as_inserts(table_rows.rows)?), + "external_generation_job_summary" => db_update + .external_generation_job_summary + .append(__sdk::parse_row_list_as_inserts(table_rows.rows)?), "feature_gate_config" => db_update .feature_gate_config .append(__sdk::parse_row_list_as_inserts(table_rows.rows)?), @@ -4463,6 +4520,9 @@ impl __sdk::DbUpdate for DbUpdate { "profile_code_operation" => db_update .profile_code_operation .append(__sdk::parse_row_list_as_inserts(table_rows.rows)?), + "profile_daily_free_points" => db_update + .profile_daily_free_points + .append(__sdk::parse_row_list_as_inserts(table_rows.rows)?), "profile_dashboard_state" => db_update .profile_dashboard_state .append(__sdk::parse_row_list_as_inserts(table_rows.rows)?), @@ -4836,6 +4896,9 @@ impl __sdk::DbUpdate for DbUpdate { "external_generation_job_event" => db_update .external_generation_job_event .append(__sdk::parse_row_list_as_deletes(table_rows.rows)?), + "external_generation_job_summary" => db_update + .external_generation_job_summary + .append(__sdk::parse_row_list_as_deletes(table_rows.rows)?), "feature_gate_config" => db_update .feature_gate_config .append(__sdk::parse_row_list_as_deletes(table_rows.rows)?), @@ -4887,6 +4950,9 @@ impl __sdk::DbUpdate for DbUpdate { "profile_code_operation" => db_update .profile_code_operation .append(__sdk::parse_row_list_as_deletes(table_rows.rows)?), + "profile_daily_free_points" => db_update + .profile_daily_free_points + .append(__sdk::parse_row_list_as_deletes(table_rows.rows)?), "profile_dashboard_state" => db_update .profile_dashboard_state .append(__sdk::parse_row_list_as_deletes(table_rows.rows)?), @@ -5159,6 +5225,7 @@ pub struct AppliedDiff<'r> { external_api_key: __sdk::TableAppliedDiff<'r, ExternalApiKey>, external_generation_job: __sdk::TableAppliedDiff<'r, ExternalGenerationJob>, external_generation_job_event: __sdk::TableAppliedDiff<'r, ExternalGenerationJobEvent>, + external_generation_job_summary: __sdk::TableAppliedDiff<'r, ExternalGenerationJobSummary>, feature_gate_config: __sdk::TableAppliedDiff<'r, FeatureGateConfig>, inventory_slot: __sdk::TableAppliedDiff<'r, InventorySlot>, jump_hop_agent_session: __sdk::TableAppliedDiff<'r, JumpHopAgentSessionRow>, @@ -5176,6 +5243,7 @@ pub struct AppliedDiff<'r> { npc_state: __sdk::TableAppliedDiff<'r, NpcState>, player_progression: __sdk::TableAppliedDiff<'r, PlayerProgression>, profile_code_operation: __sdk::TableAppliedDiff<'r, ProfileCodeOperation>, + profile_daily_free_points: __sdk::TableAppliedDiff<'r, ProfileDailyFreePoints>, profile_dashboard_state: __sdk::TableAppliedDiff<'r, ProfileDashboardState>, profile_feedback_submission: __sdk::TableAppliedDiff<'r, ProfileFeedbackSubmission>, profile_invite_code: __sdk::TableAppliedDiff<'r, ProfileInviteCode>, @@ -5516,6 +5584,11 @@ impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> { &self.external_generation_job_event, event, ); + callbacks.invoke_table_row_callbacks::( + "external_generation_job_summary", + &self.external_generation_job_summary, + event, + ); callbacks.invoke_table_row_callbacks::( "feature_gate_config", &self.feature_gate_config, @@ -5597,6 +5670,11 @@ impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> { &self.profile_code_operation, event, ); + callbacks.invoke_table_row_callbacks::( + "profile_daily_free_points", + &self.profile_daily_free_points, + event, + ); callbacks.invoke_table_row_callbacks::( "profile_dashboard_state", &self.profile_dashboard_state, @@ -6179,19 +6257,19 @@ impl __sdk::SubscriptionHandle for SubscriptionHandle { /// either a [`DbConnection`] or an [`EventContext`] and operate on either. pub trait RemoteDbContext: __sdk::DbContext< - DbView = RemoteTables, - Reducers = RemoteReducers, - SubscriptionBuilder = __sdk::SubscriptionBuilder, - > + DbView = RemoteTables, + Reducers = RemoteReducers, + SubscriptionBuilder = __sdk::SubscriptionBuilder, +> { } impl< - Ctx: __sdk::DbContext< + Ctx: __sdk::DbContext< DbView = RemoteTables, Reducers = RemoteReducers, SubscriptionBuilder = __sdk::SubscriptionBuilder, >, -> RemoteDbContext for Ctx + > RemoteDbContext for Ctx { } @@ -6637,6 +6715,7 @@ impl __sdk::SpacetimeModule for RemoteModule { external_api_key_table::register_table(client_cache); external_generation_job_table::register_table(client_cache); external_generation_job_event_table::register_table(client_cache); + external_generation_job_summary_table::register_table(client_cache); feature_gate_config_table::register_table(client_cache); inventory_slot_table::register_table(client_cache); jump_hop_agent_session_table::register_table(client_cache); @@ -6654,6 +6733,7 @@ impl __sdk::SpacetimeModule for RemoteModule { npc_state_table::register_table(client_cache); player_progression_table::register_table(client_cache); profile_code_operation_table::register_table(client_cache); + profile_daily_free_points_table::register_table(client_cache); profile_dashboard_state_table::register_table(client_cache); profile_feedback_submission_table::register_table(client_cache); profile_invite_code_table::register_table(client_cache); @@ -6776,6 +6856,7 @@ impl __sdk::SpacetimeModule for RemoteModule { "external_api_key", "external_generation_job", "external_generation_job_event", + "external_generation_job_summary", "feature_gate_config", "inventory_slot", "jump_hop_agent_session", @@ -6793,6 +6874,7 @@ impl __sdk::SpacetimeModule for RemoteModule { "npc_state", "player_progression", "profile_code_operation", + "profile_daily_free_points", "profile_dashboard_state", "profile_feedback_submission", "profile_invite_code", diff --git a/server-rs/crates/spacetime-client/src/module_bindings/accept_quest_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/accept_quest_reducer.rs index 61e6b9c53..dfebf9039 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/accept_quest_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/accept_quest_reducer.rs @@ -47,11 +47,9 @@ pub trait accept_quest { &self, input: QuestRecordInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -60,11 +58,9 @@ impl accept_quest for super::RemoteReducers { &self, input: QuestRecordInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(AcceptQuestArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/acknowledge_external_generation_job_summaries_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/acknowledge_external_generation_job_summaries_and_return_procedure.rs new file mode 100644 index 000000000..5ef0251b0 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/acknowledge_external_generation_job_summaries_and_return_procedure.rs @@ -0,0 +1,62 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +use super::external_generation_job_acknowledge_input_type::ExternalGenerationJobAcknowledgeInput; +use super::external_generation_job_summary_procedure_result_type::ExternalGenerationJobSummaryProcedureResult; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +struct AcknowledgeExternalGenerationJobSummariesAndReturnArgs { + pub input: ExternalGenerationJobAcknowledgeInput, +} + +impl __sdk::InModule for AcknowledgeExternalGenerationJobSummariesAndReturnArgs { + type Module = super::RemoteModule; +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the procedure `acknowledge_external_generation_job_summaries_and_return`. +/// +/// Implemented for [`super::RemoteProcedures`]. +pub trait acknowledge_external_generation_job_summaries_and_return { + fn acknowledge_external_generation_job_summaries_and_return( + &self, + input: ExternalGenerationJobAcknowledgeInput, + ) { + self.acknowledge_external_generation_job_summaries_and_return_then(input, |_, _| {}); + } + + fn acknowledge_external_generation_job_summaries_and_return_then( + &self, + input: ExternalGenerationJobAcknowledgeInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ); +} + +impl acknowledge_external_generation_job_summaries_and_return for super::RemoteProcedures { + fn acknowledge_external_generation_job_summaries_and_return_then( + &self, + input: ExternalGenerationJobAcknowledgeInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ) { + self.imp + .invoke_procedure_with_callback::<_, ExternalGenerationJobSummaryProcedureResult>( + "acknowledge_external_generation_job_summaries_and_return", + AcknowledgeExternalGenerationJobSummariesAndReturnArgs { input }, + __callback, + ); + } +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/acknowledge_external_generation_jobs_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/acknowledge_external_generation_jobs_and_return_procedure.rs index 4c83af9ca..e9b1c18fe 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/acknowledge_external_generation_jobs_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/acknowledge_external_generation_jobs_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait acknowledge_external_generation_jobs_and_return { input: ExternalGenerationJobAcknowledgeInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl acknowledge_external_generation_jobs_and_return for super::RemoteProcedures input: ExternalGenerationJobAcknowledgeInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, ExternalGenerationJobProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/acknowledge_quest_completion_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/acknowledge_quest_completion_reducer.rs index b1419fb7e..6ae2fd100 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/acknowledge_quest_completion_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/acknowledge_quest_completion_reducer.rs @@ -47,11 +47,9 @@ pub trait acknowledge_quest_completion { &self, input: QuestCompletionAckInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -60,11 +58,9 @@ impl acknowledge_quest_completion for super::RemoteReducers { &self, input: QuestCompletionAckInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(AcknowledgeQuestCompletionArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/admin_disable_profile_redeem_code_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/admin_disable_profile_redeem_code_procedure.rs index 9865ace59..bbdaab4f3 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/admin_disable_profile_redeem_code_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/admin_disable_profile_redeem_code_procedure.rs @@ -31,10 +31,10 @@ pub trait admin_disable_profile_redeem_code { input: RuntimeProfileRedeemCodeAdminDisableInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl admin_disable_profile_redeem_code for super::RemoteProcedures { input: RuntimeProfileRedeemCodeAdminDisableInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileRedeemCodeAdminProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/admin_disable_profile_task_config_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/admin_disable_profile_task_config_procedure.rs index 0417bd2e9..c968f950d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/admin_disable_profile_task_config_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/admin_disable_profile_task_config_procedure.rs @@ -31,10 +31,10 @@ pub trait admin_disable_profile_task_config { input: RuntimeProfileTaskConfigAdminDisableInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl admin_disable_profile_task_config for super::RemoteProcedures { input: RuntimeProfileTaskConfigAdminDisableInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileTaskConfigAdminProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/admin_get_profile_wallet_config_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/admin_get_profile_wallet_config_procedure.rs index 1f48e0770..c7c836f1b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/admin_get_profile_wallet_config_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/admin_get_profile_wallet_config_procedure.rs @@ -31,10 +31,10 @@ pub trait admin_get_profile_wallet_config { input: RuntimeProfileWalletConfigAdminGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl admin_get_profile_wallet_config for super::RemoteProcedures { input: RuntimeProfileWalletConfigAdminGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileWalletConfigAdminProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/admin_list_editor_assets_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/admin_list_editor_assets_and_return_procedure.rs index 8e3589adb..cc3c8ecd6 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/admin_list_editor_assets_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/admin_list_editor_assets_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait admin_list_editor_assets_and_return { input: AdminEditorAssetListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl admin_list_editor_assets_and_return for super::RemoteProcedures { input: AdminEditorAssetListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, AdminEditorAssetListProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/admin_list_editor_showcase_assets_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/admin_list_editor_showcase_assets_and_return_procedure.rs index a740930a7..69ce4beca 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/admin_list_editor_showcase_assets_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/admin_list_editor_showcase_assets_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait admin_list_editor_showcase_assets_and_return { input: EditorShowcaseAssetAdminListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl admin_list_editor_showcase_assets_and_return for super::RemoteProcedures { input: EditorShowcaseAssetAdminListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorShowcaseAssetListProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/admin_list_profile_invite_codes_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/admin_list_profile_invite_codes_procedure.rs index 96d2350f5..cdfa27d91 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/admin_list_profile_invite_codes_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/admin_list_profile_invite_codes_procedure.rs @@ -31,10 +31,10 @@ pub trait admin_list_profile_invite_codes { input: RuntimeProfileInviteCodeAdminListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl admin_list_profile_invite_codes for super::RemoteProcedures { input: RuntimeProfileInviteCodeAdminListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileInviteCodeAdminListProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/admin_list_profile_recharge_products_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/admin_list_profile_recharge_products_procedure.rs index a1deed886..e84d4ec62 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/admin_list_profile_recharge_products_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/admin_list_profile_recharge_products_procedure.rs @@ -34,10 +34,10 @@ pub trait admin_list_profile_recharge_products { input: RuntimeProfileRechargeProductAdminListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl admin_list_profile_recharge_products for super::RemoteProcedures { input: RuntimeProfileRechargeProductAdminListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp.invoke_procedure_with_callback::<_, RuntimeProfileRechargeProductAdminListProcedureResult>( "admin_list_profile_recharge_products", diff --git a/server-rs/crates/spacetime-client/src/module_bindings/admin_list_profile_redeem_codes_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/admin_list_profile_redeem_codes_procedure.rs index c7d6a78e8..2c9b9dd73 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/admin_list_profile_redeem_codes_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/admin_list_profile_redeem_codes_procedure.rs @@ -31,10 +31,10 @@ pub trait admin_list_profile_redeem_codes { input: RuntimeProfileRedeemCodeAdminListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl admin_list_profile_redeem_codes for super::RemoteProcedures { input: RuntimeProfileRedeemCodeAdminListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileRedeemCodeAdminListProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/admin_list_profile_task_configs_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/admin_list_profile_task_configs_procedure.rs index a152116df..88ca28d55 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/admin_list_profile_task_configs_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/admin_list_profile_task_configs_procedure.rs @@ -31,10 +31,10 @@ pub trait admin_list_profile_task_configs { input: RuntimeProfileTaskConfigAdminListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl admin_list_profile_task_configs for super::RemoteProcedures { input: RuntimeProfileTaskConfigAdminListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileTaskConfigAdminListProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/admin_list_work_visibility_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/admin_list_work_visibility_procedure.rs index df222e415..72028b5e5 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/admin_list_work_visibility_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/admin_list_work_visibility_procedure.rs @@ -31,10 +31,10 @@ pub trait admin_list_work_visibility { input: AdminWorkVisibilityListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl admin_list_work_visibility for super::RemoteProcedures { input: AdminWorkVisibilityListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, AdminWorkVisibilityListProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/admin_review_editor_showcase_asset_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/admin_review_editor_showcase_asset_and_return_procedure.rs index 71334fc07..29b2ecf60 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/admin_review_editor_showcase_asset_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/admin_review_editor_showcase_asset_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait admin_review_editor_showcase_asset_and_return { input: EditorShowcaseAssetAdminReviewInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl admin_review_editor_showcase_asset_and_return for super::RemoteProcedures { input: EditorShowcaseAssetAdminReviewInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorShowcaseAssetProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/admin_update_work_visibility_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/admin_update_work_visibility_procedure.rs index 4a88c0841..bbc85a892 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/admin_update_work_visibility_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/admin_update_work_visibility_procedure.rs @@ -31,10 +31,10 @@ pub trait admin_update_work_visibility { input: AdminWorkVisibilityUpdateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl admin_update_work_visibility for super::RemoteProcedures { input: AdminWorkVisibilityUpdateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, AdminWorkVisibilityProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_invite_code_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_invite_code_procedure.rs index 2411092d2..3601be97f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_invite_code_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_invite_code_procedure.rs @@ -31,10 +31,10 @@ pub trait admin_upsert_profile_invite_code { input: RuntimeProfileInviteCodeAdminUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl admin_upsert_profile_invite_code for super::RemoteProcedures { input: RuntimeProfileInviteCodeAdminUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileInviteCodeAdminProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_recharge_product_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_recharge_product_procedure.rs index 83941b836..e3f42278b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_recharge_product_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_recharge_product_procedure.rs @@ -34,10 +34,10 @@ pub trait admin_upsert_profile_recharge_product { input: RuntimeProfileRechargeProductAdminUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl admin_upsert_profile_recharge_product for super::RemoteProcedures { input: RuntimeProfileRechargeProductAdminUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileRechargeProductAdminProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_redeem_code_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_redeem_code_procedure.rs index 9c7ae92f1..7e918220f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_redeem_code_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_redeem_code_procedure.rs @@ -31,10 +31,10 @@ pub trait admin_upsert_profile_redeem_code { input: RuntimeProfileRedeemCodeAdminUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl admin_upsert_profile_redeem_code for super::RemoteProcedures { input: RuntimeProfileRedeemCodeAdminUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileRedeemCodeAdminProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_task_config_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_task_config_procedure.rs index b441a8084..a3d3e11a4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_task_config_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_task_config_procedure.rs @@ -31,10 +31,10 @@ pub trait admin_upsert_profile_task_config { input: RuntimeProfileTaskConfigAdminUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl admin_upsert_profile_task_config for super::RemoteProcedures { input: RuntimeProfileTaskConfigAdminUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileTaskConfigAdminProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_wallet_config_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_wallet_config_procedure.rs index 46814d669..b87b6506d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_wallet_config_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/admin_upsert_profile_wallet_config_procedure.rs @@ -34,10 +34,10 @@ pub trait admin_upsert_profile_wallet_config { input: RuntimeProfileWalletConfigAdminUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl admin_upsert_profile_wallet_config for super::RemoteProcedures { input: RuntimeProfileWalletConfigAdminUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileWalletConfigAdminProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/advance_puzzle_clear_next_level_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/advance_puzzle_clear_next_level_procedure.rs index a75b35e46..7ed1e4bc9 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/advance_puzzle_clear_next_level_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/advance_puzzle_clear_next_level_procedure.rs @@ -31,10 +31,10 @@ pub trait advance_puzzle_clear_next_level { input: PuzzleClearRunNextLevelInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl advance_puzzle_clear_next_level for super::RemoteProcedures { input: PuzzleClearRunNextLevelInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleClearRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/advance_puzzle_next_level_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/advance_puzzle_next_level_procedure.rs index 7cb4f8f4d..6d3e9f791 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/advance_puzzle_next_level_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/advance_puzzle_next_level_procedure.rs @@ -31,10 +31,10 @@ pub trait advance_puzzle_next_level { input: PuzzleRunNextLevelInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl advance_puzzle_next_level for super::RemoteProcedures { input: PuzzleRunNextLevelInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/append_ai_text_chunk_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/append_ai_text_chunk_and_return_procedure.rs index 191e2ea7c..11323392d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/append_ai_text_chunk_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/append_ai_text_chunk_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait append_ai_text_chunk_and_return { input: AiTextChunkAppendInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl append_ai_text_chunk_and_return for super::RemoteProcedures { input: AiTextChunkAppendInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, AiTaskProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/append_visual_novel_runtime_history_entry_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/append_visual_novel_runtime_history_entry_procedure.rs index ad1099d0f..4686ba5ca 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/append_visual_novel_runtime_history_entry_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/append_visual_novel_runtime_history_entry_procedure.rs @@ -34,10 +34,10 @@ pub trait append_visual_novel_runtime_history_entry { input: VisualNovelRuntimeHistoryAppendInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl append_visual_novel_runtime_history_entry for super::RemoteProcedures { input: VisualNovelRuntimeHistoryAppendInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, VisualNovelHistoryProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/apply_chapter_progression_ledger_entry_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/apply_chapter_progression_ledger_entry_and_return_procedure.rs index 4a9499061..bba4c841b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/apply_chapter_progression_ledger_entry_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/apply_chapter_progression_ledger_entry_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait apply_chapter_progression_ledger_entry_and_return { input: ChapterProgressionLedgerInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl apply_chapter_progression_ledger_entry_and_return for super::RemoteProcedur input: ChapterProgressionLedgerInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, ChapterProgressionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/apply_chapter_progression_ledger_entry_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/apply_chapter_progression_ledger_entry_reducer.rs index 44596083b..98f821ef9 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/apply_chapter_progression_ledger_entry_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/apply_chapter_progression_ledger_entry_reducer.rs @@ -50,11 +50,9 @@ pub trait apply_chapter_progression_ledger_entry { &self, input: ChapterProgressionLedgerInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -63,11 +61,9 @@ impl apply_chapter_progression_ledger_entry for super::RemoteReducers { &self, input: ChapterProgressionLedgerInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp.invoke_reducer_with_callback( ApplyChapterProgressionLedgerEntryArgs { input }, diff --git a/server-rs/crates/spacetime-client/src/module_bindings/apply_inventory_mutation_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/apply_inventory_mutation_reducer.rs index d9b4240d8..91f7d2c06 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/apply_inventory_mutation_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/apply_inventory_mutation_reducer.rs @@ -47,11 +47,9 @@ pub trait apply_inventory_mutation { &self, input: InventoryMutationInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -60,11 +58,9 @@ impl apply_inventory_mutation for super::RemoteReducers { &self, input: InventoryMutationInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(ApplyInventoryMutationArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/apply_quest_signal_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/apply_quest_signal_reducer.rs index 6b4c310ba..afb452b55 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/apply_quest_signal_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/apply_quest_signal_reducer.rs @@ -47,11 +47,9 @@ pub trait apply_quest_signal { &self, input: QuestSignalApplyInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -60,11 +58,9 @@ impl apply_quest_signal for super::RemoteReducers { &self, input: QuestSignalApplyInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(ApplyQuestSignalArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/attach_ai_result_reference_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/attach_ai_result_reference_and_return_procedure.rs index 94d418502..2f3edbe2a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/attach_ai_result_reference_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/attach_ai_result_reference_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait attach_ai_result_reference_and_return { input: AiResultReferenceInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl attach_ai_result_reference_and_return for super::RemoteProcedures { input: AiResultReferenceInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, AiTaskProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/authenticate_external_api_key_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/authenticate_external_api_key_and_return_procedure.rs index 279534f41..4c3aec34c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/authenticate_external_api_key_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/authenticate_external_api_key_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait authenticate_external_api_key_and_return { input: ExternalApiKeyAuthenticateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl authenticate_external_api_key_and_return for super::RemoteProcedures { input: ExternalApiKeyAuthenticateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, ExternalApiKeyProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/authorize_database_migration_operator_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/authorize_database_migration_operator_procedure.rs index ac77f7e80..b58850228 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/authorize_database_migration_operator_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/authorize_database_migration_operator_procedure.rs @@ -34,10 +34,10 @@ pub trait authorize_database_migration_operator { input: DatabaseMigrationAuthorizeOperatorInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl authorize_database_migration_operator for super::RemoteProcedures { input: DatabaseMigrationAuthorizeOperatorInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, DatabaseMigrationOperatorProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/backfill_external_generation_job_summaries_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/backfill_external_generation_job_summaries_and_return_procedure.rs new file mode 100644 index 000000000..8636a1587 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/backfill_external_generation_job_summaries_and_return_procedure.rs @@ -0,0 +1,61 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +use super::external_generation_job_summary_backfill_input_type::ExternalGenerationJobSummaryBackfillInput; +use super::external_generation_job_summary_backfill_procedure_result_type::ExternalGenerationJobSummaryBackfillProcedureResult; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +struct BackfillExternalGenerationJobSummariesAndReturnArgs { + pub input: ExternalGenerationJobSummaryBackfillInput, +} + +impl __sdk::InModule for BackfillExternalGenerationJobSummariesAndReturnArgs { + type Module = super::RemoteModule; +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the procedure `backfill_external_generation_job_summaries_and_return`. +/// +/// Implemented for [`super::RemoteProcedures`]. +pub trait backfill_external_generation_job_summaries_and_return { + fn backfill_external_generation_job_summaries_and_return( + &self, + input: ExternalGenerationJobSummaryBackfillInput, + ) { + self.backfill_external_generation_job_summaries_and_return_then(input, |_, _| {}); + } + + fn backfill_external_generation_job_summaries_and_return_then( + &self, + input: ExternalGenerationJobSummaryBackfillInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ); +} + +impl backfill_external_generation_job_summaries_and_return for super::RemoteProcedures { + fn backfill_external_generation_job_summaries_and_return_then( + &self, + input: ExternalGenerationJobSummaryBackfillInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ) { + self.imp.invoke_procedure_with_callback::<_, ExternalGenerationJobSummaryBackfillProcedureResult>( + "backfill_external_generation_job_summaries_and_return", + BackfillExternalGenerationJobSummariesAndReturnArgs { input, }, + __callback, + ); + } +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/begin_story_session_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/begin_story_session_and_return_procedure.rs index 304b2e0c2..eef3de0f7 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/begin_story_session_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/begin_story_session_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait begin_story_session_and_return { input: StorySessionInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl begin_story_session_and_return for super::RemoteProcedures { input: StorySessionInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, StorySessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/begin_story_session_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/begin_story_session_reducer.rs index 22bc4add7..6a082f419 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/begin_story_session_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/begin_story_session_reducer.rs @@ -47,11 +47,9 @@ pub trait begin_story_session { &self, input: StorySessionInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -60,11 +58,9 @@ impl begin_story_session for super::RemoteReducers { &self, input: StorySessionInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(BeginStorySessionArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/bind_asset_object_to_entity_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/bind_asset_object_to_entity_and_return_procedure.rs index 78c80aee5..b709d5c2f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/bind_asset_object_to_entity_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/bind_asset_object_to_entity_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait bind_asset_object_to_entity_and_return { input: AssetEntityBindingInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl bind_asset_object_to_entity_and_return for super::RemoteProcedures { input: AssetEntityBindingInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, AssetEntityBindingProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/bind_asset_object_to_entity_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/bind_asset_object_to_entity_reducer.rs index caf48b269..b20bc5b23 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/bind_asset_object_to_entity_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/bind_asset_object_to_entity_reducer.rs @@ -47,11 +47,9 @@ pub trait bind_asset_object_to_entity { &self, input: AssetEntityBindingInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -60,11 +58,9 @@ impl bind_asset_object_to_entity for super::RemoteReducers { &self, input: AssetEntityBindingInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(BindAssetObjectToEntityArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/cancel_ai_task_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/cancel_ai_task_and_return_procedure.rs index 0c5dc3eb4..b239e0600 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/cancel_ai_task_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/cancel_ai_task_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait cancel_ai_task_and_return { input: AiTaskCancelInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl cancel_ai_task_and_return for super::RemoteProcedures { input: AiTaskCancelInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, AiTaskProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/checkpoint_wooden_fish_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/checkpoint_wooden_fish_run_procedure.rs index 5bd5a45c7..51aa864f5 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/checkpoint_wooden_fish_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/checkpoint_wooden_fish_run_procedure.rs @@ -31,10 +31,10 @@ pub trait checkpoint_wooden_fish_run { input: WoodenFishRunCheckpointInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl checkpoint_wooden_fish_run for super::RemoteProcedures { input: WoodenFishRunCheckpointInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, WoodenFishRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/claim_external_generation_jobs_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/claim_external_generation_jobs_and_return_procedure.rs index 979ea56cc..6455c7b2e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/claim_external_generation_jobs_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/claim_external_generation_jobs_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait claim_external_generation_jobs_and_return { input: ExternalGenerationJobClaimInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl claim_external_generation_jobs_and_return for super::RemoteProcedures { input: ExternalGenerationJobClaimInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, ExternalGenerationJobProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/claim_profile_recharge_order_expiration_schedule_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/claim_profile_recharge_order_expiration_schedule_and_return_procedure.rs index 93d75e737..93a626b00 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/claim_profile_recharge_order_expiration_schedule_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/claim_profile_recharge_order_expiration_schedule_and_return_procedure.rs @@ -34,10 +34,13 @@ pub trait claim_profile_recharge_order_expiration_schedule_and_return { input: RuntimeProfileRechargeOrderExpirationClaimInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result< + RuntimeProfileRechargeOrderExpirationClaimProcedureResult, + __sdk::InternalError, + >, + ) + Send + + 'static, ); } @@ -47,10 +50,13 @@ impl claim_profile_recharge_order_expiration_schedule_and_return for super::Remo input: RuntimeProfileRechargeOrderExpirationClaimInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result< + RuntimeProfileRechargeOrderExpirationClaimProcedureResult, + __sdk::InternalError, + >, + ) + Send + + 'static, ) { self.imp.invoke_procedure_with_callback::<_, RuntimeProfileRechargeOrderExpirationClaimProcedureResult>( "claim_profile_recharge_order_expiration_schedule_and_return", diff --git a/server-rs/crates/spacetime-client/src/module_bindings/claim_profile_task_reward_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/claim_profile_task_reward_and_return_procedure.rs index ea5010707..5a386f3cd 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/claim_profile_task_reward_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/claim_profile_task_reward_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait claim_profile_task_reward_and_return { input: RuntimeProfileTaskClaimInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl claim_profile_task_reward_and_return for super::RemoteProcedures { input: RuntimeProfileTaskClaimInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileTaskClaimProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/claim_puzzle_background_compile_task_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/claim_puzzle_background_compile_task_procedure.rs index e43e713fe..45b9de6db 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/claim_puzzle_background_compile_task_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/claim_puzzle_background_compile_task_procedure.rs @@ -31,10 +31,10 @@ pub trait claim_puzzle_background_compile_task { input: PuzzleBackgroundCompileTaskClaimInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl claim_puzzle_background_compile_task for super::RemoteProcedures { input: PuzzleBackgroundCompileTaskClaimInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleBackgroundCompileTaskProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/claim_puzzle_work_point_incentive_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/claim_puzzle_work_point_incentive_procedure.rs index 1d7872222..f7ac8d756 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/claim_puzzle_work_point_incentive_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/claim_puzzle_work_point_incentive_procedure.rs @@ -31,10 +31,10 @@ pub trait claim_puzzle_work_point_incentive { input: PuzzleWorkPointIncentiveClaimInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl claim_puzzle_work_point_incentive for super::RemoteProcedures { input: PuzzleWorkPointIncentiveClaimInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/clear_database_migration_import_chunks_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/clear_database_migration_import_chunks_procedure.rs index d05fcdf27..51146e998 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/clear_database_migration_import_chunks_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/clear_database_migration_import_chunks_procedure.rs @@ -34,10 +34,10 @@ pub trait clear_database_migration_import_chunks { input: DatabaseMigrationImportChunksClearInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl clear_database_migration_import_chunks for super::RemoteProcedures { input: DatabaseMigrationImportChunksClearInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, DatabaseMigrationProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/clear_platform_browse_history_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/clear_platform_browse_history_and_return_procedure.rs index c8ba8d497..2e6238453 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/clear_platform_browse_history_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/clear_platform_browse_history_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait clear_platform_browse_history_and_return { input: RuntimeBrowseHistoryClearInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl clear_platform_browse_history_and_return for super::RemoteProcedures { input: RuntimeBrowseHistoryClearInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeBrowseHistoryProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/click_match_3_d_item_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/click_match_3_d_item_procedure.rs index 278845ba6..6070a62d8 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/click_match_3_d_item_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/click_match_3_d_item_procedure.rs @@ -31,10 +31,10 @@ pub trait click_match_3_d_item { input: Match3DRunClickInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl click_match_3_d_item for super::RemoteProcedures { input: Match3DRunClickInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, Match3DClickItemProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/close_profile_recharge_order_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/close_profile_recharge_order_and_return_procedure.rs index 984e99294..461f43399 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/close_profile_recharge_order_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/close_profile_recharge_order_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait close_profile_recharge_order_and_return { input: RuntimeProfileRechargeOrderCloseInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl close_profile_recharge_order_and_return for super::RemoteProcedures { input: RuntimeProfileRechargeOrderCloseInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileRechargeCenterProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/compact_external_generation_job_payloads_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/compact_external_generation_job_payloads_and_return_procedure.rs new file mode 100644 index 000000000..416c06161 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/compact_external_generation_job_payloads_and_return_procedure.rs @@ -0,0 +1,61 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +use super::external_generation_job_payload_compaction_input_type::ExternalGenerationJobPayloadCompactionInput; +use super::external_generation_job_payload_compaction_procedure_result_type::ExternalGenerationJobPayloadCompactionProcedureResult; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +struct CompactExternalGenerationJobPayloadsAndReturnArgs { + pub input: ExternalGenerationJobPayloadCompactionInput, +} + +impl __sdk::InModule for CompactExternalGenerationJobPayloadsAndReturnArgs { + type Module = super::RemoteModule; +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the procedure `compact_external_generation_job_payloads_and_return`. +/// +/// Implemented for [`super::RemoteProcedures`]. +pub trait compact_external_generation_job_payloads_and_return { + fn compact_external_generation_job_payloads_and_return( + &self, + input: ExternalGenerationJobPayloadCompactionInput, + ) { + self.compact_external_generation_job_payloads_and_return_then(input, |_, _| {}); + } + + fn compact_external_generation_job_payloads_and_return_then( + &self, + input: ExternalGenerationJobPayloadCompactionInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ); +} + +impl compact_external_generation_job_payloads_and_return for super::RemoteProcedures { + fn compact_external_generation_job_payloads_and_return_then( + &self, + input: ExternalGenerationJobPayloadCompactionInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ) { + self.imp.invoke_procedure_with_callback::<_, ExternalGenerationJobPayloadCompactionProcedureResult>( + "compact_external_generation_job_payloads_and_return", + CompactExternalGenerationJobPayloadsAndReturnArgs { input, }, + __callback, + ); + } +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/compile_big_fish_draft_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/compile_big_fish_draft_procedure.rs index bafe95a65..6eea6571a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/compile_big_fish_draft_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/compile_big_fish_draft_procedure.rs @@ -31,10 +31,10 @@ pub trait compile_big_fish_draft { input: BigFishDraftCompileInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl compile_big_fish_draft for super::RemoteProcedures { input: BigFishDraftCompileInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BigFishSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/compile_custom_world_published_profile_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/compile_custom_world_published_profile_procedure.rs index 67706fab4..aefbe6029 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/compile_custom_world_published_profile_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/compile_custom_world_published_profile_procedure.rs @@ -34,10 +34,10 @@ pub trait compile_custom_world_published_profile { input: CustomWorldPublishedProfileCompileInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl compile_custom_world_published_profile for super::RemoteProcedures { input: CustomWorldPublishedProfileCompileInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldPublishedProfileCompileResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/compile_jump_hop_draft_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/compile_jump_hop_draft_procedure.rs index f0479afaf..fe89daba5 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/compile_jump_hop_draft_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/compile_jump_hop_draft_procedure.rs @@ -31,10 +31,10 @@ pub trait compile_jump_hop_draft { input: JumpHopDraftCompileInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl compile_jump_hop_draft for super::RemoteProcedures { input: JumpHopDraftCompileInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, JumpHopAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/compile_match_3_d_draft_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/compile_match_3_d_draft_procedure.rs index 1a7a97ed1..d6db07879 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/compile_match_3_d_draft_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/compile_match_3_d_draft_procedure.rs @@ -31,10 +31,10 @@ pub trait compile_match_3_d_draft { input: Match3DDraftCompileInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl compile_match_3_d_draft for super::RemoteProcedures { input: Match3DDraftCompileInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, Match3DAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/compile_puzzle_agent_draft_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/compile_puzzle_agent_draft_procedure.rs index 177c0c40c..7badcdaea 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/compile_puzzle_agent_draft_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/compile_puzzle_agent_draft_procedure.rs @@ -31,10 +31,10 @@ pub trait compile_puzzle_agent_draft { input: PuzzleDraftCompileInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl compile_puzzle_agent_draft for super::RemoteProcedures { input: PuzzleDraftCompileInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/compile_puzzle_clear_draft_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/compile_puzzle_clear_draft_procedure.rs index 79726bb52..ca73cd1ee 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/compile_puzzle_clear_draft_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/compile_puzzle_clear_draft_procedure.rs @@ -31,10 +31,10 @@ pub trait compile_puzzle_clear_draft { input: PuzzleClearDraftCompileInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl compile_puzzle_clear_draft for super::RemoteProcedures { input: PuzzleClearDraftCompileInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleClearAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/compile_square_hole_draft_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/compile_square_hole_draft_procedure.rs index 0b715fc44..ee6766b0b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/compile_square_hole_draft_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/compile_square_hole_draft_procedure.rs @@ -31,10 +31,10 @@ pub trait compile_square_hole_draft { input: SquareHoleDraftCompileInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl compile_square_hole_draft for super::RemoteProcedures { input: SquareHoleDraftCompileInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, SquareHoleAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/compile_visual_novel_work_profile_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/compile_visual_novel_work_profile_procedure.rs index e6d5f25bb..457f98095 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/compile_visual_novel_work_profile_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/compile_visual_novel_work_profile_procedure.rs @@ -31,10 +31,10 @@ pub trait compile_visual_novel_work_profile { input: VisualNovelWorkCompileInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl compile_visual_novel_work_profile for super::RemoteProcedures { input: VisualNovelWorkCompileInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, VisualNovelAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/compile_wooden_fish_draft_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/compile_wooden_fish_draft_procedure.rs index 61c7b013f..99e98ca89 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/compile_wooden_fish_draft_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/compile_wooden_fish_draft_procedure.rs @@ -31,10 +31,10 @@ pub trait compile_wooden_fish_draft { input: WoodenFishDraftCompileInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl compile_wooden_fish_draft for super::RemoteProcedures { input: WoodenFishDraftCompileInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, WoodenFishAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/complete_ai_stage_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/complete_ai_stage_and_return_procedure.rs index e59ab8f0e..51375935c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/complete_ai_stage_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/complete_ai_stage_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait complete_ai_stage_and_return { input: AiStageCompletionInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl complete_ai_stage_and_return for super::RemoteProcedures { input: AiStageCompletionInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, AiTaskProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/complete_ai_task_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/complete_ai_task_and_return_procedure.rs index ca7eab9f8..040af6392 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/complete_ai_task_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/complete_ai_task_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait complete_ai_task_and_return { input: AiTaskFinishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl complete_ai_task_and_return for super::RemoteProcedures { input: AiTaskFinishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, AiTaskProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/complete_external_generation_job_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/complete_external_generation_job_and_return_procedure.rs index cebd89edd..9c923b968 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/complete_external_generation_job_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/complete_external_generation_job_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait complete_external_generation_job_and_return { input: ExternalGenerationJobCompleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl complete_external_generation_job_and_return for super::RemoteProcedures { input: ExternalGenerationJobCompleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, ExternalGenerationJobProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/complete_profile_recharge_order_expiration_schedule_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/complete_profile_recharge_order_expiration_schedule_and_return_procedure.rs index 64040eb0e..b3c8a39e2 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/complete_profile_recharge_order_expiration_schedule_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/complete_profile_recharge_order_expiration_schedule_and_return_procedure.rs @@ -34,13 +34,13 @@ pub trait complete_profile_recharge_order_expiration_schedule_and_return { input: RuntimeProfileRechargeOrderExpirationCompleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result< - RuntimeProfileRechargeOrderExpirationCompleteProcedureResult, - __sdk::InternalError, - >, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result< + RuntimeProfileRechargeOrderExpirationCompleteProcedureResult, + __sdk::InternalError, + >, + ) + Send + + 'static, ); } @@ -50,13 +50,13 @@ impl complete_profile_recharge_order_expiration_schedule_and_return for super::R input: RuntimeProfileRechargeOrderExpirationCompleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result< - RuntimeProfileRechargeOrderExpirationCompleteProcedureResult, - __sdk::InternalError, - >, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result< + RuntimeProfileRechargeOrderExpirationCompleteProcedureResult, + __sdk::InternalError, + >, + ) + Send + + 'static, ) { self.imp.invoke_procedure_with_callback::<_, RuntimeProfileRechargeOrderExpirationCompleteProcedureResult>( "complete_profile_recharge_order_expiration_schedule_and_return", diff --git a/server-rs/crates/spacetime-client/src/module_bindings/confirm_asset_object_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/confirm_asset_object_and_return_procedure.rs index cc65f7445..0b4f26b2b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/confirm_asset_object_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/confirm_asset_object_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait confirm_asset_object_and_return { input: AssetObjectUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl confirm_asset_object_and_return for super::RemoteProcedures { input: AssetObjectUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, AssetObjectProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/confirm_asset_object_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/confirm_asset_object_reducer.rs index f5edb63a4..183c2efa8 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/confirm_asset_object_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/confirm_asset_object_reducer.rs @@ -47,11 +47,9 @@ pub trait confirm_asset_object { &self, input: AssetObjectUpsertInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -60,11 +58,9 @@ impl confirm_asset_object for super::RemoteReducers { &self, input: AssetObjectUpsertInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(ConfirmAssetObjectArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/consume_profile_wallet_points_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/consume_profile_wallet_points_and_return_procedure.rs index 11394b665..3d3e47a20 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/consume_profile_wallet_points_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/consume_profile_wallet_points_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait consume_profile_wallet_points_and_return { input: RuntimeProfileWalletAdjustmentInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl consume_profile_wallet_points_and_return for super::RemoteProcedures { input: RuntimeProfileWalletAdjustmentInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileWalletAdjustmentProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/continue_story_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/continue_story_and_return_procedure.rs index 1c2b51d8d..0d58ec1b8 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/continue_story_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/continue_story_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait continue_story_and_return { input: StoryContinueInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl continue_story_and_return for super::RemoteProcedures { input: StoryContinueInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, StorySessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/continue_story_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/continue_story_reducer.rs index fb35bd1f4..4117cfae5 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/continue_story_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/continue_story_reducer.rs @@ -47,11 +47,9 @@ pub trait continue_story { &self, input: StoryContinueInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -60,11 +58,9 @@ impl continue_story for super::RemoteReducers { &self, input: StoryContinueInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(ContinueStoryArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_ai_task_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_ai_task_and_return_procedure.rs index a2f40fd06..20d8ceee1 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_ai_task_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_ai_task_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait create_ai_task_and_return { input: AiTaskCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl create_ai_task_and_return for super::RemoteProcedures { input: AiTaskCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, AiTaskProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_ai_task_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_ai_task_reducer.rs index b87207f02..213f28e58 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_ai_task_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_ai_task_reducer.rs @@ -47,11 +47,9 @@ pub trait create_ai_task { &self, input: AiTaskCreateInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -60,11 +58,9 @@ impl create_ai_task for super::RemoteReducers { &self, input: AiTaskCreateInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(CreateAiTaskArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_bark_battle_draft_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_bark_battle_draft_procedure.rs index 87e104b6b..dbc6f3174 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_bark_battle_draft_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_bark_battle_draft_procedure.rs @@ -31,10 +31,10 @@ pub trait create_bark_battle_draft { input: BarkBattleDraftCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl create_bark_battle_draft for super::RemoteProcedures { input: BarkBattleDraftCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BarkBattleProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_battle_state_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_battle_state_and_return_procedure.rs index c028ba4e9..ef11107a8 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_battle_state_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_battle_state_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait create_battle_state_and_return { input: BattleStateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl create_battle_state_and_return for super::RemoteProcedures { input: BattleStateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BattleStateProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_battle_state_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_battle_state_reducer.rs index 7f34eab2e..1072f8a53 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_battle_state_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_battle_state_reducer.rs @@ -47,11 +47,9 @@ pub trait create_battle_state { &self, input: BattleStateInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -60,11 +58,9 @@ impl create_battle_state for super::RemoteReducers { &self, input: BattleStateInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(CreateBattleStateArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_big_fish_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_big_fish_session_procedure.rs index 6e48521ad..bc67436e1 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_big_fish_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_big_fish_session_procedure.rs @@ -31,10 +31,10 @@ pub trait create_big_fish_session { input: BigFishSessionCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl create_big_fish_session for super::RemoteProcedures { input: BigFishSessionCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BigFishSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_custom_world_agent_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_custom_world_agent_session_procedure.rs index 96a211624..6a08bcc7a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_custom_world_agent_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_custom_world_agent_session_procedure.rs @@ -31,10 +31,10 @@ pub trait create_custom_world_agent_session { input: CustomWorldAgentSessionCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl create_custom_world_agent_session for super::RemoteProcedures { input: CustomWorldAgentSessionCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_editor_agent_conversation_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_editor_agent_conversation_and_return_procedure.rs index 6e45ee7c2..0317b0856 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_editor_agent_conversation_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_editor_agent_conversation_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait create_editor_agent_conversation_and_return { input: EditorAgentConversationCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl create_editor_agent_conversation_and_return for super::RemoteProcedures { input: EditorAgentConversationCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorAgentConversationProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_editor_asset_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_editor_asset_and_return_procedure.rs index e1926a0ef..8ca475434 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_editor_asset_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_editor_asset_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait create_editor_asset_and_return { input: EditorAssetCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl create_editor_asset_and_return for super::RemoteProcedures { input: EditorAssetCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorAssetProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_editor_asset_folder_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_editor_asset_folder_and_return_procedure.rs index e45dcf833..33ec7f85c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_editor_asset_folder_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_editor_asset_folder_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait create_editor_asset_folder_and_return { input: EditorAssetFolderCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl create_editor_asset_folder_and_return for super::RemoteProcedures { input: EditorAssetFolderCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorAssetFolderProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_editor_project_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_editor_project_and_return_procedure.rs index 7c305c26e..7340d97d3 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_editor_project_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_editor_project_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait create_editor_project_and_return { input: EditorProjectCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl create_editor_project_and_return for super::RemoteProcedures { input: EditorProjectCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorProjectProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_editor_project_resource_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_editor_project_resource_and_return_procedure.rs index 957e3aff0..649d2a3e0 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_editor_project_resource_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_editor_project_resource_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait create_editor_project_resource_and_return { input: EditorProjectResourceCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl create_editor_project_resource_and_return for super::RemoteProcedures { input: EditorProjectResourceCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorProjectResourceProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_external_api_key_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_external_api_key_and_return_procedure.rs index 7dc438368..daf4a676f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_external_api_key_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_external_api_key_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait create_external_api_key_and_return { input: ExternalApiKeyCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl create_external_api_key_and_return for super::RemoteProcedures { input: ExternalApiKeyCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, ExternalApiKeyProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_jump_hop_agent_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_jump_hop_agent_session_procedure.rs index 6bfce7c52..e4eeb9047 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_jump_hop_agent_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_jump_hop_agent_session_procedure.rs @@ -31,10 +31,10 @@ pub trait create_jump_hop_agent_session { input: JumpHopAgentSessionCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl create_jump_hop_agent_session for super::RemoteProcedures { input: JumpHopAgentSessionCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, JumpHopAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_match_3_d_agent_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_match_3_d_agent_session_procedure.rs index 717ef728a..c482c9c6b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_match_3_d_agent_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_match_3_d_agent_session_procedure.rs @@ -31,10 +31,10 @@ pub trait create_match_3_d_agent_session { input: Match3DAgentSessionCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl create_match_3_d_agent_session for super::RemoteProcedures { input: Match3DAgentSessionCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, Match3DAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_profile_recharge_order_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_profile_recharge_order_and_return_procedure.rs index 1c53f6aea..893fbdf68 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_profile_recharge_order_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_profile_recharge_order_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait create_profile_recharge_order_and_return { input: RuntimeProfileRechargeOrderCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl create_profile_recharge_order_and_return for super::RemoteProcedures { input: RuntimeProfileRechargeOrderCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileRechargeCenterProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_puzzle_agent_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_puzzle_agent_session_procedure.rs index 62b770811..9460692b7 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_puzzle_agent_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_puzzle_agent_session_procedure.rs @@ -31,10 +31,10 @@ pub trait create_puzzle_agent_session { input: PuzzleAgentSessionCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl create_puzzle_agent_session for super::RemoteProcedures { input: PuzzleAgentSessionCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_puzzle_clear_agent_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_puzzle_clear_agent_session_procedure.rs index 1059b5c61..382822ef4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_puzzle_clear_agent_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_puzzle_clear_agent_session_procedure.rs @@ -31,10 +31,10 @@ pub trait create_puzzle_clear_agent_session { input: PuzzleClearAgentSessionCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl create_puzzle_clear_agent_session for super::RemoteProcedures { input: PuzzleClearAgentSessionCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleClearAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_square_hole_agent_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_square_hole_agent_session_procedure.rs index d58dbfa6b..cd5403a59 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_square_hole_agent_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_square_hole_agent_session_procedure.rs @@ -31,10 +31,10 @@ pub trait create_square_hole_agent_session { input: SquareHoleAgentSessionCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl create_square_hole_agent_session for super::RemoteProcedures { input: SquareHoleAgentSessionCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, SquareHoleAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_visual_novel_agent_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_visual_novel_agent_session_procedure.rs index c42cc766d..ef82cfc18 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_visual_novel_agent_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_visual_novel_agent_session_procedure.rs @@ -31,10 +31,10 @@ pub trait create_visual_novel_agent_session { input: VisualNovelAgentSessionCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl create_visual_novel_agent_session for super::RemoteProcedures { input: VisualNovelAgentSessionCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, VisualNovelAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/create_wooden_fish_agent_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/create_wooden_fish_agent_session_procedure.rs index ec6bec0d3..aabcde5c6 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/create_wooden_fish_agent_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/create_wooden_fish_agent_session_procedure.rs @@ -31,10 +31,10 @@ pub trait create_wooden_fish_agent_session { input: WoodenFishAgentSessionCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl create_wooden_fish_agent_session for super::RemoteProcedures { input: WoodenFishAgentSessionCreateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, WoodenFishAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/delete_bark_battle_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/delete_bark_battle_work_procedure.rs index f164baeca..934e66021 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/delete_bark_battle_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/delete_bark_battle_work_procedure.rs @@ -31,10 +31,10 @@ pub trait delete_bark_battle_work { input: BarkBattleWorkDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl delete_bark_battle_work for super::RemoteProcedures { input: BarkBattleWorkDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BarkBattleProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/delete_big_fish_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/delete_big_fish_work_procedure.rs index d2be83acb..51cc224f2 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/delete_big_fish_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/delete_big_fish_work_procedure.rs @@ -31,10 +31,10 @@ pub trait delete_big_fish_work { input: BigFishWorkDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl delete_big_fish_work for super::RemoteProcedures { input: BigFishWorkDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BigFishWorksProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/delete_custom_world_agent_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/delete_custom_world_agent_session_procedure.rs index 398878304..27341561e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/delete_custom_world_agent_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/delete_custom_world_agent_session_procedure.rs @@ -31,10 +31,10 @@ pub trait delete_custom_world_agent_session { input: CustomWorldAgentSessionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl delete_custom_world_agent_session for super::RemoteProcedures { input: CustomWorldAgentSessionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldWorksListResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/delete_custom_world_profile_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/delete_custom_world_profile_and_return_procedure.rs index 5dc9da2fd..246d4d003 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/delete_custom_world_profile_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/delete_custom_world_profile_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait delete_custom_world_profile_and_return { input: CustomWorldProfileDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl delete_custom_world_profile_and_return for super::RemoteProcedures { input: CustomWorldProfileDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldProfileListResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/delete_editor_agent_conversation_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/delete_editor_agent_conversation_and_return_procedure.rs index e7bf76bfd..95157cf96 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/delete_editor_agent_conversation_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/delete_editor_agent_conversation_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait delete_editor_agent_conversation_and_return { input: EditorAgentConversationDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl delete_editor_agent_conversation_and_return for super::RemoteProcedures { input: EditorAgentConversationDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorAgentConversationProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/delete_editor_asset_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/delete_editor_asset_and_return_procedure.rs index a7c50753a..4c71ecd53 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/delete_editor_asset_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/delete_editor_asset_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait delete_editor_asset_and_return { input: EditorAssetDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl delete_editor_asset_and_return for super::RemoteProcedures { input: EditorAssetDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorAssetProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/delete_editor_asset_folder_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/delete_editor_asset_folder_and_return_procedure.rs index 15aae6008..6500a7c79 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/delete_editor_asset_folder_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/delete_editor_asset_folder_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait delete_editor_asset_folder_and_return { input: EditorAssetFolderDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl delete_editor_asset_folder_and_return for super::RemoteProcedures { input: EditorAssetFolderDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorAssetFolderProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/delete_editor_project_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/delete_editor_project_and_return_procedure.rs index 4586a8691..529aec929 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/delete_editor_project_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/delete_editor_project_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait delete_editor_project_and_return { input: EditorProjectDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl delete_editor_project_and_return for super::RemoteProcedures { input: EditorProjectDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorProjectDeleteProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/delete_jump_hop_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/delete_jump_hop_work_procedure.rs index 407411c0d..9cad50c50 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/delete_jump_hop_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/delete_jump_hop_work_procedure.rs @@ -31,10 +31,10 @@ pub trait delete_jump_hop_work { input: JumpHopWorkDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl delete_jump_hop_work for super::RemoteProcedures { input: JumpHopWorkDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, JumpHopWorksProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/delete_match_3_d_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/delete_match_3_d_work_procedure.rs index c87cd16a8..c26b2dc81 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/delete_match_3_d_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/delete_match_3_d_work_procedure.rs @@ -31,10 +31,10 @@ pub trait delete_match_3_d_work { input: Match3DWorkDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl delete_match_3_d_work for super::RemoteProcedures { input: Match3DWorkDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, Match3DWorksProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/delete_puzzle_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/delete_puzzle_work_procedure.rs index fc8152c56..5b7e5375d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/delete_puzzle_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/delete_puzzle_work_procedure.rs @@ -31,10 +31,10 @@ pub trait delete_puzzle_work { input: PuzzleWorkDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl delete_puzzle_work for super::RemoteProcedures { input: PuzzleWorkDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleWorksProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/delete_runtime_snapshot_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/delete_runtime_snapshot_and_return_procedure.rs index 9173255ab..6373a19b5 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/delete_runtime_snapshot_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/delete_runtime_snapshot_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait delete_runtime_snapshot_and_return { input: RuntimeSnapshotDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl delete_runtime_snapshot_and_return for super::RemoteProcedures { input: RuntimeSnapshotDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeSnapshotProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/delete_square_hole_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/delete_square_hole_work_procedure.rs index 3a8db794b..3469c3141 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/delete_square_hole_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/delete_square_hole_work_procedure.rs @@ -31,10 +31,10 @@ pub trait delete_square_hole_work { input: SquareHoleWorkDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl delete_square_hole_work for super::RemoteProcedures { input: SquareHoleWorkDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, SquareHoleWorksProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/delete_visual_novel_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/delete_visual_novel_work_procedure.rs index eac6ceacd..61ed99045 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/delete_visual_novel_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/delete_visual_novel_work_procedure.rs @@ -31,10 +31,10 @@ pub trait delete_visual_novel_work { input: VisualNovelWorkDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl delete_visual_novel_work for super::RemoteProcedures { input: VisualNovelWorkDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, VisualNovelWorksProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/delete_wooden_fish_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/delete_wooden_fish_work_procedure.rs index 7ba06da31..c0050f072 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/delete_wooden_fish_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/delete_wooden_fish_work_procedure.rs @@ -31,10 +31,10 @@ pub trait delete_wooden_fish_work { input: WoodenFishWorkDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl delete_wooden_fish_work for super::RemoteProcedures { input: WoodenFishWorkDeleteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, WoodenFishWorksProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/drag_puzzle_piece_or_group_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/drag_puzzle_piece_or_group_procedure.rs index 1207f7b53..daad89bd9 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/drag_puzzle_piece_or_group_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/drag_puzzle_piece_or_group_procedure.rs @@ -31,10 +31,10 @@ pub trait drag_puzzle_piece_or_group { input: PuzzleRunDragInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl drag_puzzle_piece_or_group for super::RemoteProcedures { input: PuzzleRunDragInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/drop_square_hole_shape_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/drop_square_hole_shape_procedure.rs index 4056e06ab..91d6bb611 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/drop_square_hole_shape_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/drop_square_hole_shape_procedure.rs @@ -31,10 +31,10 @@ pub trait drop_square_hole_shape { input: SquareHoleRunDropInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl drop_square_hole_shape for super::RemoteProcedures { input: SquareHoleRunDropInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, SquareHoleDropShapeProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/enqueue_external_generation_job_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/enqueue_external_generation_job_and_return_procedure.rs index e38fdf29d..cd14e1432 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/enqueue_external_generation_job_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/enqueue_external_generation_job_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait enqueue_external_generation_job_and_return { input: ExternalGenerationJobEnqueueInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl enqueue_external_generation_job_and_return for super::RemoteProcedures { input: ExternalGenerationJobEnqueueInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, ExternalGenerationJobProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/ensure_analytics_date_dimension_for_date_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/ensure_analytics_date_dimension_for_date_reducer.rs index a6ea3098f..30b9ba357 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/ensure_analytics_date_dimension_for_date_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/ensure_analytics_date_dimension_for_date_reducer.rs @@ -50,11 +50,9 @@ pub trait ensure_analytics_date_dimension_for_date { &self, input: AnalyticsDateDimensionEnsureInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -63,11 +61,9 @@ impl ensure_analytics_date_dimension_for_date for super::RemoteReducers { &self, input: AnalyticsDateDimensionEnsureInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp.invoke_reducer_with_callback( EnsureAnalyticsDateDimensionForDateArgs { input }, diff --git a/server-rs/crates/spacetime-client/src/module_bindings/execute_custom_world_agent_action_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/execute_custom_world_agent_action_procedure.rs index c10084666..e778877c8 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/execute_custom_world_agent_action_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/execute_custom_world_agent_action_procedure.rs @@ -31,10 +31,10 @@ pub trait execute_custom_world_agent_action { input: CustomWorldAgentActionExecuteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl execute_custom_world_agent_action for super::RemoteProcedures { input: CustomWorldAgentActionExecuteInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldAgentActionExecuteResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/expire_profile_recharge_order_timer_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/expire_profile_recharge_order_timer_reducer.rs index 4ee98c011..b9e5a5db5 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/expire_profile_recharge_order_timer_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/expire_profile_recharge_order_timer_reducer.rs @@ -50,11 +50,9 @@ pub trait expire_profile_recharge_order_timer { &self, timer: ProfileRechargeOrderExpirationTimer, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -63,11 +61,9 @@ impl expire_profile_recharge_order_timer for super::RemoteReducers { &self, timer: ProfileRechargeOrderExpirationTimer, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(ExpireProfileRechargeOrderTimerArgs { timer }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/export_auth_store_projection_from_tables_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/export_auth_store_projection_from_tables_procedure.rs index 9334aa833..ece547ce8 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/export_auth_store_projection_from_tables_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/export_auth_store_projection_from_tables_procedure.rs @@ -27,10 +27,10 @@ pub trait export_auth_store_projection_from_tables { &self, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -39,10 +39,10 @@ impl export_auth_store_projection_from_tables for super::RemoteProcedures { &self, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, AuthStoreProjectionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/export_database_migration_to_file_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/export_database_migration_to_file_procedure.rs index d850737bc..3dfe18f83 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/export_database_migration_to_file_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/export_database_migration_to_file_procedure.rs @@ -31,10 +31,10 @@ pub trait export_database_migration_to_file { input: DatabaseMigrationExportInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl export_database_migration_to_file for super::RemoteProcedures { input: DatabaseMigrationExportInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, DatabaseMigrationProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_payload_compaction_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_payload_compaction_input_type.rs new file mode 100644 index 000000000..06a2f380f --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_payload_compaction_input_type.rs @@ -0,0 +1,18 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct ExternalGenerationJobPayloadCompactionInput { + pub dry_run: bool, + pub limit: u32, + pub cursor_job_id: Option, + pub completed_before_micros: Option, +} + +impl __sdk::InModule for ExternalGenerationJobPayloadCompactionInput { + type Module = super::RemoteModule; +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_payload_compaction_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_payload_compaction_procedure_result_type.rs new file mode 100644 index 000000000..134452fba --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_payload_compaction_procedure_result_type.rs @@ -0,0 +1,26 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct ExternalGenerationJobPayloadCompactionProcedureResult { + pub ok: bool, + pub dry_run: bool, + pub scanned_count: u64, + pub matched_count: u32, + pub updated_count: u32, + pub before_bytes: u64, + pub after_bytes: u64, + pub inline_media_count: u64, + pub invalid_json_count: u32, + pub next_cursor_job_id: Option, + pub has_more: bool, + pub error_message: Option, +} + +impl __sdk::InModule for ExternalGenerationJobPayloadCompactionProcedureResult { + type Module = super::RemoteModule; +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_summary_backfill_input_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_summary_backfill_input_type.rs new file mode 100644 index 000000000..687b3f131 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_summary_backfill_input_type.rs @@ -0,0 +1,18 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct ExternalGenerationJobSummaryBackfillInput { + pub owner_user_id: Option, + pub limit: u32, + pub cursor_job_id: Option, + pub dry_run: bool, +} + +impl __sdk::InModule for ExternalGenerationJobSummaryBackfillInput { + type Module = super::RemoteModule; +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_summary_backfill_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_summary_backfill_procedure_result_type.rs new file mode 100644 index 000000000..9335eca2a --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_summary_backfill_procedure_result_type.rs @@ -0,0 +1,22 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct ExternalGenerationJobSummaryBackfillProcedureResult { + pub ok: bool, + pub dry_run: bool, + pub scanned_count: u64, + pub selected_count: u32, + pub upserted_count: u32, + pub next_cursor_job_id: Option, + pub has_more: bool, + pub error_message: Option, +} + +impl __sdk::InModule for ExternalGenerationJobSummaryBackfillProcedureResult { + type Module = super::RemoteModule; +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_summary_procedure_result_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_summary_procedure_result_type.rs new file mode 100644 index 000000000..b67d01d12 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_summary_procedure_result_type.rs @@ -0,0 +1,24 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +use super::external_generation_job_summary_snapshot_type::ExternalGenerationJobSummarySnapshot; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct ExternalGenerationJobSummaryProcedureResult { + pub ok: bool, + pub job: Option, + pub jobs: Vec, + pub pending_count: u32, + pub running_count: u32, + pub unacknowledged_terminal_count: u32, + pub now_micros: i64, + pub error_message: Option, +} + +impl __sdk::InModule for ExternalGenerationJobSummaryProcedureResult { + type Module = super::RemoteModule; +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_summary_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_summary_snapshot_type.rs new file mode 100644 index 000000000..492102908 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_summary_snapshot_type.rs @@ -0,0 +1,30 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct ExternalGenerationJobSummarySnapshot { + pub job_id: String, + pub job_kind: String, + pub owner_user_id: String, + pub source_module: String, + pub source_entity_id: String, + pub request_label: String, + pub request_prompt: Option, + pub status: String, + pub last_error_message: Option, + pub created_at_micros: i64, + pub started_at_micros: Option, + pub completed_at_micros: Option, + pub updated_at_micros: i64, + pub price_mud_points: u64, + pub refund_ledger_id: Option, + pub notification_acknowledged_at_micros: Option, +} + +impl __sdk::InModule for ExternalGenerationJobSummarySnapshot { + type Module = super::RemoteModule; +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_summary_table.rs b/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_summary_table.rs new file mode 100644 index 000000000..55e14f63a --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_summary_table.rs @@ -0,0 +1,169 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use super::external_generation_job_summary_type::ExternalGenerationJobSummary; +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +/// Table handle for the table `external_generation_job_summary`. +/// +/// Obtain a handle from the [`ExternalGenerationJobSummaryTableAccess::external_generation_job_summary`] method on [`super::RemoteTables`], +/// like `ctx.db.external_generation_job_summary()`. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.external_generation_job_summary().on_insert(...)`. +pub struct ExternalGenerationJobSummaryTableHandle<'ctx> { + imp: __sdk::TableHandle, + ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the table `external_generation_job_summary`. +/// +/// Implemented for [`super::RemoteTables`]. +pub trait ExternalGenerationJobSummaryTableAccess { + #[allow(non_snake_case)] + /// Obtain a [`ExternalGenerationJobSummaryTableHandle`], which mediates access to the table `external_generation_job_summary`. + fn external_generation_job_summary(&self) -> ExternalGenerationJobSummaryTableHandle<'_>; +} + +impl ExternalGenerationJobSummaryTableAccess for super::RemoteTables { + fn external_generation_job_summary(&self) -> ExternalGenerationJobSummaryTableHandle<'_> { + ExternalGenerationJobSummaryTableHandle { + imp: self + .imp + .get_table::("external_generation_job_summary"), + ctx: std::marker::PhantomData, + } + } +} + +pub struct ExternalGenerationJobSummaryInsertCallbackId(__sdk::CallbackId); +pub struct ExternalGenerationJobSummaryDeleteCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::Table for ExternalGenerationJobSummaryTableHandle<'ctx> { + type Row = ExternalGenerationJobSummary; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { + self.imp.count() + } + fn iter(&self) -> impl Iterator + '_ { + self.imp.iter() + } + + type InsertCallbackId = ExternalGenerationJobSummaryInsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ExternalGenerationJobSummaryInsertCallbackId { + ExternalGenerationJobSummaryInsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: ExternalGenerationJobSummaryInsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } + + type DeleteCallbackId = ExternalGenerationJobSummaryDeleteCallbackId; + + fn on_delete( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ExternalGenerationJobSummaryDeleteCallbackId { + ExternalGenerationJobSummaryDeleteCallbackId(self.imp.on_delete(Box::new(callback))) + } + + fn remove_on_delete(&self, callback: ExternalGenerationJobSummaryDeleteCallbackId) { + self.imp.remove_on_delete(callback.0) + } +} + +pub struct ExternalGenerationJobSummaryUpdateCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::TableWithPrimaryKey for ExternalGenerationJobSummaryTableHandle<'ctx> { + type UpdateCallbackId = ExternalGenerationJobSummaryUpdateCallbackId; + + fn on_update( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row, &Self::Row) + Send + 'static, + ) -> ExternalGenerationJobSummaryUpdateCallbackId { + ExternalGenerationJobSummaryUpdateCallbackId(self.imp.on_update(Box::new(callback))) + } + + fn remove_on_update(&self, callback: ExternalGenerationJobSummaryUpdateCallbackId) { + self.imp.remove_on_update(callback.0) + } +} + +/// Access to the `job_id` unique index on the table `external_generation_job_summary`, +/// which allows point queries on the field of the same name +/// via the [`ExternalGenerationJobSummaryJobIdUnique::find`] method. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.external_generation_job_summary().job_id().find(...)`. +pub struct ExternalGenerationJobSummaryJobIdUnique<'ctx> { + imp: __sdk::UniqueConstraintHandle, + phantom: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +impl<'ctx> ExternalGenerationJobSummaryTableHandle<'ctx> { + /// Get a handle on the `job_id` unique index on the table `external_generation_job_summary`. + pub fn job_id(&self) -> ExternalGenerationJobSummaryJobIdUnique<'ctx> { + ExternalGenerationJobSummaryJobIdUnique { + imp: self.imp.get_unique_constraint::("job_id"), + phantom: std::marker::PhantomData, + } + } +} + +impl<'ctx> ExternalGenerationJobSummaryJobIdUnique<'ctx> { + /// Find the subscribed row whose `job_id` column value is equal to `col_val`, + /// if such a row is present in the client cache. + pub fn find(&self, col_val: &String) -> Option { + self.imp.find(col_val) + } +} + +#[doc(hidden)] +pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { + let _table = client_cache + .get_or_make_table::("external_generation_job_summary"); + _table.add_unique_constraint::("job_id", |row| &row.job_id); +} + +#[doc(hidden)] +pub(super) fn parse_table_update( + raw_updates: __ws::v2::TableUpdate, +) -> __sdk::Result<__sdk::TableUpdate> { + __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { + __sdk::InternalError::failed_parse( + "TableUpdate", + "TableUpdate", + ) + .with_cause(e) + .into() + }) +} + +#[allow(non_camel_case_types)] +/// Extension trait for query builder access to the table `ExternalGenerationJobSummary`. +/// +/// Implemented for [`__sdk::QueryTableAccessor`]. +pub trait external_generation_job_summaryQueryTableAccess { + #[allow(non_snake_case)] + /// Get a query builder for the table `ExternalGenerationJobSummary`. + fn external_generation_job_summary( + &self, + ) -> __sdk::__query_builder::Table; +} + +impl external_generation_job_summaryQueryTableAccess for __sdk::QueryTableAccessor { + fn external_generation_job_summary( + &self, + ) -> __sdk::__query_builder::Table { + __sdk::__query_builder::Table::new("external_generation_job_summary") + } +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_summary_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_summary_type.rs new file mode 100644 index 000000000..685be2b15 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/external_generation_job_summary_type.rs @@ -0,0 +1,103 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct ExternalGenerationJobSummary { + pub job_id: String, + pub job_kind: String, + pub owner_user_id: String, + pub source_module: String, + pub source_entity_id: String, + pub request_label: String, + pub request_prompt: Option, + pub status: String, + pub last_error_message: Option, + pub created_at: __sdk::Timestamp, + pub started_at: Option<__sdk::Timestamp>, + pub completed_at: Option<__sdk::Timestamp>, + pub updated_at: __sdk::Timestamp, + pub price_mud_points: u64, + pub refund_ledger_id: Option, + pub notification_acknowledged_at: Option<__sdk::Timestamp>, +} + +impl __sdk::InModule for ExternalGenerationJobSummary { + type Module = super::RemoteModule; +} + +/// Column accessor struct for the table `ExternalGenerationJobSummary`. +/// +/// Provides typed access to columns for query building. +pub struct ExternalGenerationJobSummaryCols { + pub job_id: __sdk::__query_builder::Col, + pub job_kind: __sdk::__query_builder::Col, + pub owner_user_id: __sdk::__query_builder::Col, + pub source_module: __sdk::__query_builder::Col, + pub source_entity_id: __sdk::__query_builder::Col, + pub request_label: __sdk::__query_builder::Col, + pub request_prompt: __sdk::__query_builder::Col>, + pub status: __sdk::__query_builder::Col, + pub last_error_message: + __sdk::__query_builder::Col>, + pub created_at: __sdk::__query_builder::Col, + pub started_at: + __sdk::__query_builder::Col>, + pub completed_at: + __sdk::__query_builder::Col>, + pub updated_at: __sdk::__query_builder::Col, + pub price_mud_points: __sdk::__query_builder::Col, + pub refund_ledger_id: __sdk::__query_builder::Col>, + pub notification_acknowledged_at: + __sdk::__query_builder::Col>, +} + +impl __sdk::__query_builder::HasCols for ExternalGenerationJobSummary { + type Cols = ExternalGenerationJobSummaryCols; + fn cols(table_name: &'static str) -> Self::Cols { + ExternalGenerationJobSummaryCols { + job_id: __sdk::__query_builder::Col::new(table_name, "job_id"), + job_kind: __sdk::__query_builder::Col::new(table_name, "job_kind"), + owner_user_id: __sdk::__query_builder::Col::new(table_name, "owner_user_id"), + source_module: __sdk::__query_builder::Col::new(table_name, "source_module"), + source_entity_id: __sdk::__query_builder::Col::new(table_name, "source_entity_id"), + request_label: __sdk::__query_builder::Col::new(table_name, "request_label"), + request_prompt: __sdk::__query_builder::Col::new(table_name, "request_prompt"), + status: __sdk::__query_builder::Col::new(table_name, "status"), + last_error_message: __sdk::__query_builder::Col::new(table_name, "last_error_message"), + created_at: __sdk::__query_builder::Col::new(table_name, "created_at"), + started_at: __sdk::__query_builder::Col::new(table_name, "started_at"), + completed_at: __sdk::__query_builder::Col::new(table_name, "completed_at"), + updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + price_mud_points: __sdk::__query_builder::Col::new(table_name, "price_mud_points"), + refund_ledger_id: __sdk::__query_builder::Col::new(table_name, "refund_ledger_id"), + notification_acknowledged_at: __sdk::__query_builder::Col::new( + table_name, + "notification_acknowledged_at", + ), + } + } +} + +/// Indexed column accessor struct for the table `ExternalGenerationJobSummary`. +/// +/// Provides typed access to indexed columns for query building. +pub struct ExternalGenerationJobSummaryIxCols { + pub job_id: __sdk::__query_builder::IxCol, + pub owner_user_id: __sdk::__query_builder::IxCol, +} + +impl __sdk::__query_builder::HasIxCols for ExternalGenerationJobSummary { + type IxCols = ExternalGenerationJobSummaryIxCols; + fn ix_cols(table_name: &'static str) -> Self::IxCols { + ExternalGenerationJobSummaryIxCols { + job_id: __sdk::__query_builder::IxCol::new(table_name, "job_id"), + owner_user_id: __sdk::__query_builder::IxCol::new(table_name, "owner_user_id"), + } + } +} + +impl __sdk::__query_builder::CanBeLookupTable for ExternalGenerationJobSummary {} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/fail_ai_task_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/fail_ai_task_and_return_procedure.rs index 3194799b9..46090a010 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/fail_ai_task_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/fail_ai_task_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait fail_ai_task_and_return { input: AiTaskFailureInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl fail_ai_task_and_return for super::RemoteProcedures { input: AiTaskFailureInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, AiTaskProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/fail_external_generation_job_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/fail_external_generation_job_and_return_procedure.rs index 92be5608f..46c1f8846 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/fail_external_generation_job_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/fail_external_generation_job_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait fail_external_generation_job_and_return { input: ExternalGenerationJobFailInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl fail_external_generation_job_and_return for super::RemoteProcedures { input: ExternalGenerationJobFailInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, ExternalGenerationJobProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/finalize_big_fish_agent_message_turn_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/finalize_big_fish_agent_message_turn_procedure.rs index a2dd9fd57..9f5c8e7a6 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/finalize_big_fish_agent_message_turn_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/finalize_big_fish_agent_message_turn_procedure.rs @@ -31,10 +31,10 @@ pub trait finalize_big_fish_agent_message_turn { input: BigFishMessageFinalizeInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl finalize_big_fish_agent_message_turn for super::RemoteProcedures { input: BigFishMessageFinalizeInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BigFishSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/finalize_custom_world_agent_message_turn_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/finalize_custom_world_agent_message_turn_procedure.rs index f670f9b6f..fad75a7be 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/finalize_custom_world_agent_message_turn_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/finalize_custom_world_agent_message_turn_procedure.rs @@ -34,10 +34,10 @@ pub trait finalize_custom_world_agent_message_turn { input: CustomWorldAgentMessageFinalizeInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl finalize_custom_world_agent_message_turn for super::RemoteProcedures { input: CustomWorldAgentMessageFinalizeInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldAgentOperationProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/finalize_match_3_d_agent_message_turn_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/finalize_match_3_d_agent_message_turn_procedure.rs index ea0ec2258..9d51ab95d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/finalize_match_3_d_agent_message_turn_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/finalize_match_3_d_agent_message_turn_procedure.rs @@ -31,10 +31,10 @@ pub trait finalize_match_3_d_agent_message_turn { input: Match3DAgentMessageFinalizeInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl finalize_match_3_d_agent_message_turn for super::RemoteProcedures { input: Match3DAgentMessageFinalizeInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, Match3DAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/finalize_puzzle_agent_message_turn_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/finalize_puzzle_agent_message_turn_procedure.rs index 7f06aafae..0014d3943 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/finalize_puzzle_agent_message_turn_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/finalize_puzzle_agent_message_turn_procedure.rs @@ -31,10 +31,10 @@ pub trait finalize_puzzle_agent_message_turn { input: PuzzleAgentMessageFinalizeInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl finalize_puzzle_agent_message_turn for super::RemoteProcedures { input: PuzzleAgentMessageFinalizeInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/finalize_square_hole_agent_message_turn_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/finalize_square_hole_agent_message_turn_procedure.rs index 350f160de..75808f109 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/finalize_square_hole_agent_message_turn_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/finalize_square_hole_agent_message_turn_procedure.rs @@ -31,10 +31,10 @@ pub trait finalize_square_hole_agent_message_turn { input: SquareHoleAgentMessageFinalizeInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl finalize_square_hole_agent_message_turn for super::RemoteProcedures { input: SquareHoleAgentMessageFinalizeInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, SquareHoleAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/finalize_visual_novel_agent_message_turn_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/finalize_visual_novel_agent_message_turn_procedure.rs index 08b475605..5305f8e8b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/finalize_visual_novel_agent_message_turn_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/finalize_visual_novel_agent_message_turn_procedure.rs @@ -34,10 +34,10 @@ pub trait finalize_visual_novel_agent_message_turn { input: VisualNovelAgentMessageFinalizeInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl finalize_visual_novel_agent_message_turn for super::RemoteProcedures { input: VisualNovelAgentMessageFinalizeInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, VisualNovelAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/finish_bark_battle_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/finish_bark_battle_run_procedure.rs index 28fc7ef3a..8eeaf0c11 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/finish_bark_battle_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/finish_bark_battle_run_procedure.rs @@ -31,10 +31,10 @@ pub trait finish_bark_battle_run { input: BarkBattleRunFinishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl finish_bark_battle_run for super::RemoteProcedures { input: BarkBattleRunFinishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BarkBattleProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/finish_match_3_d_time_up_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/finish_match_3_d_time_up_procedure.rs index bd8496313..0d68dbd95 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/finish_match_3_d_time_up_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/finish_match_3_d_time_up_procedure.rs @@ -31,10 +31,10 @@ pub trait finish_match_3_d_time_up { input: Match3DRunTimeUpInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl finish_match_3_d_time_up for super::RemoteProcedures { input: Match3DRunTimeUpInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, Match3DRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/finish_square_hole_time_up_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/finish_square_hole_time_up_procedure.rs index 3ca46a0d7..f3b5ffc32 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/finish_square_hole_time_up_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/finish_square_hole_time_up_procedure.rs @@ -31,10 +31,10 @@ pub trait finish_square_hole_time_up { input: SquareHoleRunTimeUpInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl finish_square_hole_time_up for super::RemoteProcedures { input: SquareHoleRunTimeUpInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, SquareHoleRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/finish_wooden_fish_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/finish_wooden_fish_run_procedure.rs index 30e452e1b..240541fb7 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/finish_wooden_fish_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/finish_wooden_fish_run_procedure.rs @@ -31,10 +31,10 @@ pub trait finish_wooden_fish_run { input: WoodenFishRunFinishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl finish_wooden_fish_run for super::RemoteProcedures { input: WoodenFishRunFinishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, WoodenFishRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/generate_big_fish_asset_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/generate_big_fish_asset_procedure.rs index 144c5d40b..1a87c951d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/generate_big_fish_asset_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/generate_big_fish_asset_procedure.rs @@ -31,10 +31,10 @@ pub trait generate_big_fish_asset { input: BigFishAssetGenerateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl generate_big_fish_asset for super::RemoteProcedures { input: BigFishAssetGenerateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BigFishSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_bark_battle_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_bark_battle_run_procedure.rs index bec98ca83..87c3f82ad 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_bark_battle_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_bark_battle_run_procedure.rs @@ -31,10 +31,10 @@ pub trait get_bark_battle_run { input: BarkBattleRunGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_bark_battle_run for super::RemoteProcedures { input: BarkBattleRunGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BarkBattleProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_bark_battle_runtime_config_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_bark_battle_runtime_config_procedure.rs index 6e4364f2e..f163d3b39 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_bark_battle_runtime_config_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_bark_battle_runtime_config_procedure.rs @@ -31,10 +31,10 @@ pub trait get_bark_battle_runtime_config { input: BarkBattleRuntimeConfigGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_bark_battle_runtime_config for super::RemoteProcedures { input: BarkBattleRuntimeConfigGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BarkBattleProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_battle_state_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_battle_state_procedure.rs index 0fcc276ca..a737fbdf0 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_battle_state_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_battle_state_procedure.rs @@ -31,10 +31,10 @@ pub trait get_battle_state { input: BattleStateQueryInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_battle_state for super::RemoteProcedures { input: BattleStateQueryInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BattleStateProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_big_fish_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_big_fish_run_procedure.rs index 9a601ff29..867a6759b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_big_fish_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_big_fish_run_procedure.rs @@ -31,10 +31,10 @@ pub trait get_big_fish_run { input: BigFishRunGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_big_fish_run for super::RemoteProcedures { input: BigFishRunGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BigFishRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_big_fish_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_big_fish_session_procedure.rs index 7f52f94b8..0b0d78f1d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_big_fish_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_big_fish_session_procedure.rs @@ -31,10 +31,10 @@ pub trait get_big_fish_session { input: BigFishSessionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_big_fish_session for super::RemoteProcedures { input: BigFishSessionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BigFishSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_chapter_progression_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_chapter_progression_procedure.rs index 18f9ae27b..eef158dce 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_chapter_progression_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_chapter_progression_procedure.rs @@ -31,10 +31,10 @@ pub trait get_chapter_progression { input: ChapterProgressionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_chapter_progression for super::RemoteProcedures { input: ChapterProgressionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, ChapterProgressionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_creation_entry_config_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_creation_entry_config_procedure.rs index 8a3a38dde..eb840cdcb 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_creation_entry_config_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_creation_entry_config_procedure.rs @@ -27,10 +27,10 @@ pub trait get_creation_entry_config { &self, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -39,10 +39,10 @@ impl get_creation_entry_config for super::RemoteProcedures { &self, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CreationEntryConfigProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_card_detail_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_card_detail_procedure.rs index e10343459..11a903298 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_card_detail_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_card_detail_procedure.rs @@ -31,10 +31,10 @@ pub trait get_custom_world_agent_card_detail { input: CustomWorldAgentCardDetailGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_custom_world_agent_card_detail for super::RemoteProcedures { input: CustomWorldAgentCardDetailGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldDraftCardDetailResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_operation_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_operation_procedure.rs index cce1dbb53..1c4ffd6af 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_operation_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_operation_procedure.rs @@ -31,10 +31,10 @@ pub trait get_custom_world_agent_operation { input: CustomWorldAgentOperationGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_custom_world_agent_operation for super::RemoteProcedures { input: CustomWorldAgentOperationGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldAgentOperationProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_session_procedure.rs index f4b678e9c..212987e41 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_agent_session_procedure.rs @@ -31,10 +31,10 @@ pub trait get_custom_world_agent_session { input: CustomWorldAgentSessionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_custom_world_agent_session for super::RemoteProcedures { input: CustomWorldAgentSessionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_gallery_detail_by_code_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_gallery_detail_by_code_procedure.rs index a387cbaf7..24768c431 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_gallery_detail_by_code_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_gallery_detail_by_code_procedure.rs @@ -31,10 +31,10 @@ pub trait get_custom_world_gallery_detail_by_code { input: CustomWorldGalleryDetailByCodeInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_custom_world_gallery_detail_by_code for super::RemoteProcedures { input: CustomWorldGalleryDetailByCodeInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldLibraryMutationResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_gallery_detail_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_gallery_detail_procedure.rs index d0e029ff8..f5127dcfa 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_gallery_detail_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_gallery_detail_procedure.rs @@ -31,10 +31,10 @@ pub trait get_custom_world_gallery_detail { input: CustomWorldGalleryDetailInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_custom_world_gallery_detail for super::RemoteProcedures { input: CustomWorldGalleryDetailInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldLibraryMutationResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_library_detail_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_library_detail_procedure.rs index 82bd1c3ca..ab99274ae 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_library_detail_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_custom_world_library_detail_procedure.rs @@ -31,10 +31,10 @@ pub trait get_custom_world_library_detail { input: CustomWorldLibraryDetailInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_custom_world_library_detail for super::RemoteProcedures { input: CustomWorldLibraryDetailInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldLibraryMutationResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_editor_agent_conversation_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_editor_agent_conversation_and_return_procedure.rs index e45514af6..79b9f43f1 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_editor_agent_conversation_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_editor_agent_conversation_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait get_editor_agent_conversation_and_return { input: EditorAgentConversationGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_editor_agent_conversation_and_return for super::RemoteProcedures { input: EditorAgentConversationGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorAgentConversationProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_editor_asset_library_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_editor_asset_library_and_return_procedure.rs index c1053c14c..dca803bbf 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_editor_asset_library_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_editor_asset_library_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait get_editor_asset_library_and_return { input: EditorAssetLibraryGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_editor_asset_library_and_return for super::RemoteProcedures { input: EditorAssetLibraryGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorAssetLibraryProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_editor_project_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_editor_project_and_return_procedure.rs index f01b87343..07d5c4fe1 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_editor_project_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_editor_project_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait get_editor_project_and_return { input: EditorProjectGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_editor_project_and_return for super::RemoteProcedures { input: EditorProjectGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorProjectProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_editor_showcase_campaign_config_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_editor_showcase_campaign_config_and_return_procedure.rs index 5d7332290..baeb8c837 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_editor_showcase_campaign_config_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_editor_showcase_campaign_config_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait get_editor_showcase_campaign_config_and_return { input: EditorShowcaseCampaignConfigGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl get_editor_showcase_campaign_config_and_return for super::RemoteProcedures input: EditorShowcaseCampaignConfigGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorShowcaseCampaignConfigProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_external_generation_job_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_external_generation_job_and_return_procedure.rs index 51ce17456..e2bc98a33 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_external_generation_job_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_external_generation_job_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait get_external_generation_job_and_return { input: ExternalGenerationJobGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_external_generation_job_and_return for super::RemoteProcedures { input: ExternalGenerationJobGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, ExternalGenerationJobProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_external_generation_job_summary_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_external_generation_job_summary_and_return_procedure.rs new file mode 100644 index 000000000..2b5703e72 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_external_generation_job_summary_and_return_procedure.rs @@ -0,0 +1,59 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +use super::external_generation_job_get_input_type::ExternalGenerationJobGetInput; +use super::external_generation_job_summary_procedure_result_type::ExternalGenerationJobSummaryProcedureResult; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +struct GetExternalGenerationJobSummaryAndReturnArgs { + pub input: ExternalGenerationJobGetInput, +} + +impl __sdk::InModule for GetExternalGenerationJobSummaryAndReturnArgs { + type Module = super::RemoteModule; +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the procedure `get_external_generation_job_summary_and_return`. +/// +/// Implemented for [`super::RemoteProcedures`]. +pub trait get_external_generation_job_summary_and_return { + fn get_external_generation_job_summary_and_return(&self, input: ExternalGenerationJobGetInput) { + self.get_external_generation_job_summary_and_return_then(input, |_, _| {}); + } + + fn get_external_generation_job_summary_and_return_then( + &self, + input: ExternalGenerationJobGetInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ); +} + +impl get_external_generation_job_summary_and_return for super::RemoteProcedures { + fn get_external_generation_job_summary_and_return_then( + &self, + input: ExternalGenerationJobGetInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ) { + self.imp + .invoke_procedure_with_callback::<_, ExternalGenerationJobSummaryProcedureResult>( + "get_external_generation_job_summary_and_return", + GetExternalGenerationJobSummaryAndReturnArgs { input }, + __callback, + ); + } +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_external_generation_queue_stats_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_external_generation_queue_stats_and_return_procedure.rs index 429d22f4b..9d7a98a05 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_external_generation_queue_stats_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_external_generation_queue_stats_and_return_procedure.rs @@ -27,10 +27,10 @@ pub trait get_external_generation_queue_stats_and_return { &self, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -39,10 +39,10 @@ impl get_external_generation_queue_stats_and_return for super::RemoteProcedures &self, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, ExternalGenerationQueueStatsProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_feature_gate_config_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_feature_gate_config_procedure.rs index ab7b59b18..5e2bb5c7a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_feature_gate_config_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_feature_gate_config_procedure.rs @@ -27,10 +27,10 @@ pub trait get_feature_gate_config { &self, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -39,10 +39,10 @@ impl get_feature_gate_config for super::RemoteProcedures { &self, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, FeatureGateConfigProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_jump_hop_agent_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_jump_hop_agent_session_procedure.rs index 482aa1a5d..fde5cf939 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_jump_hop_agent_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_jump_hop_agent_session_procedure.rs @@ -31,10 +31,10 @@ pub trait get_jump_hop_agent_session { input: JumpHopAgentSessionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_jump_hop_agent_session for super::RemoteProcedures { input: JumpHopAgentSessionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, JumpHopAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_jump_hop_leaderboard_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_jump_hop_leaderboard_procedure.rs index 519e5acd8..93176d49a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_jump_hop_leaderboard_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_jump_hop_leaderboard_procedure.rs @@ -31,10 +31,10 @@ pub trait get_jump_hop_leaderboard { input: JumpHopLeaderboardGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_jump_hop_leaderboard for super::RemoteProcedures { input: JumpHopLeaderboardGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, JumpHopLeaderboardProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_jump_hop_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_jump_hop_run_procedure.rs index 5c301da70..9f641d0fc 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_jump_hop_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_jump_hop_run_procedure.rs @@ -31,10 +31,10 @@ pub trait get_jump_hop_run { input: JumpHopRunGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_jump_hop_run for super::RemoteProcedures { input: JumpHopRunGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, JumpHopRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_jump_hop_work_profile_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_jump_hop_work_profile_procedure.rs index fd1fbd3eb..62515a287 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_jump_hop_work_profile_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_jump_hop_work_profile_procedure.rs @@ -31,10 +31,10 @@ pub trait get_jump_hop_work_profile { input: JumpHopWorkGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_jump_hop_work_profile for super::RemoteProcedures { input: JumpHopWorkGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, JumpHopWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_match_3_d_agent_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_match_3_d_agent_session_procedure.rs index 574012d90..62d093b5d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_match_3_d_agent_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_match_3_d_agent_session_procedure.rs @@ -31,10 +31,10 @@ pub trait get_match_3_d_agent_session { input: Match3DAgentSessionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_match_3_d_agent_session for super::RemoteProcedures { input: Match3DAgentSessionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, Match3DAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_match_3_d_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_match_3_d_run_procedure.rs index 0a4722304..033e620c9 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_match_3_d_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_match_3_d_run_procedure.rs @@ -31,10 +31,10 @@ pub trait get_match_3_d_run { input: Match3DRunGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_match_3_d_run for super::RemoteProcedures { input: Match3DRunGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, Match3DRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_match_3_d_work_detail_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_match_3_d_work_detail_procedure.rs index 0ea9f495b..5a74982b4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_match_3_d_work_detail_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_match_3_d_work_detail_procedure.rs @@ -31,10 +31,10 @@ pub trait get_match_3_d_work_detail { input: Match3DWorkGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_match_3_d_work_detail for super::RemoteProcedures { input: Match3DWorkGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, Match3DWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_player_progression_or_default_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_player_progression_or_default_procedure.rs index 38a1525aa..97c139fbf 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_player_progression_or_default_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_player_progression_or_default_procedure.rs @@ -31,10 +31,10 @@ pub trait get_player_progression_or_default { input: PlayerProgressionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_player_progression_or_default for super::RemoteProcedures { input: PlayerProgressionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PlayerProgressionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_profile_dashboard_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_profile_dashboard_procedure.rs index 6c48fafb3..38200b75a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_profile_dashboard_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_profile_dashboard_procedure.rs @@ -31,10 +31,10 @@ pub trait get_profile_dashboard { input: RuntimeProfileDashboardGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_profile_dashboard for super::RemoteProcedures { input: RuntimeProfileDashboardGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileDashboardProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_profile_play_stats_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_profile_play_stats_procedure.rs index 088f48122..2ad47ed23 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_profile_play_stats_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_profile_play_stats_procedure.rs @@ -31,10 +31,10 @@ pub trait get_profile_play_stats { input: RuntimeProfilePlayStatsGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_profile_play_stats for super::RemoteProcedures { input: RuntimeProfilePlayStatsGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfilePlayStatsProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_profile_recharge_center_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_profile_recharge_center_procedure.rs index 3e42f3d57..bf070c9c8 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_profile_recharge_center_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_profile_recharge_center_procedure.rs @@ -31,10 +31,10 @@ pub trait get_profile_recharge_center { input: RuntimeProfileRechargeCenterGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_profile_recharge_center for super::RemoteProcedures { input: RuntimeProfileRechargeCenterGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileRechargeCenterProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_profile_recharge_order_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_profile_recharge_order_and_return_procedure.rs index f187bc6fb..437f0048b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_profile_recharge_order_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_profile_recharge_order_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait get_profile_recharge_order_and_return { input: RuntimeProfileRechargeOrderGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_profile_recharge_order_and_return for super::RemoteProcedures { input: RuntimeProfileRechargeOrderGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileRechargeCenterProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_profile_referral_invite_center_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_profile_referral_invite_center_procedure.rs index c72214843..2b3dcdadc 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_profile_referral_invite_center_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_profile_referral_invite_center_procedure.rs @@ -31,10 +31,10 @@ pub trait get_profile_referral_invite_center { input: RuntimeReferralInviteCenterGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_profile_referral_invite_center for super::RemoteProcedures { input: RuntimeReferralInviteCenterGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeReferralInviteCenterProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_profile_task_center_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_profile_task_center_procedure.rs index 0aa83260a..105a4f98c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_profile_task_center_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_profile_task_center_procedure.rs @@ -31,10 +31,10 @@ pub trait get_profile_task_center { input: RuntimeProfileTaskCenterGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_profile_task_center for super::RemoteProcedures { input: RuntimeProfileTaskCenterGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileTaskCenterProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_agent_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_agent_session_procedure.rs index 8aa5a78f3..979293827 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_agent_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_agent_session_procedure.rs @@ -31,10 +31,10 @@ pub trait get_puzzle_agent_session { input: PuzzleAgentSessionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_puzzle_agent_session for super::RemoteProcedures { input: PuzzleAgentSessionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_clear_agent_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_clear_agent_session_procedure.rs index 34c2f8e3f..a4d8c2549 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_clear_agent_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_clear_agent_session_procedure.rs @@ -31,10 +31,10 @@ pub trait get_puzzle_clear_agent_session { input: PuzzleClearAgentSessionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_puzzle_clear_agent_session for super::RemoteProcedures { input: PuzzleClearAgentSessionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleClearAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_clear_runtime_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_clear_runtime_run_procedure.rs index 7a196e1ae..7ab18b5fe 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_clear_runtime_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_clear_runtime_run_procedure.rs @@ -31,10 +31,10 @@ pub trait get_puzzle_clear_runtime_run { input: PuzzleClearRunGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_puzzle_clear_runtime_run for super::RemoteProcedures { input: PuzzleClearRunGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleClearRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_clear_work_profile_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_clear_work_profile_procedure.rs index a394e9e85..8e3921ab4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_clear_work_profile_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_clear_work_profile_procedure.rs @@ -31,10 +31,10 @@ pub trait get_puzzle_clear_work_profile { input: PuzzleClearWorkGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_puzzle_clear_work_profile for super::RemoteProcedures { input: PuzzleClearWorkGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleClearWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_gallery_detail_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_gallery_detail_procedure.rs index 85b700817..a1471eb32 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_gallery_detail_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_gallery_detail_procedure.rs @@ -31,10 +31,10 @@ pub trait get_puzzle_gallery_detail { input: PuzzleWorkGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_puzzle_gallery_detail for super::RemoteProcedures { input: PuzzleWorkGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_run_procedure.rs index d09fc2854..2db5ab66a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_run_procedure.rs @@ -31,10 +31,10 @@ pub trait get_puzzle_run { input: PuzzleRunGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_puzzle_run for super::RemoteProcedures { input: PuzzleRunGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_work_detail_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_work_detail_procedure.rs index 0d6c4f703..d36c74171 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_work_detail_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_puzzle_work_detail_procedure.rs @@ -31,10 +31,10 @@ pub trait get_puzzle_work_detail { input: PuzzleWorkGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_puzzle_work_detail for super::RemoteProcedures { input: PuzzleWorkGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_recent_editor_project_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_recent_editor_project_and_return_procedure.rs index ea9b9720c..b16395853 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_recent_editor_project_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_recent_editor_project_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait get_recent_editor_project_and_return { input: EditorProjectGetRecentInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_recent_editor_project_and_return for super::RemoteProcedures { input: EditorProjectGetRecentInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorProjectProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_inventory_state_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_inventory_state_procedure.rs index c8dfefacb..abbf4f20a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_inventory_state_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_inventory_state_procedure.rs @@ -31,10 +31,10 @@ pub trait get_runtime_inventory_state { input: RuntimeInventoryStateQueryInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_runtime_inventory_state for super::RemoteProcedures { input: RuntimeInventoryStateQueryInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeInventoryStateProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_setting_or_default_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_setting_or_default_procedure.rs index 4ca8b03ea..261caed19 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_setting_or_default_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_setting_or_default_procedure.rs @@ -31,10 +31,10 @@ pub trait get_runtime_setting_or_default { input: RuntimeSettingGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_runtime_setting_or_default for super::RemoteProcedures { input: RuntimeSettingGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeSettingProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_snapshot_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_snapshot_procedure.rs index 7f9feb4f8..989fa40e0 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_snapshot_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_runtime_snapshot_procedure.rs @@ -31,10 +31,10 @@ pub trait get_runtime_snapshot { input: RuntimeSnapshotGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_runtime_snapshot for super::RemoteProcedures { input: RuntimeSnapshotGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeSnapshotProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_square_hole_agent_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_square_hole_agent_session_procedure.rs index 1db6459e7..46a172805 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_square_hole_agent_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_square_hole_agent_session_procedure.rs @@ -31,10 +31,10 @@ pub trait get_square_hole_agent_session { input: SquareHoleAgentSessionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_square_hole_agent_session for super::RemoteProcedures { input: SquareHoleAgentSessionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, SquareHoleAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_square_hole_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_square_hole_run_procedure.rs index 5082e7905..6dabdb923 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_square_hole_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_square_hole_run_procedure.rs @@ -31,10 +31,10 @@ pub trait get_square_hole_run { input: SquareHoleRunGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_square_hole_run for super::RemoteProcedures { input: SquareHoleRunGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, SquareHoleRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_square_hole_work_detail_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_square_hole_work_detail_procedure.rs index 67a16a079..78812cf70 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_square_hole_work_detail_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_square_hole_work_detail_procedure.rs @@ -31,10 +31,10 @@ pub trait get_square_hole_work_detail { input: SquareHoleWorkGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_square_hole_work_detail for super::RemoteProcedures { input: SquareHoleWorkGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, SquareHoleWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_story_session_state_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_story_session_state_procedure.rs index 44b48ada6..7e566dc98 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_story_session_state_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_story_session_state_procedure.rs @@ -31,10 +31,10 @@ pub trait get_story_session_state { input: StorySessionStateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_story_session_state for super::RemoteProcedures { input: StorySessionStateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, StorySessionStateProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_visual_novel_agent_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_visual_novel_agent_session_procedure.rs index a4b4b6d1a..7b50fd298 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_visual_novel_agent_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_visual_novel_agent_session_procedure.rs @@ -31,10 +31,10 @@ pub trait get_visual_novel_agent_session { input: VisualNovelAgentSessionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_visual_novel_agent_session for super::RemoteProcedures { input: VisualNovelAgentSessionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, VisualNovelAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_visual_novel_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_visual_novel_run_procedure.rs index 86bdff381..702737fdc 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_visual_novel_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_visual_novel_run_procedure.rs @@ -31,10 +31,10 @@ pub trait get_visual_novel_run { input: VisualNovelRunGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_visual_novel_run for super::RemoteProcedures { input: VisualNovelRunGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, VisualNovelRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_visual_novel_work_detail_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_visual_novel_work_detail_procedure.rs index 64864f299..373121fcc 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_visual_novel_work_detail_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_visual_novel_work_detail_procedure.rs @@ -31,10 +31,10 @@ pub trait get_visual_novel_work_detail { input: VisualNovelWorkGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_visual_novel_work_detail for super::RemoteProcedures { input: VisualNovelWorkGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, VisualNovelWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_wooden_fish_agent_session_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_wooden_fish_agent_session_procedure.rs index e0b80d0e3..8d1983530 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_wooden_fish_agent_session_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_wooden_fish_agent_session_procedure.rs @@ -31,10 +31,10 @@ pub trait get_wooden_fish_agent_session { input: WoodenFishAgentSessionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_wooden_fish_agent_session for super::RemoteProcedures { input: WoodenFishAgentSessionGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, WoodenFishAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_wooden_fish_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_wooden_fish_run_procedure.rs index 8a0e145a5..bb381e482 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_wooden_fish_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_wooden_fish_run_procedure.rs @@ -31,10 +31,10 @@ pub trait get_wooden_fish_run { input: WoodenFishRunGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_wooden_fish_run for super::RemoteProcedures { input: WoodenFishRunGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, WoodenFishRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/get_wooden_fish_work_profile_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/get_wooden_fish_work_profile_procedure.rs index 44f67362d..1621290a7 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/get_wooden_fish_work_profile_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/get_wooden_fish_work_profile_procedure.rs @@ -31,10 +31,10 @@ pub trait get_wooden_fish_work_profile { input: WoodenFishWorkGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl get_wooden_fish_work_profile for super::RemoteProcedures { input: WoodenFishWorkGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, WoodenFishWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/grant_new_user_registration_wallet_reward_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/grant_new_user_registration_wallet_reward_procedure.rs index 71e481514..c1d7b6dea 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/grant_new_user_registration_wallet_reward_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/grant_new_user_registration_wallet_reward_procedure.rs @@ -31,10 +31,10 @@ pub trait grant_new_user_registration_wallet_reward { input: RuntimeProfileDashboardGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl grant_new_user_registration_wallet_reward for super::RemoteProcedures { input: RuntimeProfileDashboardGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileWalletAdjustmentProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/grant_player_progression_experience_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/grant_player_progression_experience_and_return_procedure.rs index a3f2aa9e4..4c67da630 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/grant_player_progression_experience_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/grant_player_progression_experience_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait grant_player_progression_experience_and_return { input: PlayerProgressionGrantInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl grant_player_progression_experience_and_return for super::RemoteProcedures input: PlayerProgressionGrantInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PlayerProgressionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/grant_player_progression_experience_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/grant_player_progression_experience_reducer.rs index bd07115eb..83b48cf76 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/grant_player_progression_experience_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/grant_player_progression_experience_reducer.rs @@ -50,11 +50,9 @@ pub trait grant_player_progression_experience { &self, input: PlayerProgressionGrantInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -63,11 +61,9 @@ impl grant_player_progression_experience for super::RemoteReducers { &self, input: PlayerProgressionGrantInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(GrantPlayerProgressionExperienceArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_from_chunks_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_from_chunks_procedure.rs index 080dda548..731574800 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_from_chunks_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_from_chunks_procedure.rs @@ -31,10 +31,10 @@ pub trait import_database_migration_from_chunks { input: DatabaseMigrationImportChunksInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl import_database_migration_from_chunks for super::RemoteProcedures { input: DatabaseMigrationImportChunksInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, DatabaseMigrationProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_from_file_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_from_file_procedure.rs index 2ce4ee2ad..7b2322ee5 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_from_file_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_from_file_procedure.rs @@ -31,10 +31,10 @@ pub trait import_database_migration_from_file { input: DatabaseMigrationImportInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl import_database_migration_from_file for super::RemoteProcedures { input: DatabaseMigrationImportInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, DatabaseMigrationProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_incremental_from_chunks_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_incremental_from_chunks_procedure.rs index bbe493578..51ff565c0 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_incremental_from_chunks_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_incremental_from_chunks_procedure.rs @@ -34,10 +34,10 @@ pub trait import_database_migration_incremental_from_chunks { input: DatabaseMigrationImportChunksInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl import_database_migration_incremental_from_chunks for super::RemoteProcedur input: DatabaseMigrationImportChunksInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, DatabaseMigrationProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_incremental_from_file_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_incremental_from_file_procedure.rs index f911c87f8..2fc318044 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_incremental_from_file_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/import_database_migration_incremental_from_file_procedure.rs @@ -31,10 +31,10 @@ pub trait import_database_migration_incremental_from_file { input: DatabaseMigrationImportInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl import_database_migration_incremental_from_file for super::RemoteProcedures input: DatabaseMigrationImportInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, DatabaseMigrationProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/jump_hop_jump_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/jump_hop_jump_procedure.rs index 1535f96f7..19cfbec94 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/jump_hop_jump_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/jump_hop_jump_procedure.rs @@ -31,10 +31,10 @@ pub trait jump_hop_jump { input: JumpHopRunJumpInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl jump_hop_jump for super::RemoteProcedures { input: JumpHopRunJumpInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, JumpHopRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_asset_history_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_asset_history_and_return_procedure.rs index ea689b101..bcc2a742c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_asset_history_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_asset_history_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait list_asset_history_and_return { input: AssetHistoryListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl list_asset_history_and_return for super::RemoteProcedures { input: AssetHistoryListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, AssetHistoryListResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_big_fish_works_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_big_fish_works_procedure.rs index 45ba04afd..8e4c21ba6 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_big_fish_works_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_big_fish_works_procedure.rs @@ -31,10 +31,10 @@ pub trait list_big_fish_works { input: BigFishWorksListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl list_big_fish_works for super::RemoteProcedures { input: BigFishWorksListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BigFishWorksProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_gallery_entries_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_gallery_entries_procedure.rs index 01f6cb0ce..63ee059f5 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_gallery_entries_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_gallery_entries_procedure.rs @@ -27,10 +27,10 @@ pub trait list_custom_world_gallery_entries { &self, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -39,10 +39,10 @@ impl list_custom_world_gallery_entries for super::RemoteProcedures { &self, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldGalleryListResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_profiles_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_profiles_procedure.rs index c42ce2c6f..f8834945d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_profiles_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_profiles_procedure.rs @@ -31,10 +31,10 @@ pub trait list_custom_world_profiles { input: CustomWorldProfileListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl list_custom_world_profiles for super::RemoteProcedures { input: CustomWorldProfileListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldProfileListResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_works_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_works_procedure.rs index 77f48ba6f..d469f660c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_works_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_custom_world_works_procedure.rs @@ -31,10 +31,10 @@ pub trait list_custom_world_works { input: CustomWorldWorksListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl list_custom_world_works for super::RemoteProcedures { input: CustomWorldWorksListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldWorksListResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_editor_agent_conversations_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_editor_agent_conversations_and_return_procedure.rs index b1f34b959..148f968cf 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_editor_agent_conversations_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_editor_agent_conversations_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait list_editor_agent_conversations_and_return { input: EditorAgentConversationListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl list_editor_agent_conversations_and_return for super::RemoteProcedures { input: EditorAgentConversationListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorAgentConversationListProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_editor_projects_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_editor_projects_and_return_procedure.rs index c0181cff2..fca9595d0 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_editor_projects_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_editor_projects_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait list_editor_projects_and_return { input: EditorProjectListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl list_editor_projects_and_return for super::RemoteProcedures { input: EditorProjectListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorProjectListProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_external_api_keys_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_external_api_keys_and_return_procedure.rs index d0ab651fb..ca827a562 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_external_api_keys_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_external_api_keys_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait list_external_api_keys_and_return { input: ExternalApiKeyListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl list_external_api_keys_and_return for super::RemoteProcedures { input: ExternalApiKeyListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, ExternalApiKeyProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_external_generation_job_summaries_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_external_generation_job_summaries_and_return_procedure.rs new file mode 100644 index 000000000..47e617b0d --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_external_generation_job_summaries_and_return_procedure.rs @@ -0,0 +1,62 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +use super::external_generation_job_list_input_type::ExternalGenerationJobListInput; +use super::external_generation_job_summary_procedure_result_type::ExternalGenerationJobSummaryProcedureResult; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +struct ListExternalGenerationJobSummariesAndReturnArgs { + pub input: ExternalGenerationJobListInput, +} + +impl __sdk::InModule for ListExternalGenerationJobSummariesAndReturnArgs { + type Module = super::RemoteModule; +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the procedure `list_external_generation_job_summaries_and_return`. +/// +/// Implemented for [`super::RemoteProcedures`]. +pub trait list_external_generation_job_summaries_and_return { + fn list_external_generation_job_summaries_and_return( + &self, + input: ExternalGenerationJobListInput, + ) { + self.list_external_generation_job_summaries_and_return_then(input, |_, _| {}); + } + + fn list_external_generation_job_summaries_and_return_then( + &self, + input: ExternalGenerationJobListInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ); +} + +impl list_external_generation_job_summaries_and_return for super::RemoteProcedures { + fn list_external_generation_job_summaries_and_return_then( + &self, + input: ExternalGenerationJobListInput, + + __callback: impl FnOnce( + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, + ) { + self.imp + .invoke_procedure_with_callback::<_, ExternalGenerationJobSummaryProcedureResult>( + "list_external_generation_job_summaries_and_return", + ListExternalGenerationJobSummariesAndReturnArgs { input }, + __callback, + ); + } +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_external_generation_jobs_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_external_generation_jobs_and_return_procedure.rs index 5164ba9c7..05f3b53ef 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_external_generation_jobs_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_external_generation_jobs_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait list_external_generation_jobs_and_return { input: ExternalGenerationJobListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl list_external_generation_jobs_and_return for super::RemoteProcedures { input: ExternalGenerationJobListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, ExternalGenerationJobProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_jump_hop_works_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_jump_hop_works_procedure.rs index 18b5cce57..96d931c3e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_jump_hop_works_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_jump_hop_works_procedure.rs @@ -31,10 +31,10 @@ pub trait list_jump_hop_works { input: JumpHopWorksListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl list_jump_hop_works for super::RemoteProcedures { input: JumpHopWorksListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, JumpHopWorksProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_match_3_d_works_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_match_3_d_works_procedure.rs index c593e8485..b1477034e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_match_3_d_works_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_match_3_d_works_procedure.rs @@ -31,10 +31,10 @@ pub trait list_match_3_d_works { input: Match3DWorksListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl list_match_3_d_works for super::RemoteProcedures { input: Match3DWorksListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, Match3DWorksProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_platform_browse_history_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_platform_browse_history_procedure.rs index 001766567..0d368a995 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_platform_browse_history_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_platform_browse_history_procedure.rs @@ -31,10 +31,10 @@ pub trait list_platform_browse_history { input: RuntimeBrowseHistoryListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl list_platform_browse_history for super::RemoteProcedures { input: RuntimeBrowseHistoryListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeBrowseHistoryProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_profile_save_archives_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_profile_save_archives_procedure.rs index 1c7176cf7..31c214bb0 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_profile_save_archives_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_profile_save_archives_procedure.rs @@ -31,10 +31,10 @@ pub trait list_profile_save_archives { input: RuntimeProfileSaveArchiveListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl list_profile_save_archives for super::RemoteProcedures { input: RuntimeProfileSaveArchiveListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileSaveArchiveProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_profile_wallet_ledger_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_profile_wallet_ledger_procedure.rs index d51f0df2d..23496701a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_profile_wallet_ledger_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_profile_wallet_ledger_procedure.rs @@ -31,10 +31,10 @@ pub trait list_profile_wallet_ledger { input: RuntimeProfileWalletLedgerListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl list_profile_wallet_ledger for super::RemoteProcedures { input: RuntimeProfileWalletLedgerListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileWalletLedgerProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_public_editor_project_resources_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_public_editor_project_resources_and_return_procedure.rs index b594fe7ff..70b2895cf 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_public_editor_project_resources_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_public_editor_project_resources_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait list_public_editor_project_resources_and_return { input: EditorProjectResourcePublicShowcaseListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl list_public_editor_project_resources_and_return for super::RemoteProcedures input: EditorProjectResourcePublicShowcaseListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorProjectResourceListProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_public_editor_showcase_assets_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_public_editor_showcase_assets_and_return_procedure.rs index b6b5ad8b8..9e357db4f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_public_editor_showcase_assets_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_public_editor_showcase_assets_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait list_public_editor_showcase_assets_and_return { input: EditorShowcaseAssetPublicListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl list_public_editor_showcase_assets_and_return for super::RemoteProcedures { input: EditorShowcaseAssetPublicListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorShowcaseAssetListProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_puzzle_clear_works_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_puzzle_clear_works_procedure.rs index d440e9b8b..c57ebfe45 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_puzzle_clear_works_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_puzzle_clear_works_procedure.rs @@ -31,10 +31,10 @@ pub trait list_puzzle_clear_works { input: PuzzleClearWorksListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl list_puzzle_clear_works for super::RemoteProcedures { input: PuzzleClearWorksListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleClearWorksProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_puzzle_gallery_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_puzzle_gallery_procedure.rs index e62fd064b..553b8e087 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_puzzle_gallery_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_puzzle_gallery_procedure.rs @@ -27,10 +27,10 @@ pub trait list_puzzle_gallery { &self, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -39,10 +39,10 @@ impl list_puzzle_gallery for super::RemoteProcedures { &self, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleWorksProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_puzzle_works_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_puzzle_works_procedure.rs index 1da004e91..844d16dfe 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_puzzle_works_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_puzzle_works_procedure.rs @@ -31,10 +31,10 @@ pub trait list_puzzle_works { input: PuzzleWorksListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl list_puzzle_works for super::RemoteProcedures { input: PuzzleWorksListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleWorksProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_square_hole_works_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_square_hole_works_procedure.rs index 1c706a8cb..0052c502c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_square_hole_works_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_square_hole_works_procedure.rs @@ -31,10 +31,10 @@ pub trait list_square_hole_works { input: SquareHoleWorksListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl list_square_hole_works for super::RemoteProcedures { input: SquareHoleWorksListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, SquareHoleWorksProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_unchecked_expired_profile_recharge_orders_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_unchecked_expired_profile_recharge_orders_procedure.rs index e930565c3..500ca0cf2 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_unchecked_expired_profile_recharge_orders_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_unchecked_expired_profile_recharge_orders_procedure.rs @@ -34,13 +34,13 @@ pub trait list_unchecked_expired_profile_recharge_orders { input: RuntimeProfileRechargeOrderExpirationCheckListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result< - RuntimeProfileRechargeOrderExpirationCheckListProcedureResult, - __sdk::InternalError, - >, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result< + RuntimeProfileRechargeOrderExpirationCheckListProcedureResult, + __sdk::InternalError, + >, + ) + Send + + 'static, ); } @@ -50,13 +50,13 @@ impl list_unchecked_expired_profile_recharge_orders for super::RemoteProcedures input: RuntimeProfileRechargeOrderExpirationCheckListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result< - RuntimeProfileRechargeOrderExpirationCheckListProcedureResult, - __sdk::InternalError, - >, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result< + RuntimeProfileRechargeOrderExpirationCheckListProcedureResult, + __sdk::InternalError, + >, + ) + Send + + 'static, ) { self.imp.invoke_procedure_with_callback::<_, RuntimeProfileRechargeOrderExpirationCheckListProcedureResult>( "list_unchecked_expired_profile_recharge_orders", diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_visual_novel_runtime_history_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_visual_novel_runtime_history_procedure.rs index fdc06a222..680a8455c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_visual_novel_runtime_history_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_visual_novel_runtime_history_procedure.rs @@ -31,10 +31,10 @@ pub trait list_visual_novel_runtime_history { input: VisualNovelRuntimeHistoryListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl list_visual_novel_runtime_history for super::RemoteProcedures { input: VisualNovelRuntimeHistoryListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, VisualNovelHistoryProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_visual_novel_works_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_visual_novel_works_procedure.rs index 1920ee797..be9b9a294 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_visual_novel_works_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_visual_novel_works_procedure.rs @@ -31,10 +31,10 @@ pub trait list_visual_novel_works { input: VisualNovelWorksListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl list_visual_novel_works for super::RemoteProcedures { input: VisualNovelWorksListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, VisualNovelWorksProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/list_wooden_fish_works_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/list_wooden_fish_works_procedure.rs index d449ff80d..87695a8e7 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/list_wooden_fish_works_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/list_wooden_fish_works_procedure.rs @@ -31,10 +31,10 @@ pub trait list_wooden_fish_works { input: WoodenFishWorksListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl list_wooden_fish_works for super::RemoteProcedures { input: WoodenFishWorksListInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, WoodenFishWorksProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/mark_editor_showcase_asset_refunded_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/mark_editor_showcase_asset_refunded_and_return_procedure.rs index a2d7ad962..2da58f2ca 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/mark_editor_showcase_asset_refunded_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/mark_editor_showcase_asset_refunded_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait mark_editor_showcase_asset_refunded_and_return { input: EditorShowcaseAssetRefundMarkInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl mark_editor_showcase_asset_refunded_and_return for super::RemoteProcedures input: EditorShowcaseAssetRefundMarkInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorShowcaseAssetProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/mark_profile_recharge_order_expiration_checked_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/mark_profile_recharge_order_expiration_checked_procedure.rs index 22ec511a5..5eb121685 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/mark_profile_recharge_order_expiration_checked_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/mark_profile_recharge_order_expiration_checked_procedure.rs @@ -34,10 +34,13 @@ pub trait mark_profile_recharge_order_expiration_checked { input: RuntimeProfileRechargeOrderExpirationCheckInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result< + RuntimeProfileRechargeOrderExpirationCheckProcedureResult, + __sdk::InternalError, + >, + ) + Send + + 'static, ); } @@ -47,10 +50,13 @@ impl mark_profile_recharge_order_expiration_checked for super::RemoteProcedures input: RuntimeProfileRechargeOrderExpirationCheckInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result< + RuntimeProfileRechargeOrderExpirationCheckProcedureResult, + __sdk::InternalError, + >, + ) + Send + + 'static, ) { self.imp.invoke_procedure_with_callback::<_, RuntimeProfileRechargeOrderExpirationCheckProcedureResult>( "mark_profile_recharge_order_expiration_checked", diff --git a/server-rs/crates/spacetime-client/src/module_bindings/mark_profile_recharge_order_paid_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/mark_profile_recharge_order_paid_and_return_procedure.rs index f412f184a..09ba81c70 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/mark_profile_recharge_order_paid_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/mark_profile_recharge_order_paid_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait mark_profile_recharge_order_paid_and_return { input: RuntimeProfileRechargeOrderPaidInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl mark_profile_recharge_order_paid_and_return for super::RemoteProcedures { input: RuntimeProfileRechargeOrderPaidInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileRechargeCenterProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/mark_puzzle_clear_level_time_up_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/mark_puzzle_clear_level_time_up_procedure.rs index 99280a6ba..97042b075 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/mark_puzzle_clear_level_time_up_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/mark_puzzle_clear_level_time_up_procedure.rs @@ -31,10 +31,10 @@ pub trait mark_puzzle_clear_level_time_up { input: PuzzleClearRunTimeUpInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl mark_puzzle_clear_level_time_up for super::RemoteProcedures { input: PuzzleClearRunTimeUpInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleClearRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/mark_puzzle_draft_generation_failed_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/mark_puzzle_draft_generation_failed_procedure.rs index ae073d5cb..954b7ddc0 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/mark_puzzle_draft_generation_failed_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/mark_puzzle_draft_generation_failed_procedure.rs @@ -31,10 +31,10 @@ pub trait mark_puzzle_draft_generation_failed { input: PuzzleDraftCompileFailureInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl mark_puzzle_draft_generation_failed for super::RemoteProcedures { input: PuzzleDraftCompileFailureInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/mark_puzzle_level_generation_failed_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/mark_puzzle_level_generation_failed_procedure.rs index 51d799900..1906e8d89 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/mark_puzzle_level_generation_failed_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/mark_puzzle_level_generation_failed_procedure.rs @@ -31,10 +31,10 @@ pub trait mark_puzzle_level_generation_failed { input: PuzzleLevelGenerationFailureInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl mark_puzzle_level_generation_failed for super::RemoteProcedures { input: PuzzleLevelGenerationFailureInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/profile_daily_free_points_table.rs b/server-rs/crates/spacetime-client/src/module_bindings/profile_daily_free_points_table.rs new file mode 100644 index 000000000..dea2cd453 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/profile_daily_free_points_table.rs @@ -0,0 +1,162 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use super::profile_daily_free_points_type::ProfileDailyFreePoints; +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +/// Table handle for the table `profile_daily_free_points`. +/// +/// Obtain a handle from the [`ProfileDailyFreePointsTableAccess::profile_daily_free_points`] method on [`super::RemoteTables`], +/// like `ctx.db.profile_daily_free_points()`. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.profile_daily_free_points().on_insert(...)`. +pub struct ProfileDailyFreePointsTableHandle<'ctx> { + imp: __sdk::TableHandle, + ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the table `profile_daily_free_points`. +/// +/// Implemented for [`super::RemoteTables`]. +pub trait ProfileDailyFreePointsTableAccess { + #[allow(non_snake_case)] + /// Obtain a [`ProfileDailyFreePointsTableHandle`], which mediates access to the table `profile_daily_free_points`. + fn profile_daily_free_points(&self) -> ProfileDailyFreePointsTableHandle<'_>; +} + +impl ProfileDailyFreePointsTableAccess for super::RemoteTables { + fn profile_daily_free_points(&self) -> ProfileDailyFreePointsTableHandle<'_> { + ProfileDailyFreePointsTableHandle { + imp: self + .imp + .get_table::("profile_daily_free_points"), + ctx: std::marker::PhantomData, + } + } +} + +pub struct ProfileDailyFreePointsInsertCallbackId(__sdk::CallbackId); +pub struct ProfileDailyFreePointsDeleteCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::Table for ProfileDailyFreePointsTableHandle<'ctx> { + type Row = ProfileDailyFreePoints; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { + self.imp.count() + } + fn iter(&self) -> impl Iterator + '_ { + self.imp.iter() + } + + type InsertCallbackId = ProfileDailyFreePointsInsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ProfileDailyFreePointsInsertCallbackId { + ProfileDailyFreePointsInsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: ProfileDailyFreePointsInsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } + + type DeleteCallbackId = ProfileDailyFreePointsDeleteCallbackId; + + fn on_delete( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ProfileDailyFreePointsDeleteCallbackId { + ProfileDailyFreePointsDeleteCallbackId(self.imp.on_delete(Box::new(callback))) + } + + fn remove_on_delete(&self, callback: ProfileDailyFreePointsDeleteCallbackId) { + self.imp.remove_on_delete(callback.0) + } +} + +pub struct ProfileDailyFreePointsUpdateCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::TableWithPrimaryKey for ProfileDailyFreePointsTableHandle<'ctx> { + type UpdateCallbackId = ProfileDailyFreePointsUpdateCallbackId; + + fn on_update( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row, &Self::Row) + Send + 'static, + ) -> ProfileDailyFreePointsUpdateCallbackId { + ProfileDailyFreePointsUpdateCallbackId(self.imp.on_update(Box::new(callback))) + } + + fn remove_on_update(&self, callback: ProfileDailyFreePointsUpdateCallbackId) { + self.imp.remove_on_update(callback.0) + } +} + +/// Access to the `user_id` unique index on the table `profile_daily_free_points`, +/// which allows point queries on the field of the same name +/// via the [`ProfileDailyFreePointsUserIdUnique::find`] method. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.profile_daily_free_points().user_id().find(...)`. +pub struct ProfileDailyFreePointsUserIdUnique<'ctx> { + imp: __sdk::UniqueConstraintHandle, + phantom: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +impl<'ctx> ProfileDailyFreePointsTableHandle<'ctx> { + /// Get a handle on the `user_id` unique index on the table `profile_daily_free_points`. + pub fn user_id(&self) -> ProfileDailyFreePointsUserIdUnique<'ctx> { + ProfileDailyFreePointsUserIdUnique { + imp: self.imp.get_unique_constraint::("user_id"), + phantom: std::marker::PhantomData, + } + } +} + +impl<'ctx> ProfileDailyFreePointsUserIdUnique<'ctx> { + /// Find the subscribed row whose `user_id` column value is equal to `col_val`, + /// if such a row is present in the client cache. + pub fn find(&self, col_val: &String) -> Option { + self.imp.find(col_val) + } +} + +#[doc(hidden)] +pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { + let _table = + client_cache.get_or_make_table::("profile_daily_free_points"); + _table.add_unique_constraint::("user_id", |row| &row.user_id); +} + +#[doc(hidden)] +pub(super) fn parse_table_update( + raw_updates: __ws::v2::TableUpdate, +) -> __sdk::Result<__sdk::TableUpdate> { + __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { + __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate") + .with_cause(e) + .into() + }) +} + +#[allow(non_camel_case_types)] +/// Extension trait for query builder access to the table `ProfileDailyFreePoints`. +/// +/// Implemented for [`__sdk::QueryTableAccessor`]. +pub trait profile_daily_free_pointsQueryTableAccess { + #[allow(non_snake_case)] + /// Get a query builder for the table `ProfileDailyFreePoints`. + fn profile_daily_free_points(&self) -> __sdk::__query_builder::Table; +} + +impl profile_daily_free_pointsQueryTableAccess for __sdk::QueryTableAccessor { + fn profile_daily_free_points(&self) -> __sdk::__query_builder::Table { + __sdk::__query_builder::Table::new("profile_daily_free_points") + } +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/profile_daily_free_points_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/profile_daily_free_points_type.rs new file mode 100644 index 000000000..5089ac146 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/profile_daily_free_points_type.rs @@ -0,0 +1,61 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct ProfileDailyFreePoints { + pub user_id: String, + pub day_key: i64, + pub granted_points: u64, + pub remaining_points: u64, + pub updated_at: __sdk::Timestamp, +} + +impl __sdk::InModule for ProfileDailyFreePoints { + type Module = super::RemoteModule; +} + +/// Column accessor struct for the table `ProfileDailyFreePoints`. +/// +/// Provides typed access to columns for query building. +pub struct ProfileDailyFreePointsCols { + pub user_id: __sdk::__query_builder::Col, + pub day_key: __sdk::__query_builder::Col, + pub granted_points: __sdk::__query_builder::Col, + pub remaining_points: __sdk::__query_builder::Col, + pub updated_at: __sdk::__query_builder::Col, +} + +impl __sdk::__query_builder::HasCols for ProfileDailyFreePoints { + type Cols = ProfileDailyFreePointsCols; + fn cols(table_name: &'static str) -> Self::Cols { + ProfileDailyFreePointsCols { + user_id: __sdk::__query_builder::Col::new(table_name, "user_id"), + day_key: __sdk::__query_builder::Col::new(table_name, "day_key"), + granted_points: __sdk::__query_builder::Col::new(table_name, "granted_points"), + remaining_points: __sdk::__query_builder::Col::new(table_name, "remaining_points"), + updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"), + } + } +} + +/// Indexed column accessor struct for the table `ProfileDailyFreePoints`. +/// +/// Provides typed access to indexed columns for query building. +pub struct ProfileDailyFreePointsIxCols { + pub user_id: __sdk::__query_builder::IxCol, +} + +impl __sdk::__query_builder::HasIxCols for ProfileDailyFreePoints { + type IxCols = ProfileDailyFreePointsIxCols; + fn ix_cols(table_name: &'static str) -> Self::IxCols { + ProfileDailyFreePointsIxCols { + user_id: __sdk::__query_builder::IxCol::new(table_name, "user_id"), + } + } +} + +impl __sdk::__query_builder::CanBeLookupTable for ProfileDailyFreePoints {} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/public_work_play_daily_stat_table.rs b/server-rs/crates/spacetime-client/src/module_bindings/public_work_play_daily_stat_table.rs index d0c84769d..8c95aaa8f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/public_work_play_daily_stat_table.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/public_work_play_daily_stat_table.rs @@ -153,7 +153,7 @@ pub trait public_work_play_daily_statQueryTableAccess { #[allow(non_snake_case)] /// Get a query builder for the table `PublicWorkPlayDailyStat`. fn public_work_play_daily_stat(&self) - -> __sdk::__query_builder::Table; + -> __sdk::__query_builder::Table; } impl public_work_play_daily_statQueryTableAccess for __sdk::QueryTableAccessor { diff --git a/server-rs/crates/spacetime-client/src/module_bindings/publish_bark_battle_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/publish_bark_battle_work_procedure.rs index 87884f544..9ea9f0db8 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/publish_bark_battle_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/publish_bark_battle_work_procedure.rs @@ -31,10 +31,10 @@ pub trait publish_bark_battle_work { input: BarkBattleWorkPublishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl publish_bark_battle_work for super::RemoteProcedures { input: BarkBattleWorkPublishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BarkBattleProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/publish_big_fish_game_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/publish_big_fish_game_procedure.rs index d4507ad82..e8007288d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/publish_big_fish_game_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/publish_big_fish_game_procedure.rs @@ -31,10 +31,10 @@ pub trait publish_big_fish_game { input: BigFishPublishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl publish_big_fish_game for super::RemoteProcedures { input: BigFishPublishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BigFishSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_profile_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_profile_and_return_procedure.rs index d57419224..e5434acad 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_profile_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_profile_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait publish_custom_world_profile_and_return { input: CustomWorldProfilePublishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl publish_custom_world_profile_and_return for super::RemoteProcedures { input: CustomWorldProfilePublishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldLibraryMutationResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_profile_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_profile_reducer.rs index 2e2f71f65..84e6b339f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_profile_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_profile_reducer.rs @@ -50,11 +50,9 @@ pub trait publish_custom_world_profile { &self, input: CustomWorldProfilePublishInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -63,11 +61,9 @@ impl publish_custom_world_profile for super::RemoteReducers { &self, input: CustomWorldProfilePublishInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(PublishCustomWorldProfileArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_world_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_world_procedure.rs index 42c76aad3..1eb935a23 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_world_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/publish_custom_world_world_procedure.rs @@ -31,10 +31,10 @@ pub trait publish_custom_world_world { input: CustomWorldPublishWorldInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl publish_custom_world_world for super::RemoteProcedures { input: CustomWorldPublishWorldInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldPublishWorldResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/publish_jump_hop_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/publish_jump_hop_work_procedure.rs index 926aed9b0..a87bb2784 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/publish_jump_hop_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/publish_jump_hop_work_procedure.rs @@ -31,10 +31,10 @@ pub trait publish_jump_hop_work { input: JumpHopWorkPublishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl publish_jump_hop_work for super::RemoteProcedures { input: JumpHopWorkPublishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, JumpHopWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/publish_match_3_d_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/publish_match_3_d_work_procedure.rs index db0c7efe7..65bd160e5 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/publish_match_3_d_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/publish_match_3_d_work_procedure.rs @@ -31,10 +31,10 @@ pub trait publish_match_3_d_work { input: Match3DWorkPublishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl publish_match_3_d_work for super::RemoteProcedures { input: Match3DWorkPublishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, Match3DWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/publish_puzzle_clear_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/publish_puzzle_clear_work_procedure.rs index 55b2ca6d7..c5e2ea820 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/publish_puzzle_clear_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/publish_puzzle_clear_work_procedure.rs @@ -31,10 +31,10 @@ pub trait publish_puzzle_clear_work { input: PuzzleClearWorkPublishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl publish_puzzle_clear_work for super::RemoteProcedures { input: PuzzleClearWorkPublishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleClearWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/publish_puzzle_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/publish_puzzle_work_procedure.rs index 932b66d68..288b44a51 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/publish_puzzle_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/publish_puzzle_work_procedure.rs @@ -31,10 +31,10 @@ pub trait publish_puzzle_work { input: PuzzlePublishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl publish_puzzle_work for super::RemoteProcedures { input: PuzzlePublishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/publish_square_hole_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/publish_square_hole_work_procedure.rs index ad4e944ed..f0c2be486 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/publish_square_hole_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/publish_square_hole_work_procedure.rs @@ -31,10 +31,10 @@ pub trait publish_square_hole_work { input: SquareHoleWorkPublishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl publish_square_hole_work for super::RemoteProcedures { input: SquareHoleWorkPublishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, SquareHoleWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/publish_visual_novel_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/publish_visual_novel_work_procedure.rs index 91c7eabde..7ddf731bc 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/publish_visual_novel_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/publish_visual_novel_work_procedure.rs @@ -31,10 +31,10 @@ pub trait publish_visual_novel_work { input: VisualNovelWorkPublishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl publish_visual_novel_work for super::RemoteProcedures { input: VisualNovelWorkPublishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, VisualNovelWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/publish_wooden_fish_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/publish_wooden_fish_work_procedure.rs index f370ace6a..5dd6555ce 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/publish_wooden_fish_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/publish_wooden_fish_work_procedure.rs @@ -31,10 +31,10 @@ pub trait publish_wooden_fish_work { input: WoodenFishWorkPublishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl publish_wooden_fish_work for super::RemoteProcedures { input: WoodenFishWorkPublishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, WoodenFishWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/put_database_migration_import_chunk_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/put_database_migration_import_chunk_procedure.rs index f3776bfdc..597b25119 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/put_database_migration_import_chunk_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/put_database_migration_import_chunk_procedure.rs @@ -31,10 +31,10 @@ pub trait put_database_migration_import_chunk { input: DatabaseMigrationImportChunkInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl put_database_migration_import_chunk for super::RemoteProcedures { input: DatabaseMigrationImportChunkInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, DatabaseMigrationProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_clear_gallery_view_table.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_clear_gallery_view_table.rs index f41b3c6b4..a5999e301 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_clear_gallery_view_table.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_clear_gallery_view_table.rs @@ -108,7 +108,7 @@ pub trait puzzle_clear_gallery_viewQueryTableAccess { #[allow(non_snake_case)] /// Get a query builder for the table `PuzzleClearGalleryViewRow`. fn puzzle_clear_gallery_view(&self) - -> __sdk::__query_builder::Table; + -> __sdk::__query_builder::Table; } impl puzzle_clear_gallery_viewQueryTableAccess for __sdk::QueryTableAccessor { diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_clear_work_profile_table.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_clear_work_profile_table.rs index 5baf33163..412795e70 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_clear_work_profile_table.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_clear_work_profile_table.rs @@ -153,7 +153,7 @@ pub trait puzzle_clear_work_profileQueryTableAccess { #[allow(non_snake_case)] /// Get a query builder for the table `PuzzleClearWorkProfileRow`. fn puzzle_clear_work_profile(&self) - -> __sdk::__query_builder::Table; + -> __sdk::__query_builder::Table; } impl puzzle_clear_work_profileQueryTableAccess for __sdk::QueryTableAccessor { diff --git a/server-rs/crates/spacetime-client/src/module_bindings/query_analytics_metric_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/query_analytics_metric_procedure.rs index 7973f5469..adc25bfaf 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/query_analytics_metric_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/query_analytics_metric_procedure.rs @@ -31,10 +31,10 @@ pub trait query_analytics_metric { input: AnalyticsMetricQueryInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl query_analytics_metric for super::RemoteProcedures { input: AnalyticsMetricQueryInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, AnalyticsMetricQueryProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/record_big_fish_like_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/record_big_fish_like_procedure.rs index 536aa47d2..0429a9f71 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/record_big_fish_like_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/record_big_fish_like_procedure.rs @@ -31,10 +31,10 @@ pub trait record_big_fish_like { input: BigFishWorkLikeRecordInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl record_big_fish_like for super::RemoteProcedures { input: BigFishWorkLikeRecordInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BigFishWorksProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/record_big_fish_play_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/record_big_fish_play_procedure.rs index 8cf35b2b1..f4cfaa6b0 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/record_big_fish_play_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/record_big_fish_play_procedure.rs @@ -31,10 +31,10 @@ pub trait record_big_fish_play { input: BigFishPlayRecordInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl record_big_fish_play for super::RemoteProcedures { input: BigFishPlayRecordInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BigFishWorksProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/record_custom_world_profile_like_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/record_custom_world_profile_like_procedure.rs index 1cd0aaad9..6ac81dd96 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/record_custom_world_profile_like_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/record_custom_world_profile_like_procedure.rs @@ -31,10 +31,10 @@ pub trait record_custom_world_profile_like { input: CustomWorldProfileLikeRecordInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl record_custom_world_profile_like for super::RemoteProcedures { input: CustomWorldProfileLikeRecordInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldLibraryMutationResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/record_custom_world_profile_play_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/record_custom_world_profile_play_procedure.rs index 2534ee30d..f803e277e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/record_custom_world_profile_play_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/record_custom_world_profile_play_procedure.rs @@ -31,10 +31,10 @@ pub trait record_custom_world_profile_play { input: CustomWorldProfilePlayRecordInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl record_custom_world_profile_play for super::RemoteProcedures { input: CustomWorldProfilePlayRecordInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldLibraryMutationResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/record_daily_login_tracking_event_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/record_daily_login_tracking_event_and_return_procedure.rs index c131f3bab..9365d3359 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/record_daily_login_tracking_event_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/record_daily_login_tracking_event_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait record_daily_login_tracking_event_and_return { input: RuntimeProfileTaskCenterGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl record_daily_login_tracking_event_and_return for super::RemoteProcedures { input: RuntimeProfileTaskCenterGetInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeTrackingEventProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/record_puzzle_work_like_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/record_puzzle_work_like_procedure.rs index fa55cf09d..78dce7f84 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/record_puzzle_work_like_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/record_puzzle_work_like_procedure.rs @@ -31,10 +31,10 @@ pub trait record_puzzle_work_like { input: PuzzleWorkLikeRecordInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl record_puzzle_work_like for super::RemoteProcedures { input: PuzzleWorkLikeRecordInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/record_tracking_event_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/record_tracking_event_and_return_procedure.rs index c09132c01..01361ec79 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/record_tracking_event_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/record_tracking_event_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait record_tracking_event_and_return { input: RuntimeTrackingEventInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl record_tracking_event_and_return for super::RemoteProcedures { input: RuntimeTrackingEventInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeTrackingEventProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/record_tracking_events_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/record_tracking_events_and_return_procedure.rs index 428e378f6..ba28d1a80 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/record_tracking_events_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/record_tracking_events_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait record_tracking_events_and_return { inputs: Vec, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl record_tracking_events_and_return for super::RemoteProcedures { inputs: Vec, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeTrackingEventBatchProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/record_visual_novel_runtime_event_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/record_visual_novel_runtime_event_procedure.rs index 39f3a4319..8c8db759f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/record_visual_novel_runtime_event_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/record_visual_novel_runtime_event_procedure.rs @@ -31,10 +31,10 @@ pub trait record_visual_novel_runtime_event { input: VisualNovelRuntimeEventRecordInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl record_visual_novel_runtime_event for super::RemoteProcedures { input: VisualNovelRuntimeEventRecordInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, VisualNovelRuntimeEventProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/redeem_profile_referral_invite_code_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/redeem_profile_referral_invite_code_procedure.rs index efebd26a0..44354acdf 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/redeem_profile_referral_invite_code_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/redeem_profile_referral_invite_code_procedure.rs @@ -31,10 +31,10 @@ pub trait redeem_profile_referral_invite_code { input: RuntimeReferralRedeemInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl redeem_profile_referral_invite_code for super::RemoteProcedures { input: RuntimeReferralRedeemInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeReferralRedeemProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/redeem_profile_reward_code_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/redeem_profile_reward_code_procedure.rs index 38fc64f51..4d048a49b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/redeem_profile_reward_code_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/redeem_profile_reward_code_procedure.rs @@ -31,10 +31,10 @@ pub trait redeem_profile_reward_code { input: RuntimeProfileRewardCodeRedeemInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl redeem_profile_reward_code for super::RemoteProcedures { input: RuntimeProfileRewardCodeRedeemInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileRewardCodeRedeemProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/refund_profile_wallet_points_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/refund_profile_wallet_points_and_return_procedure.rs index a4bbd3787..fb86172c0 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/refund_profile_wallet_points_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/refund_profile_wallet_points_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait refund_profile_wallet_points_and_return { input: RuntimeProfileWalletAdjustmentInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl refund_profile_wallet_points_and_return for super::RemoteProcedures { input: RuntimeProfileWalletAdjustmentInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileWalletAdjustmentProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/release_puzzle_background_compile_task_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/release_puzzle_background_compile_task_procedure.rs index 983069d66..3b85afae8 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/release_puzzle_background_compile_task_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/release_puzzle_background_compile_task_procedure.rs @@ -34,10 +34,10 @@ pub trait release_puzzle_background_compile_task { input: PuzzleBackgroundCompileTaskReleaseInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl release_puzzle_background_compile_task for super::RemoteProcedures { input: PuzzleBackgroundCompileTaskReleaseInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleBackgroundCompileTaskProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/remix_big_fish_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/remix_big_fish_work_procedure.rs index ff7d14869..7f58adb35 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/remix_big_fish_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/remix_big_fish_work_procedure.rs @@ -31,10 +31,10 @@ pub trait remix_big_fish_work { input: BigFishWorkRemixInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl remix_big_fish_work for super::RemoteProcedures { input: BigFishWorkRemixInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BigFishSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/remix_custom_world_profile_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/remix_custom_world_profile_procedure.rs index 8cd29b12f..93f743837 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/remix_custom_world_profile_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/remix_custom_world_profile_procedure.rs @@ -31,10 +31,10 @@ pub trait remix_custom_world_profile { input: CustomWorldProfileRemixInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl remix_custom_world_profile for super::RemoteProcedures { input: CustomWorldProfileRemixInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldLibraryMutationResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/remix_puzzle_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/remix_puzzle_work_procedure.rs index d25480694..da91b3345 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/remix_puzzle_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/remix_puzzle_work_procedure.rs @@ -31,10 +31,10 @@ pub trait remix_puzzle_work { input: PuzzleWorkRemixInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl remix_puzzle_work for super::RemoteProcedures { input: PuzzleWorkRemixInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/rename_editor_project_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/rename_editor_project_and_return_procedure.rs index 923337242..408122981 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/rename_editor_project_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/rename_editor_project_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait rename_editor_project_and_return { input: EditorProjectRenameInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl rename_editor_project_and_return for super::RemoteProcedures { input: EditorProjectRenameInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorProjectProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/renew_external_generation_job_lease_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/renew_external_generation_job_lease_and_return_procedure.rs index 0b31241b0..4cbd45fc1 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/renew_external_generation_job_lease_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/renew_external_generation_job_lease_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait renew_external_generation_job_lease_and_return { input: ExternalGenerationJobRenewLeaseInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl renew_external_generation_job_lease_and_return for super::RemoteProcedures input: ExternalGenerationJobRenewLeaseInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, ExternalGenerationJobProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/repair_editor_asset_media_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/repair_editor_asset_media_and_return_procedure.rs index 63499dde6..1b035028b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/repair_editor_asset_media_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/repair_editor_asset_media_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait repair_editor_asset_media_and_return { input: EditorAssetMediaRepairInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl repair_editor_asset_media_and_return for super::RemoteProcedures { input: EditorAssetMediaRepairInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorAssetProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/repair_editor_project_resource_media_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/repair_editor_project_resource_media_and_return_procedure.rs index 47b5c9178..7e521cd37 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/repair_editor_project_resource_media_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/repair_editor_project_resource_media_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait repair_editor_project_resource_media_and_return { input: EditorProjectResourceMediaRepairInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl repair_editor_project_resource_media_and_return for super::RemoteProcedures input: EditorProjectResourceMediaRepairInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorProjectResourceProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_and_return_procedure.rs index 7d6fbfd19..ac8aa07d8 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait resolve_combat_action_and_return { input: ResolveCombatActionInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl resolve_combat_action_and_return for super::RemoteProcedures { input: ResolveCombatActionInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, ResolveCombatActionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_reducer.rs index 8398db8cd..41340e2f1 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_combat_action_reducer.rs @@ -47,11 +47,9 @@ pub trait resolve_combat_action { &self, input: ResolveCombatActionInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -60,11 +58,9 @@ impl resolve_combat_action for super::RemoteReducers { &self, input: ResolveCombatActionInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(ResolveCombatActionArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_battle_interaction_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_battle_interaction_and_return_procedure.rs index 268483bf8..ff4cca292 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_battle_interaction_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_battle_interaction_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait resolve_npc_battle_interaction_and_return { input: ResolveNpcBattleInteractionInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl resolve_npc_battle_interaction_and_return for super::RemoteProcedures { input: ResolveNpcBattleInteractionInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, NpcBattleInteractionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_interaction_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_interaction_and_return_procedure.rs index 4604fc309..aaba3d9cb 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_interaction_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_interaction_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait resolve_npc_interaction_and_return { input: ResolveNpcInteractionInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl resolve_npc_interaction_and_return for super::RemoteProcedures { input: ResolveNpcInteractionInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, NpcInteractionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_interaction_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_interaction_reducer.rs index 352f5a93b..52d213b54 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_interaction_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_interaction_reducer.rs @@ -47,11 +47,9 @@ pub trait resolve_npc_interaction { &self, input: ResolveNpcInteractionInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -60,11 +58,9 @@ impl resolve_npc_interaction for super::RemoteReducers { &self, input: ResolveNpcInteractionInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(ResolveNpcInteractionArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_social_action_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_social_action_and_return_procedure.rs index 65b1690ec..c14256495 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_social_action_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_social_action_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait resolve_npc_social_action_and_return { input: ResolveNpcSocialActionInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl resolve_npc_social_action_and_return for super::RemoteProcedures { input: ResolveNpcSocialActionInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, NpcStateProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_social_action_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_social_action_reducer.rs index 9b3269315..28e5ce36a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_social_action_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_npc_social_action_reducer.rs @@ -47,11 +47,9 @@ pub trait resolve_npc_social_action { &self, input: ResolveNpcSocialActionInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -60,11 +58,9 @@ impl resolve_npc_social_action for super::RemoteReducers { &self, input: ResolveNpcSocialActionInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(ResolveNpcSocialActionArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_treasure_interaction_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_treasure_interaction_and_return_procedure.rs index 0b8f6bad2..a224c122d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_treasure_interaction_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_treasure_interaction_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait resolve_treasure_interaction_and_return { input: TreasureResolveInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl resolve_treasure_interaction_and_return for super::RemoteProcedures { input: TreasureResolveInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, TreasureRecordProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resolve_treasure_interaction_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/resolve_treasure_interaction_reducer.rs index 221ac39d3..942b377a0 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resolve_treasure_interaction_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resolve_treasure_interaction_reducer.rs @@ -47,11 +47,9 @@ pub trait resolve_treasure_interaction { &self, input: TreasureResolveInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -60,11 +58,9 @@ impl resolve_treasure_interaction for super::RemoteReducers { &self, input: TreasureResolveInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(ResolveTreasureInteractionArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/restart_jump_hop_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/restart_jump_hop_run_procedure.rs index cde6dacab..f88b059cd 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/restart_jump_hop_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/restart_jump_hop_run_procedure.rs @@ -31,10 +31,10 @@ pub trait restart_jump_hop_run { input: JumpHopRunRestartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl restart_jump_hop_run for super::RemoteProcedures { input: JumpHopRunRestartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, JumpHopRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/restart_match_3_d_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/restart_match_3_d_run_procedure.rs index 76c74037b..add954d43 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/restart_match_3_d_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/restart_match_3_d_run_procedure.rs @@ -31,10 +31,10 @@ pub trait restart_match_3_d_run { input: Match3DRunRestartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl restart_match_3_d_run for super::RemoteProcedures { input: Match3DRunRestartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, Match3DRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/restart_square_hole_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/restart_square_hole_run_procedure.rs index df72d4d33..fe07f1477 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/restart_square_hole_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/restart_square_hole_run_procedure.rs @@ -31,10 +31,10 @@ pub trait restart_square_hole_run { input: SquareHoleRunRestartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl restart_square_hole_run for super::RemoteProcedures { input: SquareHoleRunRestartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, SquareHoleRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/resume_profile_save_archive_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/resume_profile_save_archive_and_return_procedure.rs index 73e6d668e..957c105fa 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/resume_profile_save_archive_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/resume_profile_save_archive_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait resume_profile_save_archive_and_return { input: RuntimeProfileSaveArchiveResumeInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl resume_profile_save_archive_and_return for super::RemoteProcedures { input: RuntimeProfileSaveArchiveResumeInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileSaveArchiveProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/retry_puzzle_clear_level_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/retry_puzzle_clear_level_run_procedure.rs index 4a7523c15..b2b816bb7 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/retry_puzzle_clear_level_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/retry_puzzle_clear_level_run_procedure.rs @@ -31,10 +31,10 @@ pub trait retry_puzzle_clear_level_run { input: PuzzleClearRunRetryLevelInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl retry_puzzle_clear_level_run for super::RemoteProcedures { input: PuzzleClearRunRetryLevelInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleClearRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/revoke_database_migration_operator_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/revoke_database_migration_operator_procedure.rs index fe0329266..feb5086ed 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/revoke_database_migration_operator_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/revoke_database_migration_operator_procedure.rs @@ -31,10 +31,10 @@ pub trait revoke_database_migration_operator { input: DatabaseMigrationRevokeOperatorInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl revoke_database_migration_operator for super::RemoteProcedures { input: DatabaseMigrationRevokeOperatorInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, DatabaseMigrationOperatorProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/revoke_external_api_key_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/revoke_external_api_key_and_return_procedure.rs index 9faef438c..d893b2180 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/revoke_external_api_key_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/revoke_external_api_key_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait revoke_external_api_key_and_return { input: ExternalApiKeyRevokeInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl revoke_external_api_key_and_return for super::RemoteProcedures { input: ExternalApiKeyRevokeInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, ExternalApiKeyProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_daily_free_points_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_daily_free_points_snapshot_type.rs new file mode 100644 index 000000000..7a4178733 --- /dev/null +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_daily_free_points_snapshot_type.rs @@ -0,0 +1,19 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct RuntimeProfileDailyFreePointsSnapshot { + pub day_key: i64, + pub granted_points: u64, + pub remaining_points: u64, + pub resets_at_micros: i64, + pub updated_at_micros: i64, +} + +impl __sdk::InModule for RuntimeProfileDailyFreePointsSnapshot { + type Module = super::RemoteModule; +} diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_dashboard_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_dashboard_snapshot_type.rs index a3e97b05f..07ffbe95e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_dashboard_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_dashboard_snapshot_type.rs @@ -4,6 +4,8 @@ #![allow(unused, clippy::all)] use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; +use super::runtime_profile_daily_free_points_snapshot_type::RuntimeProfileDailyFreePointsSnapshot; + #[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] #[sats(crate = __lib)] pub struct RuntimeProfileDashboardSnapshot { @@ -12,6 +14,7 @@ pub struct RuntimeProfileDashboardSnapshot { pub total_play_time_ms: u64, pub played_world_count: u32, pub updated_at_micros: Option, + pub daily_free_points: RuntimeProfileDailyFreePointsSnapshot, } impl __sdk::InModule for RuntimeProfileDashboardSnapshot { diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_center_snapshot_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_center_snapshot_type.rs index a9f7f4adb..747739cc1 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_center_snapshot_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_recharge_center_snapshot_type.rs @@ -4,6 +4,7 @@ #![allow(unused, clippy::all)] use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; +use super::runtime_profile_daily_free_points_snapshot_type::RuntimeProfileDailyFreePointsSnapshot; use super::runtime_profile_membership_benefit_snapshot_type::RuntimeProfileMembershipBenefitSnapshot; use super::runtime_profile_membership_snapshot_type::RuntimeProfileMembershipSnapshot; use super::runtime_profile_recharge_order_snapshot_type::RuntimeProfileRechargeOrderSnapshot; @@ -20,6 +21,7 @@ pub struct RuntimeProfileRechargeCenterSnapshot { pub benefits: Vec, pub latest_order: Option, pub has_points_recharged: bool, + pub daily_free_points: RuntimeProfileDailyFreePointsSnapshot, } impl __sdk::InModule for RuntimeProfileRechargeCenterSnapshot { diff --git a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_ledger_source_type_type.rs b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_ledger_source_type_type.rs index aef62c997..fa1040d4c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_ledger_source_type_type.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/runtime_profile_wallet_ledger_source_type_type.rs @@ -31,6 +31,10 @@ pub enum RuntimeProfileWalletLedgerSourceType { MembershipPeriodGrant, MembershipPeriodReset, + + DailyFreeGrant, + + DailyFreeReset, } impl __sdk::InModule for RuntimeProfileWalletLedgerSourceType { diff --git a/server-rs/crates/spacetime-client/src/module_bindings/save_editor_project_layout_ack_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/save_editor_project_layout_ack_procedure.rs index d2e864785..a564b72be 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/save_editor_project_layout_ack_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/save_editor_project_layout_ack_procedure.rs @@ -31,10 +31,10 @@ pub trait save_editor_project_layout_ack { input: EditorProjectLayoutSaveInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl save_editor_project_layout_ack for super::RemoteProcedures { input: EditorProjectLayoutSaveInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorProjectLayoutSaveProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/save_editor_project_layout_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/save_editor_project_layout_and_return_procedure.rs index e4eb529dc..17585bdf2 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/save_editor_project_layout_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/save_editor_project_layout_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait save_editor_project_layout_and_return { input: EditorProjectLayoutSaveInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl save_editor_project_layout_and_return for super::RemoteProcedures { input: EditorProjectLayoutSaveInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorProjectProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/save_puzzle_form_draft_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/save_puzzle_form_draft_procedure.rs index 13aff9a3a..bd13cb5f6 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/save_puzzle_form_draft_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/save_puzzle_form_draft_procedure.rs @@ -31,10 +31,10 @@ pub trait save_puzzle_form_draft { input: PuzzleFormDraftSaveInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl save_puzzle_form_draft for super::RemoteProcedures { input: PuzzleFormDraftSaveInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/save_puzzle_generated_images_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/save_puzzle_generated_images_procedure.rs index 870d6d512..85d174569 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/save_puzzle_generated_images_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/save_puzzle_generated_images_procedure.rs @@ -31,10 +31,10 @@ pub trait save_puzzle_generated_images { input: PuzzleGeneratedImagesSaveInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl save_puzzle_generated_images for super::RemoteProcedures { input: PuzzleGeneratedImagesSaveInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/save_puzzle_ui_background_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/save_puzzle_ui_background_procedure.rs index 49750c176..80fa6304b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/save_puzzle_ui_background_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/save_puzzle_ui_background_procedure.rs @@ -31,10 +31,10 @@ pub trait save_puzzle_ui_background { input: PuzzleUiBackgroundSaveInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl save_puzzle_ui_background for super::RemoteProcedures { input: PuzzleUiBackgroundSaveInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/seed_analytics_date_dimensions_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/seed_analytics_date_dimensions_reducer.rs index 29d3b91d9..6e2ac3ad9 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/seed_analytics_date_dimensions_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/seed_analytics_date_dimensions_reducer.rs @@ -50,11 +50,9 @@ pub trait seed_analytics_date_dimensions { &self, input: AnalyticsDateDimensionSeedInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -63,11 +61,9 @@ impl seed_analytics_date_dimensions for super::RemoteReducers { &self, input: AnalyticsDateDimensionSeedInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(SeedAnalyticsDateDimensionsArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/select_puzzle_cover_image_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/select_puzzle_cover_image_procedure.rs index fd4dd93ce..9dde8aaab 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/select_puzzle_cover_image_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/select_puzzle_cover_image_procedure.rs @@ -31,10 +31,10 @@ pub trait select_puzzle_cover_image { input: PuzzleSelectCoverImageInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl select_puzzle_cover_image for super::RemoteProcedures { input: PuzzleSelectCoverImageInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/square_hole_agent_message_table.rs b/server-rs/crates/spacetime-client/src/module_bindings/square_hole_agent_message_table.rs index 63d9fa6be..9507df730 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/square_hole_agent_message_table.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/square_hole_agent_message_table.rs @@ -153,7 +153,7 @@ pub trait square_hole_agent_messageQueryTableAccess { #[allow(non_snake_case)] /// Get a query builder for the table `SquareHoleAgentMessageRow`. fn square_hole_agent_message(&self) - -> __sdk::__query_builder::Table; + -> __sdk::__query_builder::Table; } impl square_hole_agent_messageQueryTableAccess for __sdk::QueryTableAccessor { diff --git a/server-rs/crates/spacetime-client/src/module_bindings/square_hole_agent_session_table.rs b/server-rs/crates/spacetime-client/src/module_bindings/square_hole_agent_session_table.rs index 057e44690..1f8098b1c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/square_hole_agent_session_table.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/square_hole_agent_session_table.rs @@ -153,7 +153,7 @@ pub trait square_hole_agent_sessionQueryTableAccess { #[allow(non_snake_case)] /// Get a query builder for the table `SquareHoleAgentSessionRow`. fn square_hole_agent_session(&self) - -> __sdk::__query_builder::Table; + -> __sdk::__query_builder::Table; } impl square_hole_agent_sessionQueryTableAccess for __sdk::QueryTableAccessor { diff --git a/server-rs/crates/spacetime-client/src/module_bindings/start_ai_task_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/start_ai_task_reducer.rs index c5cc52567..5809736bd 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/start_ai_task_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/start_ai_task_reducer.rs @@ -47,11 +47,9 @@ pub trait start_ai_task { &self, input: AiTaskStartInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -60,11 +58,9 @@ impl start_ai_task for super::RemoteReducers { &self, input: AiTaskStartInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(StartAiTaskArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/start_ai_task_stage_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/start_ai_task_stage_reducer.rs index 24ed5b3fa..1d7b7582a 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/start_ai_task_stage_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/start_ai_task_stage_reducer.rs @@ -47,11 +47,9 @@ pub trait start_ai_task_stage { &self, input: AiTaskStageStartInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -60,11 +58,9 @@ impl start_ai_task_stage for super::RemoteReducers { &self, input: AiTaskStageStartInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(StartAiTaskStageArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/start_bark_battle_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/start_bark_battle_run_procedure.rs index 1fdc1a093..a29ff5e9e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/start_bark_battle_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/start_bark_battle_run_procedure.rs @@ -31,10 +31,10 @@ pub trait start_bark_battle_run { input: BarkBattleRunStartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl start_bark_battle_run for super::RemoteProcedures { input: BarkBattleRunStartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BarkBattleProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/start_big_fish_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/start_big_fish_run_procedure.rs index 6a149f03f..7f3713ab3 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/start_big_fish_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/start_big_fish_run_procedure.rs @@ -31,10 +31,10 @@ pub trait start_big_fish_run { input: BigFishRunStartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl start_big_fish_run for super::RemoteProcedures { input: BigFishRunStartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BigFishRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/start_jump_hop_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/start_jump_hop_run_procedure.rs index 7a52fc9fe..c5da32982 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/start_jump_hop_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/start_jump_hop_run_procedure.rs @@ -31,10 +31,10 @@ pub trait start_jump_hop_run { input: JumpHopRunStartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl start_jump_hop_run for super::RemoteProcedures { input: JumpHopRunStartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, JumpHopRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/start_match_3_d_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/start_match_3_d_run_procedure.rs index c0a8c8a93..d4126af9b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/start_match_3_d_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/start_match_3_d_run_procedure.rs @@ -31,10 +31,10 @@ pub trait start_match_3_d_run { input: Match3DRunStartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl start_match_3_d_run for super::RemoteProcedures { input: Match3DRunStartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, Match3DRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/start_puzzle_clear_runtime_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/start_puzzle_clear_runtime_run_procedure.rs index c9f6660c6..069b523c6 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/start_puzzle_clear_runtime_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/start_puzzle_clear_runtime_run_procedure.rs @@ -31,10 +31,10 @@ pub trait start_puzzle_clear_runtime_run { input: PuzzleClearRunStartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl start_puzzle_clear_runtime_run for super::RemoteProcedures { input: PuzzleClearRunStartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleClearRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/start_puzzle_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/start_puzzle_run_procedure.rs index c3d6d4576..b6baeb838 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/start_puzzle_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/start_puzzle_run_procedure.rs @@ -31,10 +31,10 @@ pub trait start_puzzle_run { input: PuzzleRunStartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl start_puzzle_run for super::RemoteProcedures { input: PuzzleRunStartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/start_square_hole_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/start_square_hole_run_procedure.rs index bda9eddd7..d85e730b0 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/start_square_hole_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/start_square_hole_run_procedure.rs @@ -31,10 +31,10 @@ pub trait start_square_hole_run { input: SquareHoleRunStartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl start_square_hole_run for super::RemoteProcedures { input: SquareHoleRunStartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, SquareHoleRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/start_visual_novel_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/start_visual_novel_run_procedure.rs index fabc598a1..98f9cfa03 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/start_visual_novel_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/start_visual_novel_run_procedure.rs @@ -31,10 +31,10 @@ pub trait start_visual_novel_run { input: VisualNovelRunStartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl start_visual_novel_run for super::RemoteProcedures { input: VisualNovelRunStartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, VisualNovelRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/start_wooden_fish_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/start_wooden_fish_run_procedure.rs index daf6358ed..65fa87c2e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/start_wooden_fish_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/start_wooden_fish_run_procedure.rs @@ -31,10 +31,10 @@ pub trait start_wooden_fish_run { input: WoodenFishRunStartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl start_wooden_fish_run for super::RemoteProcedures { input: WoodenFishRunStartInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, WoodenFishRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/stop_match_3_d_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/stop_match_3_d_run_procedure.rs index 630e7a981..f87a63d50 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/stop_match_3_d_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/stop_match_3_d_run_procedure.rs @@ -31,10 +31,10 @@ pub trait stop_match_3_d_run { input: Match3DRunStopInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl stop_match_3_d_run for super::RemoteProcedures { input: Match3DRunStopInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, Match3DRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/stop_square_hole_run_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/stop_square_hole_run_procedure.rs index 90702e09d..076a3af89 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/stop_square_hole_run_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/stop_square_hole_run_procedure.rs @@ -31,10 +31,10 @@ pub trait stop_square_hole_run { input: SquareHoleRunStopInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl stop_square_hole_run for super::RemoteProcedures { input: SquareHoleRunStopInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, SquareHoleRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/submit_big_fish_input_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/submit_big_fish_input_procedure.rs index e5bbe6b02..8ef444c53 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/submit_big_fish_input_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/submit_big_fish_input_procedure.rs @@ -31,10 +31,10 @@ pub trait submit_big_fish_input { input: BigFishInputSubmitInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl submit_big_fish_input for super::RemoteProcedures { input: BigFishInputSubmitInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BigFishRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/submit_big_fish_message_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/submit_big_fish_message_procedure.rs index 307d77554..0dbc01181 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/submit_big_fish_message_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/submit_big_fish_message_procedure.rs @@ -31,10 +31,10 @@ pub trait submit_big_fish_message { input: BigFishMessageSubmitInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl submit_big_fish_message for super::RemoteProcedures { input: BigFishMessageSubmitInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BigFishSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/submit_custom_world_agent_message_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/submit_custom_world_agent_message_procedure.rs index 5debac19f..c5e8def19 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/submit_custom_world_agent_message_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/submit_custom_world_agent_message_procedure.rs @@ -31,10 +31,10 @@ pub trait submit_custom_world_agent_message { input: CustomWorldAgentMessageSubmitInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl submit_custom_world_agent_message for super::RemoteProcedures { input: CustomWorldAgentMessageSubmitInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldAgentOperationProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/submit_editor_showcase_asset_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/submit_editor_showcase_asset_and_return_procedure.rs index 367fd0fdb..7002e581b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/submit_editor_showcase_asset_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/submit_editor_showcase_asset_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait submit_editor_showcase_asset_and_return { input: EditorShowcaseAssetSubmitInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl submit_editor_showcase_asset_and_return for super::RemoteProcedures { input: EditorShowcaseAssetSubmitInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorShowcaseAssetProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/submit_match_3_d_agent_message_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/submit_match_3_d_agent_message_procedure.rs index c323ecda4..1ceceaf5d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/submit_match_3_d_agent_message_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/submit_match_3_d_agent_message_procedure.rs @@ -31,10 +31,10 @@ pub trait submit_match_3_d_agent_message { input: Match3DAgentMessageSubmitInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl submit_match_3_d_agent_message for super::RemoteProcedures { input: Match3DAgentMessageSubmitInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, Match3DAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/submit_profile_feedback_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/submit_profile_feedback_and_return_procedure.rs index 208a35a95..3534df7d3 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/submit_profile_feedback_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/submit_profile_feedback_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait submit_profile_feedback_and_return { input: RuntimeProfileFeedbackSubmissionInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl submit_profile_feedback_and_return for super::RemoteProcedures { input: RuntimeProfileFeedbackSubmissionInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeProfileFeedbackSubmissionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/submit_puzzle_agent_message_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/submit_puzzle_agent_message_procedure.rs index 0d9e94e58..b5b2b0903 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/submit_puzzle_agent_message_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/submit_puzzle_agent_message_procedure.rs @@ -31,10 +31,10 @@ pub trait submit_puzzle_agent_message { input: PuzzleAgentMessageSubmitInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl submit_puzzle_agent_message for super::RemoteProcedures { input: PuzzleAgentMessageSubmitInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/submit_puzzle_leaderboard_entry_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/submit_puzzle_leaderboard_entry_procedure.rs index 9f72a916c..7df245643 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/submit_puzzle_leaderboard_entry_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/submit_puzzle_leaderboard_entry_procedure.rs @@ -31,10 +31,10 @@ pub trait submit_puzzle_leaderboard_entry { input: PuzzleLeaderboardSubmitInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl submit_puzzle_leaderboard_entry for super::RemoteProcedures { input: PuzzleLeaderboardSubmitInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/submit_square_hole_agent_message_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/submit_square_hole_agent_message_procedure.rs index bbdbe51da..e740fe61b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/submit_square_hole_agent_message_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/submit_square_hole_agent_message_procedure.rs @@ -31,10 +31,10 @@ pub trait submit_square_hole_agent_message { input: SquareHoleAgentMessageSubmitInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl submit_square_hole_agent_message for super::RemoteProcedures { input: SquareHoleAgentMessageSubmitInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, SquareHoleAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/submit_visual_novel_agent_message_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/submit_visual_novel_agent_message_procedure.rs index 5e9c93bb3..0df949bd7 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/submit_visual_novel_agent_message_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/submit_visual_novel_agent_message_procedure.rs @@ -31,10 +31,10 @@ pub trait submit_visual_novel_agent_message { input: VisualNovelAgentMessageSubmitInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl submit_visual_novel_agent_message for super::RemoteProcedures { input: VisualNovelAgentMessageSubmitInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, VisualNovelAgentSessionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/swap_puzzle_clear_cards_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/swap_puzzle_clear_cards_procedure.rs index d527f9ed2..fef0c0379 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/swap_puzzle_clear_cards_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/swap_puzzle_clear_cards_procedure.rs @@ -31,10 +31,10 @@ pub trait swap_puzzle_clear_cards { input: PuzzleClearRunSwapInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl swap_puzzle_clear_cards for super::RemoteProcedures { input: PuzzleClearRunSwapInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleClearRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/swap_puzzle_pieces_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/swap_puzzle_pieces_procedure.rs index f5835607f..9c6a13378 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/swap_puzzle_pieces_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/swap_puzzle_pieces_procedure.rs @@ -31,10 +31,10 @@ pub trait swap_puzzle_pieces { input: PuzzleRunSwapInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl swap_puzzle_pieces for super::RemoteProcedures { input: PuzzleRunSwapInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/sync_auth_store_projection_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/sync_auth_store_projection_procedure.rs index d9f1ca3e3..7d4803e27 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/sync_auth_store_projection_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/sync_auth_store_projection_procedure.rs @@ -31,10 +31,10 @@ pub trait sync_auth_store_projection { input: AuthStoreProjectionView, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl sync_auth_store_projection for super::RemoteProcedures { input: AuthStoreProjectionView, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, AuthStoreProjectionSyncProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/toggle_editor_showcase_asset_like_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/toggle_editor_showcase_asset_like_and_return_procedure.rs index 8fc194ca2..a014ceba1 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/toggle_editor_showcase_asset_like_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/toggle_editor_showcase_asset_like_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait toggle_editor_showcase_asset_like_and_return { input: EditorShowcaseAssetLikeToggleInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl toggle_editor_showcase_asset_like_and_return for super::RemoteProcedures { input: EditorShowcaseAssetLikeToggleInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorShowcaseAssetProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/touch_editor_agent_conversation_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/touch_editor_agent_conversation_and_return_procedure.rs index e331ab348..89741e92c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/touch_editor_agent_conversation_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/touch_editor_agent_conversation_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait touch_editor_agent_conversation_and_return { input: EditorAgentConversationTouchInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl touch_editor_agent_conversation_and_return for super::RemoteProcedures { input: EditorAgentConversationTouchInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorAgentConversationProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/turn_in_quest_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/turn_in_quest_reducer.rs index 08727f040..4306a47f1 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/turn_in_quest_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/turn_in_quest_reducer.rs @@ -47,11 +47,9 @@ pub trait turn_in_quest { &self, input: QuestTurnInInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -60,11 +58,9 @@ impl turn_in_quest for super::RemoteReducers { &self, input: QuestTurnInInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(TurnInQuestArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/unpublish_custom_world_profile_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/unpublish_custom_world_profile_and_return_procedure.rs index 31acccce2..b87880a7e 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/unpublish_custom_world_profile_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/unpublish_custom_world_profile_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait unpublish_custom_world_profile_and_return { input: CustomWorldProfileUnpublishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl unpublish_custom_world_profile_and_return for super::RemoteProcedures { input: CustomWorldProfileUnpublishInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldLibraryMutationResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/unpublish_custom_world_profile_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/unpublish_custom_world_profile_reducer.rs index 51af927fa..05274f62d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/unpublish_custom_world_profile_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/unpublish_custom_world_profile_reducer.rs @@ -50,11 +50,9 @@ pub trait unpublish_custom_world_profile { &self, input: CustomWorldProfileUnpublishInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -63,11 +61,9 @@ impl unpublish_custom_world_profile for super::RemoteReducers { &self, input: CustomWorldProfileUnpublishInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(UnpublishCustomWorldProfileArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/update_bark_battle_draft_config_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/update_bark_battle_draft_config_procedure.rs index bd9fc4212..545ca3f8c 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/update_bark_battle_draft_config_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/update_bark_battle_draft_config_procedure.rs @@ -31,10 +31,10 @@ pub trait update_bark_battle_draft_config { input: BarkBattleDraftConfigUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl update_bark_battle_draft_config for super::RemoteProcedures { input: BarkBattleDraftConfigUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, BarkBattleProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/update_editor_asset_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/update_editor_asset_and_return_procedure.rs index 4e0cfd34e..1ec662919 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/update_editor_asset_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/update_editor_asset_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait update_editor_asset_and_return { input: EditorAssetUpdateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl update_editor_asset_and_return for super::RemoteProcedures { input: EditorAssetUpdateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorAssetProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/update_editor_asset_folder_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/update_editor_asset_folder_and_return_procedure.rs index 91ca31fc5..a5ec71437 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/update_editor_asset_folder_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/update_editor_asset_folder_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait update_editor_asset_folder_and_return { input: EditorAssetFolderUpdateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl update_editor_asset_folder_and_return for super::RemoteProcedures { input: EditorAssetFolderUpdateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorAssetFolderProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/update_editor_project_resource_showcase_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/update_editor_project_resource_showcase_and_return_procedure.rs index c49ab8d9d..200432a78 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/update_editor_project_resource_showcase_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/update_editor_project_resource_showcase_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait update_editor_project_resource_showcase_and_return { input: EditorProjectResourceShowcaseUpdateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl update_editor_project_resource_showcase_and_return for super::RemoteProcedu input: EditorProjectResourceShowcaseUpdateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorProjectResourceProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/update_editor_showcase_asset_display_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/update_editor_showcase_asset_display_and_return_procedure.rs index 85db684da..40b912558 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/update_editor_showcase_asset_display_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/update_editor_showcase_asset_display_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait update_editor_showcase_asset_display_and_return { input: EditorShowcaseAssetDisplayUpdateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl update_editor_showcase_asset_display_and_return for super::RemoteProcedures input: EditorShowcaseAssetDisplayUpdateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorShowcaseAssetProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/update_jump_hop_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/update_jump_hop_work_procedure.rs index 9186048bb..c5f246bf4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/update_jump_hop_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/update_jump_hop_work_procedure.rs @@ -31,10 +31,10 @@ pub trait update_jump_hop_work { input: JumpHopWorkUpdateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl update_jump_hop_work for super::RemoteProcedures { input: JumpHopWorkUpdateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, JumpHopWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/update_match_3_d_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/update_match_3_d_work_procedure.rs index cdc427828..ea2ee9b43 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/update_match_3_d_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/update_match_3_d_work_procedure.rs @@ -31,10 +31,10 @@ pub trait update_match_3_d_work { input: Match3DWorkUpdateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl update_match_3_d_work for super::RemoteProcedures { input: Match3DWorkUpdateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, Match3DWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/update_puzzle_clear_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/update_puzzle_clear_work_procedure.rs index 5f4b592aa..2bc286f57 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/update_puzzle_clear_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/update_puzzle_clear_work_procedure.rs @@ -31,10 +31,10 @@ pub trait update_puzzle_clear_work { input: PuzzleClearWorkUpdateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl update_puzzle_clear_work for super::RemoteProcedures { input: PuzzleClearWorkUpdateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleClearWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/update_puzzle_run_pause_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/update_puzzle_run_pause_procedure.rs index 3388380c7..1679b5eca 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/update_puzzle_run_pause_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/update_puzzle_run_pause_procedure.rs @@ -31,10 +31,10 @@ pub trait update_puzzle_run_pause { input: PuzzleRunPauseInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl update_puzzle_run_pause for super::RemoteProcedures { input: PuzzleRunPauseInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/update_puzzle_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/update_puzzle_work_procedure.rs index 805712418..6710b4da4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/update_puzzle_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/update_puzzle_work_procedure.rs @@ -31,10 +31,10 @@ pub trait update_puzzle_work { input: PuzzleWorkUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl update_puzzle_work for super::RemoteProcedures { input: PuzzleWorkUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/update_square_hole_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/update_square_hole_work_procedure.rs index 914b3f05f..167865275 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/update_square_hole_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/update_square_hole_work_procedure.rs @@ -31,10 +31,10 @@ pub trait update_square_hole_work { input: SquareHoleWorkUpdateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl update_square_hole_work for super::RemoteProcedures { input: SquareHoleWorkUpdateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, SquareHoleWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/update_visual_novel_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/update_visual_novel_work_procedure.rs index bb0ccc4e4..b6e61e332 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/update_visual_novel_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/update_visual_novel_work_procedure.rs @@ -31,10 +31,10 @@ pub trait update_visual_novel_work { input: VisualNovelWorkUpdateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl update_visual_novel_work for super::RemoteProcedures { input: VisualNovelWorkUpdateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, VisualNovelWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/update_wooden_fish_work_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/update_wooden_fish_work_procedure.rs index 317852756..6b7c03d83 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/update_wooden_fish_work_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/update_wooden_fish_work_procedure.rs @@ -31,10 +31,10 @@ pub trait update_wooden_fish_work { input: WoodenFishWorkUpdateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl update_wooden_fish_work for super::RemoteProcedures { input: WoodenFishWorkUpdateInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, WoodenFishWorkProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_chapter_progression_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_chapter_progression_and_return_procedure.rs index be3ff4736..abc39bfe3 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_chapter_progression_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_chapter_progression_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait upsert_chapter_progression_and_return { input: ChapterProgressionInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl upsert_chapter_progression_and_return for super::RemoteProcedures { input: ChapterProgressionInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, ChapterProgressionProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_chapter_progression_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_chapter_progression_reducer.rs index de37e9944..0cb4bb7ac 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_chapter_progression_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_chapter_progression_reducer.rs @@ -47,11 +47,9 @@ pub trait upsert_chapter_progression { &self, input: ChapterProgressionInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -60,11 +58,9 @@ impl upsert_chapter_progression for super::RemoteReducers { &self, input: ChapterProgressionInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(UpsertChapterProgressionArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_creation_entry_event_banners_config_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_creation_entry_event_banners_config_procedure.rs index 58e38d907..aea3d28e5 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_creation_entry_event_banners_config_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_creation_entry_event_banners_config_procedure.rs @@ -34,10 +34,10 @@ pub trait upsert_creation_entry_event_banners_config { input: CreationEntryEventBannersAdminUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl upsert_creation_entry_event_banners_config for super::RemoteProcedures { input: CreationEntryEventBannersAdminUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CreationEntryConfigProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_creation_entry_type_config_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_creation_entry_type_config_procedure.rs index 98f1f7fba..32df13cb2 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_creation_entry_type_config_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_creation_entry_type_config_procedure.rs @@ -31,10 +31,10 @@ pub trait upsert_creation_entry_type_config { input: CreationEntryTypeAdminUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl upsert_creation_entry_type_config for super::RemoteProcedures { input: CreationEntryTypeAdminUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CreationEntryConfigProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_agent_operation_progress_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_agent_operation_progress_procedure.rs index 6d8e39a21..554b95b20 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_agent_operation_progress_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_agent_operation_progress_procedure.rs @@ -34,10 +34,10 @@ pub trait upsert_custom_world_agent_operation_progress { input: CustomWorldAgentOperationProgressInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl upsert_custom_world_agent_operation_progress for super::RemoteProcedures { input: CustomWorldAgentOperationProgressInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldAgentOperationProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_profile_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_profile_and_return_procedure.rs index a761ab958..343e1807f 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_profile_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_profile_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait upsert_custom_world_profile_and_return { input: CustomWorldProfileUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl upsert_custom_world_profile_and_return for super::RemoteProcedures { input: CustomWorldProfileUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CustomWorldLibraryMutationResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_profile_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_profile_reducer.rs index 91ca26527..e343b491d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_profile_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_custom_world_profile_reducer.rs @@ -50,11 +50,9 @@ pub trait upsert_custom_world_profile { &self, input: CustomWorldProfileUpsertInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -63,11 +61,9 @@ impl upsert_custom_world_profile for super::RemoteReducers { &self, input: CustomWorldProfileUpsertInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(UpsertCustomWorldProfileArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_editor_showcase_campaign_config_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_editor_showcase_campaign_config_and_return_procedure.rs index ae0bb7a6c..490d65d5d 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_editor_showcase_campaign_config_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_editor_showcase_campaign_config_and_return_procedure.rs @@ -34,10 +34,10 @@ pub trait upsert_editor_showcase_campaign_config_and_return { input: EditorShowcaseCampaignConfigUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl upsert_editor_showcase_campaign_config_and_return for super::RemoteProcedur input: EditorShowcaseCampaignConfigUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, EditorShowcaseCampaignConfigProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_feature_gate_config_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_feature_gate_config_procedure.rs index ece1afe07..3e7c3cfcb 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_feature_gate_config_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_feature_gate_config_procedure.rs @@ -31,10 +31,10 @@ pub trait upsert_feature_gate_config { input: FeatureGateConfigAdminUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl upsert_feature_gate_config for super::RemoteProcedures { input: FeatureGateConfigAdminUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, FeatureGateConfigProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_npc_state_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_npc_state_and_return_procedure.rs index 91ba0d0e5..b7f3a28d4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_npc_state_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_npc_state_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait upsert_npc_state_and_return { input: NpcStateUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl upsert_npc_state_and_return for super::RemoteProcedures { input: NpcStateUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, NpcStateProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_npc_state_reducer.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_npc_state_reducer.rs index afbe61f81..f363770d4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_npc_state_reducer.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_npc_state_reducer.rs @@ -47,11 +47,9 @@ pub trait upsert_npc_state { &self, input: NpcStateUpsertInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()>; } @@ -60,11 +58,9 @@ impl upsert_npc_state for super::RemoteReducers { &self, input: NpcStateUpsertInput, - callback: impl FnOnce( - &super::ReducerEventContext, - Result, __sdk::InternalError>, - ) + Send - + 'static, + callback: impl FnOnce(&super::ReducerEventContext, Result, __sdk::InternalError>) + + Send + + 'static, ) -> __sdk::Result<()> { self.imp .invoke_reducer_with_callback(UpsertNpcStateArgs { input }, callback) diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_platform_browse_history_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_platform_browse_history_and_return_procedure.rs index 36e5f4646..614a6d055 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_platform_browse_history_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_platform_browse_history_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait upsert_platform_browse_history_and_return { input: RuntimeBrowseHistorySyncInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl upsert_platform_browse_history_and_return for super::RemoteProcedures { input: RuntimeBrowseHistorySyncInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeBrowseHistoryProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_public_work_interaction_config_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_public_work_interaction_config_procedure.rs index 1a9a8857f..88e88d690 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_public_work_interaction_config_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_public_work_interaction_config_procedure.rs @@ -34,10 +34,10 @@ pub trait upsert_public_work_interaction_config { input: PublicWorkInteractionConfigAdminUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -47,10 +47,10 @@ impl upsert_public_work_interaction_config for super::RemoteProcedures { input: PublicWorkInteractionConfigAdminUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, CreationEntryConfigProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_runtime_setting_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_runtime_setting_and_return_procedure.rs index f8fa0351a..119eab703 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_runtime_setting_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_runtime_setting_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait upsert_runtime_setting_and_return { input: RuntimeSettingUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl upsert_runtime_setting_and_return for super::RemoteProcedures { input: RuntimeSettingUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeSettingProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_runtime_snapshot_and_return_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_runtime_snapshot_and_return_procedure.rs index fe0746baf..eceae785b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_runtime_snapshot_and_return_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_runtime_snapshot_and_return_procedure.rs @@ -31,10 +31,10 @@ pub trait upsert_runtime_snapshot_and_return { input: RuntimeSnapshotUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl upsert_runtime_snapshot_and_return for super::RemoteProcedures { input: RuntimeSnapshotUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, RuntimeSnapshotProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/upsert_visual_novel_run_snapshot_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/upsert_visual_novel_run_snapshot_procedure.rs index 8cc89e856..35722b1e4 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/upsert_visual_novel_run_snapshot_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/upsert_visual_novel_run_snapshot_procedure.rs @@ -31,10 +31,10 @@ pub trait upsert_visual_novel_run_snapshot { input: VisualNovelRunSnapshotUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl upsert_visual_novel_run_snapshot for super::RemoteProcedures { input: VisualNovelRunSnapshotUpsertInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, VisualNovelRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/use_puzzle_runtime_prop_procedure.rs b/server-rs/crates/spacetime-client/src/module_bindings/use_puzzle_runtime_prop_procedure.rs index 1e2dbe136..45bd3a730 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/use_puzzle_runtime_prop_procedure.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/use_puzzle_runtime_prop_procedure.rs @@ -31,10 +31,10 @@ pub trait use_puzzle_runtime_prop { input: PuzzleRunPropInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ); } @@ -44,10 +44,10 @@ impl use_puzzle_runtime_prop for super::RemoteProcedures { input: PuzzleRunPropInput, __callback: impl FnOnce( - &super::ProcedureEventContext, - Result, - ) + Send - + 'static, + &super::ProcedureEventContext, + Result, + ) + Send + + 'static, ) { self.imp .invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>( diff --git a/server-rs/crates/spacetime-client/src/module_bindings/visual_novel_gallery_view_table.rs b/server-rs/crates/spacetime-client/src/module_bindings/visual_novel_gallery_view_table.rs index a1f70563d..1b3931275 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/visual_novel_gallery_view_table.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/visual_novel_gallery_view_table.rs @@ -105,7 +105,7 @@ pub trait visual_novel_gallery_viewQueryTableAccess { #[allow(non_snake_case)] /// Get a query builder for the table `VisualNovelGalleryViewRow`. fn visual_novel_gallery_view(&self) - -> __sdk::__query_builder::Table; + -> __sdk::__query_builder::Table; } impl visual_novel_gallery_viewQueryTableAccess for __sdk::QueryTableAccessor { diff --git a/server-rs/crates/spacetime-client/src/module_bindings/visual_novel_work_profile_table.rs b/server-rs/crates/spacetime-client/src/module_bindings/visual_novel_work_profile_table.rs index 24682e11d..55371a46b 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/visual_novel_work_profile_table.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/visual_novel_work_profile_table.rs @@ -153,7 +153,7 @@ pub trait visual_novel_work_profileQueryTableAccess { #[allow(non_snake_case)] /// Get a query builder for the table `VisualNovelWorkProfileRow`. fn visual_novel_work_profile(&self) - -> __sdk::__query_builder::Table; + -> __sdk::__query_builder::Table; } impl visual_novel_work_profileQueryTableAccess for __sdk::QueryTableAccessor { diff --git a/server-rs/crates/spacetime-client/src/module_bindings/wooden_fish_agent_session_table.rs b/server-rs/crates/spacetime-client/src/module_bindings/wooden_fish_agent_session_table.rs index 68f2e4845..2b9e7d583 100644 --- a/server-rs/crates/spacetime-client/src/module_bindings/wooden_fish_agent_session_table.rs +++ b/server-rs/crates/spacetime-client/src/module_bindings/wooden_fish_agent_session_table.rs @@ -153,7 +153,7 @@ pub trait wooden_fish_agent_sessionQueryTableAccess { #[allow(non_snake_case)] /// Get a query builder for the table `WoodenFishAgentSessionRow`. fn wooden_fish_agent_session(&self) - -> __sdk::__query_builder::Table; + -> __sdk::__query_builder::Table; } impl wooden_fish_agent_sessionQueryTableAccess for __sdk::QueryTableAccessor { diff --git a/server-rs/crates/spacetime-client/src/profile_recharge_expiration.rs b/server-rs/crates/spacetime-client/src/profile_recharge_expiration.rs index 6ff4b41f6..7bab87ddc 100644 --- a/server-rs/crates/spacetime-client/src/profile_recharge_expiration.rs +++ b/server-rs/crates/spacetime-client/src/profile_recharge_expiration.rs @@ -1,24 +1,34 @@ use super::*; -use spacetimedb_sdk::TableWithPrimaryKey; +use spacetimedb_sdk::Table; use tokio::sync::mpsc; -const PROFILE_RECHARGE_EXPIRATION_EVENT_SUBSCRIPTION_QUERIES: [&str; 2] = [ - "SELECT * FROM profile_recharge_order WHERE status = 'pending'", - "SELECT * FROM profile_recharge_order WHERE status = 'expired'", -]; +// The timer table contains only active expiration timers, so this avoids retaining recharge history. +const PROFILE_RECHARGE_EXPIRATION_EVENT_SUBSCRIPTION_QUERIES: [&str; 1] = + ["SELECT * FROM profile_recharge_order_expiration_timer"]; + +#[cfg(test)] +mod tests { + use super::PROFILE_RECHARGE_EXPIRATION_EVENT_SUBSCRIPTION_QUERIES; + + #[test] + fn expiration_listener_subscribes_only_to_active_timer_rows() { + assert_eq!( + PROFILE_RECHARGE_EXPIRATION_EVENT_SUBSCRIPTION_QUERIES, + ["SELECT * FROM profile_recharge_order_expiration_timer"] + ); + } +} pub struct ProfileRechargeExpirationSubscription { connection: DbConnection, _subscriptions: Vec, - _update_callback: ProfileRechargeOrderUpdateCallbackId, + _delete_callback: ProfileRechargeOrderExpirationTimerDeleteCallbackId, runner: Option>, - receiver: mpsc::UnboundedReceiver, + receiver: mpsc::UnboundedReceiver, } impl ProfileRechargeExpirationSubscription { - pub async fn recv( - &mut self, - ) -> Result { + pub async fn recv(&mut self) -> Result { self.receiver .recv() .await @@ -83,19 +93,13 @@ impl SpacetimeClient { .map_err(|_| SpacetimeClientError::ConnectDropped)??; let (event_sender, event_receiver) = mpsc::unbounded_channel(); - let update_sender = event_sender.clone(); - let update_callback = - connection - .db() - .profile_recharge_order() - .on_update(move |_, old, new| { - if old.status == RuntimeProfileRechargeOrderStatus::Pending - && new.status == RuntimeProfileRechargeOrderStatus::Expired - { - let _ = update_sender - .send(map_runtime_profile_recharge_order_table_row(new.clone())); - } - }); + let delete_sender = event_sender.clone(); + let delete_callback = connection + .db() + .profile_recharge_order_expiration_timer() + .on_delete(move |_, timer| { + let _ = delete_sender.send(timer.order_id.clone()); + }); let mut subscriptions = Vec::new(); for query in PROFILE_RECHARGE_EXPIRATION_EVENT_SUBSCRIPTION_QUERIES { @@ -129,7 +133,7 @@ impl SpacetimeClient { Ok(ProfileRechargeExpirationSubscription { connection, _subscriptions: subscriptions, - _update_callback: update_callback, + _delete_callback: delete_callback, runner: Some(runner), receiver: event_receiver, }) diff --git a/server-rs/crates/spacetime-module/src/external_generation.rs b/server-rs/crates/spacetime-module/src/external_generation.rs index e66f5ee62..c4bf6c665 100644 --- a/server-rs/crates/spacetime-module/src/external_generation.rs +++ b/server-rs/crates/spacetime-module/src/external_generation.rs @@ -1,4 +1,6 @@ use crate::*; +use std::cmp::Ordering; +use std::ops::RangeFrom; const EXTERNAL_GENERATION_STATUS_PENDING: &str = "pending"; const EXTERNAL_GENERATION_STATUS_RUNNING: &str = "running"; @@ -11,8 +13,15 @@ const EXTERNAL_GENERATION_EVENT_LEASE_RENEWED: &str = "lease_renewed"; const EXTERNAL_GENERATION_EVENT_COMPLETED: &str = "completed"; const EXTERNAL_GENERATION_EVENT_FAILED: &str = "failed"; const EXTERNAL_GENERATION_EVENT_ACKNOWLEDGED: &str = "acknowledged"; +const EXTERNAL_GENERATION_EDITOR_SOURCE_MODULE: &str = "editor-canvas"; const EXTERNAL_GENERATION_FINAL_ATTEMPT_LEASE_EXPIRED_MESSAGE: &str = "worker 最终执行次数的 lease 已过期,任务已终止"; +const MAX_EXTERNAL_GENERATION_PAYLOAD_BYTES: usize = 512 * 1024; +const MAX_EXTERNAL_GENERATION_REQUEST_PROMPT_CHARS: usize = 2_048; +const MAX_EXTERNAL_GENERATION_ERROR_MESSAGE_CHARS: usize = 2_048; +const MAX_EXTERNAL_GENERATION_MAINTENANCE_BATCH_SIZE: u32 = 25; +const INLINE_MEDIA_REMOVED_PLACEHOLDER: &str = "[inline-media-removed]"; +const INLINE_MEDIA_ERROR_REDACTED_MESSAGE: &str = "外部生成失败(错误详情含内联媒体引用,已省略)"; #[spacetimedb::table( accessor = external_generation_job, @@ -31,6 +40,14 @@ const EXTERNAL_GENERATION_FINAL_ATTEMPT_LEASE_EXPIRED_MESSAGE: &str = index( accessor = by_external_generation_job_owner_user_id, btree(columns = [owner_user_id]) + ), + index( + accessor = by_external_generation_job_cursor, + btree(columns = [job_id, source_module]) + ), + index( + accessor = by_external_generation_job_source_cursor, + btree(columns = [source_module, job_id]) ) )] #[derive(Clone)] @@ -91,6 +108,34 @@ pub struct ExternalGenerationJobEvent { pub(crate) created_at: Timestamp, } +#[spacetimedb::table( + accessor = external_generation_job_summary, + index( + accessor = by_external_generation_job_summary_owner_user_id, + btree(columns = [owner_user_id]) + ) +)] +#[derive(Clone)] +pub struct ExternalGenerationJobSummary { + #[primary_key] + pub(crate) job_id: String, + pub(crate) job_kind: String, + pub(crate) owner_user_id: String, + pub(crate) source_module: String, + pub(crate) source_entity_id: String, + pub(crate) request_label: String, + pub(crate) request_prompt: Option, + pub(crate) status: String, + pub(crate) last_error_message: Option, + pub(crate) created_at: Timestamp, + pub(crate) started_at: Option, + pub(crate) completed_at: Option, + pub(crate) updated_at: Timestamp, + pub(crate) price_mud_points: u64, + pub(crate) refund_ledger_id: Option, + pub(crate) notification_acknowledged_at: Option, +} + #[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)] pub struct ExternalGenerationJobEnqueueInput { pub job_id: String, @@ -165,6 +210,22 @@ pub struct ExternalGenerationJobAcknowledgeInput { pub acknowledged_at_micros: i64, } +#[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)] +pub struct ExternalGenerationJobSummaryBackfillInput { + pub owner_user_id: Option, + pub limit: u32, + pub cursor_job_id: Option, + pub dry_run: bool, +} + +#[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)] +pub struct ExternalGenerationJobPayloadCompactionInput { + pub dry_run: bool, + pub limit: u32, + pub cursor_job_id: Option, + pub completed_before_micros: Option, +} + #[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)] pub struct ExternalGenerationJobSnapshot { pub job_id: String, @@ -205,6 +266,66 @@ pub struct ExternalGenerationJobProcedureResult { pub error_message: Option, } +#[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)] +pub struct ExternalGenerationJobSummarySnapshot { + pub job_id: String, + pub job_kind: String, + pub owner_user_id: String, + pub source_module: String, + pub source_entity_id: String, + pub request_label: String, + pub request_prompt: Option, + pub status: String, + pub last_error_message: Option, + pub created_at_micros: i64, + pub started_at_micros: Option, + pub completed_at_micros: Option, + pub updated_at_micros: i64, + pub price_mud_points: u64, + pub refund_ledger_id: Option, + pub notification_acknowledged_at_micros: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)] +pub struct ExternalGenerationJobSummaryProcedureResult { + pub ok: bool, + pub job: Option, + pub jobs: Vec, + pub pending_count: u32, + pub running_count: u32, + pub unacknowledged_terminal_count: u32, + pub now_micros: i64, + pub error_message: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)] +pub struct ExternalGenerationJobSummaryBackfillProcedureResult { + pub ok: bool, + pub dry_run: bool, + pub scanned_count: u64, + pub selected_count: u32, + pub upserted_count: u32, + pub next_cursor_job_id: Option, + pub has_more: bool, + pub error_message: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)] +pub struct ExternalGenerationJobPayloadCompactionProcedureResult { + pub ok: bool, + pub dry_run: bool, + pub scanned_count: u64, + pub matched_count: u32, + pub updated_count: u32, + pub before_bytes: u64, + pub after_bytes: u64, + pub inline_media_count: u64, + pub invalid_json_count: u32, + pub next_cursor_job_id: Option, + pub has_more: bool, + pub error_message: Option, +} + #[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)] pub struct ExternalGenerationQueueStatsSnapshot { pub pending_count: u32, @@ -371,6 +492,93 @@ pub fn acknowledge_external_generation_jobs_and_return( } } +// 正式任务列表、详情与通知确认只返回轻量投影,禁止把持久任务 payload 带入 UI 读取链路。 +#[spacetimedb::procedure] +pub fn get_external_generation_job_summary_and_return( + ctx: &mut ProcedureContext, + input: ExternalGenerationJobGetInput, +) -> ExternalGenerationJobSummaryProcedureResult { + let caller = ctx.sender(); + match ctx.try_with_tx(|tx| { + crate::editor_project_storage::require_editor_generation_runtime_service_identity( + tx, caller, + )?; + get_external_generation_job_summary_tx(tx, input.clone()) + }) { + Ok(job) => single_external_generation_job_summary_result(job), + Err(message) => failed_external_generation_job_summary_result(message), + } +} + +#[spacetimedb::procedure] +pub fn list_external_generation_job_summaries_and_return( + ctx: &mut ProcedureContext, + input: ExternalGenerationJobListInput, +) -> ExternalGenerationJobSummaryProcedureResult { + let caller = ctx.sender(); + match ctx.try_with_tx(|tx| { + crate::editor_project_storage::require_editor_generation_runtime_service_identity( + tx, caller, + )?; + list_external_generation_job_summaries_tx(tx, input.clone()) + }) { + Ok(result) => result, + Err(message) => failed_external_generation_job_summary_result(message), + } +} + +#[spacetimedb::procedure] +pub fn acknowledge_external_generation_job_summaries_and_return( + ctx: &mut ProcedureContext, + input: ExternalGenerationJobAcknowledgeInput, +) -> ExternalGenerationJobSummaryProcedureResult { + let caller = ctx.sender(); + match ctx.try_with_tx(|tx| { + crate::editor_project_storage::require_editor_generation_runtime_service_identity( + tx, caller, + )?; + acknowledge_external_generation_job_summaries_tx(tx, input.clone()) + }) { + Ok(result) => result, + Err(message) => failed_external_generation_job_summary_result(message), + } +} + +// 历史投影回填是显式维护动作;正式 list 不回扫大 payload 表。 +#[spacetimedb::procedure] +pub fn backfill_external_generation_job_summaries_and_return( + ctx: &mut ProcedureContext, + input: ExternalGenerationJobSummaryBackfillInput, +) -> ExternalGenerationJobSummaryBackfillProcedureResult { + let caller = ctx.sender(); + match ctx.try_with_tx(|tx| { + crate::migration::require_migration_operator(tx, caller)?; + backfill_external_generation_job_summaries_tx(tx, input.clone()) + }) { + Ok(result) => result, + Err(message) => { + failed_external_generation_job_summary_backfill_result(input.dry_run, message) + } + } +} + +#[spacetimedb::procedure] +pub fn compact_external_generation_job_payloads_and_return( + ctx: &mut ProcedureContext, + input: ExternalGenerationJobPayloadCompactionInput, +) -> ExternalGenerationJobPayloadCompactionProcedureResult { + let caller = ctx.sender(); + match ctx.try_with_tx(|tx| { + crate::migration::require_migration_operator(tx, caller)?; + compact_external_generation_job_payloads_tx(tx, input.clone()) + }) { + Ok(result) => result, + Err(message) => { + failed_external_generation_job_payload_compaction_result(input.dry_run, message) + } + } +} + #[spacetimedb::procedure] pub fn get_external_generation_queue_stats_and_return( ctx: &mut ProcedureContext, @@ -418,7 +626,8 @@ fn enqueue_external_generation_job_tx( "external_generation_job.request_label", &input.request_label, )?; - validate_required( + let request_payload_json = validate_external_generation_persisted_payload_for_source( + &input.source_module, "external_generation_job.request_payload_json", &input.request_payload_json, )?; @@ -429,6 +638,7 @@ fn enqueue_external_generation_job_tx( .dedupe_key() .find(&input.dedupe_key) { + persist_external_generation_job_summary(ctx, &row); return Ok(map_external_generation_job_row(row)); } if ctx @@ -451,7 +661,7 @@ fn enqueue_external_generation_job_tx( source_module: input.source_module.trim().to_string(), source_entity_id: input.source_entity_id.trim().to_string(), request_label: input.request_label.trim().to_string(), - request_payload_json: input.request_payload_json.trim().to_string(), + request_payload_json, status: EXTERNAL_GENERATION_STATUS_PENDING.to_string(), attempt: 0, max_attempts: input.max_attempts.max(1), @@ -469,7 +679,7 @@ fn enqueue_external_generation_job_tx( refund_ledger_id: None, notification_acknowledged_at: None, }; - ctx.db.external_generation_job().insert(row.clone()); + persist_external_generation_job_row(ctx, row.clone()); insert_external_generation_job_event( ctx, &row, @@ -619,11 +829,20 @@ fn complete_external_generation_job_tx( &input.worker_id, &input.lease_token, )?; + let result_payload_json = if is_external_generation_editor_source(&row.source_module) { + validate_optional_external_generation_payload_json( + "external_generation_job.result_payload_json", + input.result_payload_json.as_deref(), + )? + } else { + input + .result_payload_json + .as_deref() + .and_then(normalize_optional_text) + }; let completed_at = ctx.timestamp; row.status = EXTERNAL_GENERATION_STATUS_COMPLETED.to_string(); - row.result_payload_json = input - .result_payload_json - .and_then(|value| normalize_optional_text(value.as_str())); + row.result_payload_json = result_payload_json; row.lease_expires_at = None; row.completed_at = Some(completed_at); row.updated_at = completed_at; @@ -643,74 +862,141 @@ fn get_external_generation_job_tx( ctx: &ReducerContext, input: ExternalGenerationJobGetInput, ) -> Result { - validate_required("external_generation_job.job_id", &input.job_id)?; - validate_required( - "external_generation_job.owner_user_id", - &input.owner_user_id, - )?; - let row = ctx - .db - .external_generation_job() - .job_id() - .find(&input.job_id.trim().to_string()) - .ok_or_else(|| "external_generation_job 不存在".to_string())?; - if row.owner_user_id.trim() != input.owner_user_id.trim() { - return Err("external_generation_job 不存在".to_string()); - } - - Ok(map_external_generation_job_row(row)) + get_external_generation_job_summary_tx(ctx, input) + .map(map_external_generation_job_summary_to_compat_snapshot) } fn list_external_generation_jobs_tx( ctx: &ReducerContext, input: ExternalGenerationJobListInput, ) -> Result { + let result = list_external_generation_job_summaries_tx(ctx, input)?; + Ok(ExternalGenerationJobProcedureResult { + ok: result.ok, + job: None, + jobs: result + .jobs + .into_iter() + .map(map_external_generation_job_summary_to_compat_snapshot) + .collect(), + pending_count: result.pending_count, + running_count: result.running_count, + unacknowledged_terminal_count: result.unacknowledged_terminal_count, + now_micros: result.now_micros, + error_message: result.error_message, + }) +} + +fn acknowledge_external_generation_jobs_tx( + ctx: &ReducerContext, + input: ExternalGenerationJobAcknowledgeInput, +) -> Result { + let result = acknowledge_external_generation_job_summaries_tx(ctx, input)?; + Ok(ExternalGenerationJobProcedureResult { + ok: result.ok, + job: None, + jobs: result + .jobs + .into_iter() + .map(map_external_generation_job_summary_to_compat_snapshot) + .collect(), + pending_count: result.pending_count, + running_count: result.running_count, + unacknowledged_terminal_count: result.unacknowledged_terminal_count, + now_micros: result.now_micros, + error_message: result.error_message, + }) +} + +fn get_external_generation_job_summary_tx( + ctx: &ReducerContext, + input: ExternalGenerationJobGetInput, +) -> Result { + validate_required("external_generation_job.job_id", &input.job_id)?; + validate_required( + "external_generation_job.owner_user_id", + &input.owner_user_id, + )?; + let job_id = input.job_id.trim().to_string(); + let owner_user_id = input.owner_user_id.trim(); + + if let Some(summary) = ctx + .db + .external_generation_job_summary() + .job_id() + .find(&job_id) + { + if summary.owner_user_id.trim() != owner_user_id { + return Err("external_generation_job 不存在".to_string()); + } + return Ok(map_external_generation_job_summary_row(summary)); + } + + // 详情兼容旧任务时只允许按主键回填一行,绝不按 owner 扫描完整 payload 表。 + let row = ctx + .db + .external_generation_job() + .job_id() + .find(&job_id) + .ok_or_else(|| "external_generation_job 不存在".to_string())?; + if row.owner_user_id.trim() != owner_user_id { + return Err("external_generation_job 不存在".to_string()); + } + let summary = persist_external_generation_job_summary(ctx, &row); + Ok(map_external_generation_job_summary_row(summary)) +} + +fn list_external_generation_job_summaries_tx( + ctx: &ReducerContext, + input: ExternalGenerationJobListInput, +) -> Result { validate_required( "external_generation_job.owner_user_id", &input.owner_user_id, )?; let owner_user_id = input.owner_user_id.trim().to_string(); let now_micros = ctx.timestamp.to_micros_since_unix_epoch(); - let (pending_count, running_count, unacknowledged_terminal_count) = - count_external_generation_jobs_for_owner(ctx, &owner_user_id); let status_filter = normalize_external_generation_job_status_filter(&input.statuses); - let mut rows = Vec::new(); + let limit = input.limit.clamp(1, 100) as usize; + let mut rows = Vec::with_capacity(limit); + let mut pending_count = 0u32; + let mut running_count = 0u32; + let mut unacknowledged_terminal_count = 0u32; + // 这里故意只读轻量投影,并在单次 owner 扫描中同时计数和维护固定大小 top-N; + // 历史任务由 operator maintenance procedure 显式分批回填。 for row in ctx .db - .external_generation_job() - .by_external_generation_job_owner_user_id() + .external_generation_job_summary() + .by_external_generation_job_summary_owner_user_id() .filter(&owner_user_id) { + match row.status.as_str() { + EXTERNAL_GENERATION_STATUS_PENDING => pending_count = pending_count.saturating_add(1), + EXTERNAL_GENERATION_STATUS_RUNNING => running_count = running_count.saturating_add(1), + EXTERNAL_GENERATION_STATUS_COMPLETED | EXTERNAL_GENERATION_STATUS_FAILED => { + if row.notification_acknowledged_at.is_none() { + unacknowledged_terminal_count = unacknowledged_terminal_count.saturating_add(1); + } + } + _ => {} + } let should_include = input.include_acknowledged_terminal - || !is_external_generation_job_terminal(&row) + || !is_external_generation_job_summary_terminal(&row) || row.notification_acknowledged_at.is_none(); let should_include_status = status_filter.is_empty() || status_filter.iter().any(|status| row.status == *status); if should_include && should_include_status { - rows.push(row); + retain_external_generation_job_summary_top_n(&mut rows, row, limit); } } - rows.sort_by(|left, right| { - external_generation_job_sort_bucket(left) - .cmp(&external_generation_job_sort_bucket(right)) - .then_with(|| { - external_generation_job_sort_time_micros(right) - .cmp(&external_generation_job_sort_time_micros(left)) - }) - .then_with(|| left.job_id.cmp(&right.job_id)) - }); - - let limit = input.limit.clamp(1, 100) as usize; - rows.truncate(limit); - - Ok(ExternalGenerationJobProcedureResult { + Ok(ExternalGenerationJobSummaryProcedureResult { ok: true, job: None, jobs: rows .into_iter() - .map(map_external_generation_job_row) + .map(map_external_generation_job_summary_row) .collect(), pending_count, running_count, @@ -720,41 +1006,31 @@ fn list_external_generation_jobs_tx( }) } -fn acknowledge_external_generation_jobs_tx( +fn acknowledge_external_generation_job_summaries_tx( ctx: &ReducerContext, input: ExternalGenerationJobAcknowledgeInput, -) -> Result { +) -> Result { validate_required( "external_generation_job.owner_user_id", &input.owner_user_id, )?; - if input.job_ids.is_empty() { - return Ok(ExternalGenerationJobProcedureResult { - ok: true, - job: None, - jobs: Vec::new(), - pending_count: 0, - running_count: 0, - unacknowledged_terminal_count: 0, - now_micros: ctx.timestamp.to_micros_since_unix_epoch(), - error_message: None, - }); - } - let owner_user_id = input.owner_user_id.trim().to_string(); let acknowledged_at = Timestamp::from_micros_since_unix_epoch(input.acknowledged_at_micros); let mut acknowledged = Vec::new(); for job_id in input.job_ids.iter().take(100) { + let normalized_job_id = job_id.trim().to_string(); let Some(mut row) = ctx .db - .external_generation_job() + .external_generation_job_summary() .job_id() - .find(&job_id.trim().to_string()) + .find(&normalized_job_id) else { continue; }; - if row.owner_user_id.trim() != owner_user_id || !is_external_generation_job_terminal(&row) { + if row.owner_user_id.trim() != owner_user_id + || !is_external_generation_job_summary_terminal(&row) + { continue; } if row.notification_acknowledged_at.is_some() { @@ -762,21 +1038,23 @@ fn acknowledge_external_generation_jobs_tx( } row.notification_acknowledged_at = Some(acknowledged_at); row.updated_at = acknowledged_at; - persist_external_generation_job_row(ctx, row.clone()); - insert_external_generation_job_event( + ctx.db + .external_generation_job_summary() + .job_id() + .update(row.clone()); + insert_external_generation_job_summary_event( ctx, &row, EXTERNAL_GENERATION_EVENT_ACKNOWLEDGED, Some("用户已确认任务通知".to_string()), - None, acknowledged_at, ); - acknowledged.push(map_external_generation_job_row(row)); + acknowledged.push(map_external_generation_job_summary_row(row)); } - let (pending_count, running_count, unacknowledged_terminal_count) = - count_external_generation_jobs_for_owner(ctx, &owner_user_id); - Ok(ExternalGenerationJobProcedureResult { + let (pending_count, running_count, unacknowledged_terminal_count) = + count_external_generation_job_summaries_for_owner(ctx, &owner_user_id); + Ok(ExternalGenerationJobSummaryProcedureResult { ok: true, job: None, jobs: acknowledged, @@ -788,6 +1066,178 @@ fn acknowledge_external_generation_jobs_tx( }) } +fn backfill_external_generation_job_summaries_tx( + ctx: &ReducerContext, + input: ExternalGenerationJobSummaryBackfillInput, +) -> Result { + let owner_user_id = input + .owner_user_id + .as_deref() + .and_then(normalize_optional_text); + let cursor_job_id = input + .cursor_job_id + .as_deref() + .and_then(normalize_optional_text); + let limit = input + .limit + .clamp(1, MAX_EXTERNAL_GENERATION_MAINTENANCE_BATCH_SIZE) as usize; + let cursor_range = external_generation_job_maintenance_cursor_range(cursor_job_id.as_deref()); + let cursor_to_skip = cursor_job_id.clone(); + let rows = ctx + .db + .external_generation_job() + .by_external_generation_job_cursor() + .filter(cursor_range) + .filter(move |row| { + cursor_to_skip + .as_deref() + .is_none_or(|cursor| row.job_id != cursor) + }); + let (job_ids, next_cursor_job_id, has_more, scanned_count) = + select_external_generation_job_ids_for_maintenance(rows, limit, |row| { + owner_user_id + .as_deref() + .is_none_or(|owner| row.owner_user_id.trim() == owner) + && ctx + .db + .external_generation_job_summary() + .job_id() + .find(&row.job_id) + .is_none() + }); + + let mut upserted_count = 0u32; + if !input.dry_run { + for job_id in &job_ids { + if let Some(row) = ctx.db.external_generation_job().job_id().find(job_id) { + persist_external_generation_job_summary(ctx, &row); + upserted_count = upserted_count.saturating_add(1); + } + } + } + + Ok(ExternalGenerationJobSummaryBackfillProcedureResult { + ok: true, + dry_run: input.dry_run, + scanned_count, + selected_count: job_ids.len() as u32, + upserted_count, + next_cursor_job_id, + has_more, + error_message: None, + }) +} + +fn compact_external_generation_job_payloads_tx( + ctx: &ReducerContext, + input: ExternalGenerationJobPayloadCompactionInput, +) -> Result { + let cursor_job_id = input + .cursor_job_id + .as_deref() + .and_then(normalize_optional_text); + let limit = input + .limit + .clamp(1, MAX_EXTERNAL_GENERATION_MAINTENANCE_BATCH_SIZE) as usize; + let cursor_range = external_generation_job_maintenance_cursor_range(cursor_job_id.as_deref()); + let cursor_to_skip = cursor_job_id.clone(); + let rows = ctx + .db + .external_generation_job() + .by_external_generation_job_source_cursor() + .filter((EXTERNAL_GENERATION_EDITOR_SOURCE_MODULE, cursor_range)) + .filter(move |row| { + cursor_to_skip + .as_deref() + .is_none_or(|cursor| row.job_id != cursor) + }); + let (job_ids, next_cursor_job_id, has_more, scanned_count) = + select_external_generation_job_ids_for_maintenance(rows, limit, |row| { + should_compact_external_generation_job_payloads(row, input.completed_before_micros) + }); + + let mut matched_count = 0u32; + let mut updated_count = 0u32; + let mut before_bytes = 0u64; + let mut after_bytes = 0u64; + let mut inline_media_count = 0u64; + let mut invalid_json_count = 0u32; + + for job_id in &job_ids { + let Some(mut row) = ctx.db.external_generation_job().job_id().find(job_id) else { + continue; + }; + if !should_compact_external_generation_job_payloads(&row, input.completed_before_micros) { + continue; + } + + let request_outcome = compact_external_generation_payload_json(&row.request_payload_json); + let result_outcome = row + .result_payload_json + .as_deref() + .map(compact_external_generation_payload_json); + invalid_json_count = invalid_json_count + .saturating_add(u32::from(request_outcome.invalid_json)) + .saturating_add(u32::from( + result_outcome + .as_ref() + .is_some_and(|outcome| outcome.invalid_json), + )); + let job_inline_media_count = request_outcome.inline_media_count.saturating_add( + result_outcome + .as_ref() + .map(|outcome| outcome.inline_media_count) + .unwrap_or(0), + ); + + if job_inline_media_count > 0 { + matched_count = matched_count.saturating_add(1); + inline_media_count = inline_media_count.saturating_add(job_inline_media_count); + for outcome in std::iter::once(&request_outcome).chain(result_outcome.iter()) { + if outcome.inline_media_count > 0 { + before_bytes = before_bytes.saturating_add(outcome.before_bytes); + after_bytes = after_bytes.saturating_add(outcome.after_bytes); + } + } + } + + if input.dry_run { + continue; + } + + if let Some(compacted_json) = request_outcome.compacted_json { + row.request_payload_json = compacted_json; + } + if let Some(outcome) = result_outcome { + if let Some(compacted_json) = outcome.compacted_json { + row.result_payload_json = Some(compacted_json); + } + } + if job_inline_media_count > 0 { + persist_external_generation_job_row(ctx, row); + updated_count = updated_count.saturating_add(1); + } else { + // 即使无需压缩,正式执行也顺带补齐该终态任务的轻量投影。 + persist_external_generation_job_summary(ctx, &row); + } + } + + Ok(ExternalGenerationJobPayloadCompactionProcedureResult { + ok: true, + dry_run: input.dry_run, + scanned_count, + matched_count, + updated_count, + before_bytes, + after_bytes, + inline_media_count, + invalid_json_count, + next_cursor_job_id, + has_more, + error_message: None, + }) +} + fn renew_external_generation_job_lease_tx( ctx: &ReducerContext, input: ExternalGenerationJobRenewLeaseInput, @@ -822,10 +1272,8 @@ fn fail_external_generation_job_tx( ctx: &ReducerContext, input: ExternalGenerationJobFailInput, ) -> Result { - let error_message = input.error_message.trim(); - if error_message.is_empty() { - return Err("external_generation_job.error_message 不能为空".to_string()); - } + let error_message = normalize_external_generation_error_message(&input.error_message) + .ok_or_else(|| "external_generation_job.error_message 不能为空".to_string())?; let mut row = get_worker_owned_external_generation_job( ctx, @@ -839,7 +1287,7 @@ fn fail_external_generation_job_tx( input.failed_at_micros, "external_generation_job.retry_delay", )?; - row.last_error_message = Some(error_message.to_string()); + row.last_error_message = Some(error_message.clone()); row.refund_ledger_id = input .refund_ledger_id .and_then(|value| normalize_optional_text(value.as_str())); @@ -861,7 +1309,7 @@ fn fail_external_generation_job_tx( ctx, &row, EXTERNAL_GENERATION_EVENT_FAILED, - Some(error_message.to_string()), + Some(error_message), Some(input.worker_id), failed_at, ); @@ -1030,7 +1478,68 @@ fn is_external_generation_job_terminal(row: &ExternalGenerationJob) -> bool { ) } -fn count_external_generation_jobs_for_owner( +fn is_external_generation_job_summary_terminal(row: &ExternalGenerationJobSummary) -> bool { + matches!( + row.status.as_str(), + EXTERNAL_GENERATION_STATUS_COMPLETED + | EXTERNAL_GENERATION_STATUS_FAILED + | EXTERNAL_GENERATION_STATUS_CANCELLED + ) +} + +fn should_compact_external_generation_job_payloads( + row: &ExternalGenerationJob, + completed_before_micros: Option, +) -> bool { + if !is_external_generation_editor_source(&row.source_module) + || !is_external_generation_job_terminal(row) + { + return false; + } + completed_before_micros.is_none_or(|cutoff| { + row.completed_at + .unwrap_or(row.updated_at) + .to_micros_since_unix_epoch() + <= cutoff + }) +} + +fn external_generation_job_maintenance_cursor_range( + cursor_job_id: Option<&str>, +) -> RangeFrom<&str> { + cursor_job_id.unwrap_or_default().. +} + +fn select_external_generation_job_ids_for_maintenance( + rows: impl Iterator, + limit: usize, + mut should_select: impl FnMut(&ExternalGenerationJob) -> bool, +) -> (Vec, Option, bool, u64) { + let mut rows = rows.peekable(); + let mut selected_job_ids = Vec::with_capacity(limit); + let mut next_cursor_job_id = None; + let mut scanned_count = 0u64; + + // 游标选择阶段最多反序列化 limit + 1 条大 payload row;apply 随后按主键逐条 + // 重新读取选中行,避免把整批大 payload 同时保留在事务内存中。 + for row in rows.by_ref().take(limit) { + scanned_count = scanned_count.saturating_add(1); + next_cursor_job_id = Some(row.job_id.clone()); + if should_select(&row) { + selected_job_ids.push(row.job_id); + } + } + + let has_more = rows.peek().is_some(); + ( + selected_job_ids, + next_cursor_job_id, + has_more, + scanned_count, + ) +} + +fn count_external_generation_job_summaries_for_owner( ctx: &ReducerContext, owner_user_id: &str, ) -> (u32, u32, u32) { @@ -1041,8 +1550,8 @@ fn count_external_generation_jobs_for_owner( for row in ctx .db - .external_generation_job() - .by_external_generation_job_owner_user_id() + .external_generation_job_summary() + .by_external_generation_job_summary_owner_user_id() .filter(&normalized_owner_user_id) { match row.status.as_str() { @@ -1060,6 +1569,7 @@ fn count_external_generation_jobs_for_owner( (pending_count, running_count, unacknowledged_terminal_count) } +#[cfg(test)] fn external_generation_job_sort_bucket(row: &ExternalGenerationJob) -> u8 { match row.status.as_str() { EXTERNAL_GENERATION_STATUS_RUNNING => 0, @@ -1076,6 +1586,7 @@ fn external_generation_job_sort_bucket(row: &ExternalGenerationJob) -> u8 { } } +#[cfg(test)] fn external_generation_job_sort_time_micros(row: &ExternalGenerationJob) -> i64 { if is_external_generation_job_terminal(row) { return row @@ -1086,6 +1597,65 @@ fn external_generation_job_sort_time_micros(row: &ExternalGenerationJob) -> i64 row.updated_at.to_micros_since_unix_epoch() } +fn external_generation_job_summary_sort_bucket(row: &ExternalGenerationJobSummary) -> u8 { + match row.status.as_str() { + EXTERNAL_GENERATION_STATUS_RUNNING => 0, + EXTERNAL_GENERATION_STATUS_PENDING => 1, + EXTERNAL_GENERATION_STATUS_COMPLETED | EXTERNAL_GENERATION_STATUS_FAILED + if row.notification_acknowledged_at.is_none() => + { + 2 + } + EXTERNAL_GENERATION_STATUS_COMPLETED | EXTERNAL_GENERATION_STATUS_FAILED => 3, + EXTERNAL_GENERATION_STATUS_CANCELLED if row.notification_acknowledged_at.is_none() => 4, + EXTERNAL_GENERATION_STATUS_CANCELLED => 5, + _ => 6, + } +} + +fn external_generation_job_summary_sort_time_micros(row: &ExternalGenerationJobSummary) -> i64 { + if is_external_generation_job_summary_terminal(row) { + return row + .completed_at + .unwrap_or(row.updated_at) + .to_micros_since_unix_epoch(); + } + row.updated_at.to_micros_since_unix_epoch() +} + +fn compare_external_generation_job_summaries( + left: &ExternalGenerationJobSummary, + right: &ExternalGenerationJobSummary, +) -> Ordering { + external_generation_job_summary_sort_bucket(left) + .cmp(&external_generation_job_summary_sort_bucket(right)) + .then_with(|| { + external_generation_job_summary_sort_time_micros(right) + .cmp(&external_generation_job_summary_sort_time_micros(left)) + }) + .then_with(|| left.job_id.cmp(&right.job_id)) +} + +fn retain_external_generation_job_summary_top_n( + rows: &mut Vec, + row: ExternalGenerationJobSummary, + limit: usize, +) { + if limit == 0 { + return; + } + let insert_at = rows + .binary_search_by(|existing| compare_external_generation_job_summaries(existing, &row)) + .unwrap_or_else(|index| index); + if insert_at >= limit { + return; + } + rows.insert(insert_at, row); + if rows.len() > limit { + rows.pop(); + } +} + fn normalize_external_generation_job_status_filter(statuses: &[String]) -> Vec<&'static str> { statuses .iter() @@ -1123,7 +1693,183 @@ fn persist_external_generation_job_row(ctx: &ReducerContext, row: ExternalGenera .external_generation_job() .job_id() .delete(&row.job_id); - ctx.db.external_generation_job().insert(row); + ctx.db.external_generation_job().insert(row.clone()); + persist_external_generation_job_summary(ctx, &row); +} + +fn persist_external_generation_job_summary( + ctx: &ReducerContext, + row: &ExternalGenerationJob, +) -> ExternalGenerationJobSummary { + let existing = ctx + .db + .external_generation_job_summary() + .job_id() + .find(&row.job_id); + let cached_request_prompt = existing + .as_ref() + .map(|summary| summary.request_prompt.clone()); + let cached_notification_acknowledged_at = existing + .as_ref() + .and_then(|summary| summary.notification_acknowledged_at); + if existing.is_some() { + ctx.db + .external_generation_job_summary() + .job_id() + .delete(&row.job_id); + } + let mut summary = build_external_generation_job_summary_row(row, cached_request_prompt); + if summary.notification_acknowledged_at.is_none() { + summary.notification_acknowledged_at = cached_notification_acknowledged_at; + if let Some(acknowledged_at) = cached_notification_acknowledged_at { + summary.updated_at = summary.updated_at.max(acknowledged_at); + } + } + ctx.db + .external_generation_job_summary() + .insert(summary.clone()); + summary +} + +fn build_external_generation_job_summary_row( + row: &ExternalGenerationJob, + cached_request_prompt: Option>, +) -> ExternalGenerationJobSummary { + ExternalGenerationJobSummary { + job_id: row.job_id.clone(), + job_kind: row.job_kind.clone(), + owner_user_id: row.owner_user_id.clone(), + source_module: row.source_module.clone(), + source_entity_id: row.source_entity_id.clone(), + request_label: row.request_label.clone(), + request_prompt: match cached_request_prompt { + Some(prompt) => prompt + .as_deref() + .and_then(normalize_external_generation_request_prompt_text), + None => extract_external_generation_request_prompt(&row.request_payload_json), + }, + status: row.status.clone(), + last_error_message: row + .last_error_message + .as_deref() + .and_then(normalize_external_generation_error_message), + created_at: row.created_at, + started_at: row.started_at, + completed_at: row.completed_at, + updated_at: row.updated_at, + price_mud_points: row.price_mud_points, + refund_ledger_id: row.refund_ledger_id.clone(), + notification_acknowledged_at: row.notification_acknowledged_at, + } +} + +fn extract_external_generation_request_prompt(request_payload_json: &str) -> Option { + let payload: serde_json::Value = serde_json::from_str(request_payload_json).ok()?; + for key in ["prompt", "promptText", "spritesheetLabel"] { + if let Some(prompt) = payload + .get(key) + .and_then(serde_json::Value::as_str) + .and_then(normalize_external_generation_request_prompt_text) + { + return Some(prompt); + } + } + if let Some(prompt) = payload + .get("iconDescriptions") + .and_then(serde_json::Value::as_array) + .map(|items| { + items + .iter() + .filter_map(serde_json::Value::as_str) + .filter_map(normalize_external_generation_request_prompt_text) + .collect::>() + .join("、") + }) + .and_then(|value| normalize_external_generation_request_prompt_text(&value)) + { + return Some(prompt); + } + payload + .get("generationInputs") + .and_then(|value| value.get("fields")) + .and_then(serde_json::Value::as_array) + .and_then(|fields| { + fields.iter().find_map(|field| { + let title = field + .get("title") + .and_then(serde_json::Value::as_str)? + .trim(); + if !matches!(title, "prompt" | "gpt_description_prompt") { + return None; + } + field + .get("value") + .and_then(serde_json::Value::as_str) + .and_then(normalize_external_generation_request_prompt_text) + }) + }) +} + +fn normalize_external_generation_request_prompt_text(value: &str) -> Option { + let trimmed = value.trim(); + if trimmed.is_empty() || is_external_generation_inline_media_reference(trimmed) { + return None; + } + let mut chars = trimmed.chars(); + let mut normalized = chars + .by_ref() + .take(MAX_EXTERNAL_GENERATION_REQUEST_PROMPT_CHARS) + .collect::(); + if chars.next().is_some() { + normalized.push('…'); + } + Some(normalized) +} + +fn normalize_external_generation_error_message(value: &str) -> Option { + let trimmed = value.trim(); + if trimmed.is_empty() { + return None; + } + if contains_external_generation_inline_media_reference_text(trimmed) { + return Some(INLINE_MEDIA_ERROR_REDACTED_MESSAGE.to_string()); + } + let mut chars = trimmed.chars(); + let mut normalized = chars + .by_ref() + .take(MAX_EXTERNAL_GENERATION_ERROR_MESSAGE_CHARS) + .collect::(); + if chars.next().is_some() { + normalized.push('…'); + } + Some(normalized) +} + +fn contains_external_generation_inline_media_reference_text(value: &str) -> bool { + let normalized = value.to_ascii_lowercase(); + ["data:", "blob:"].iter().any(|scheme| { + normalized.match_indices(scheme).any(|(index, _)| { + index == 0 + || normalized + .as_bytes() + .get(index - 1) + .is_some_and(|previous| { + matches!( + previous, + b' ' | b'\t' + | b'\r' + | b'\n' + | b'"' + | b'\'' + | b'=' + | b'(' + | b'[' + | b'{' + | b',' + ) + }) + }) + }) } fn insert_external_generation_job_event( @@ -1158,6 +1904,43 @@ fn insert_external_generation_job_event( }); } +fn insert_external_generation_job_summary_event( + ctx: &ReducerContext, + row: &ExternalGenerationJobSummary, + event_kind: &str, + message: Option, + created_at: Timestamp, +) { + let event_id = format!( + "{}:{}:{}:summary:{}", + row.job_id.trim(), + event_kind.trim(), + row.status.trim(), + created_at.to_micros_since_unix_epoch() + ); + if ctx + .db + .external_generation_job_event() + .event_id() + .find(&event_id) + .is_some() + { + return; + } + ctx.db + .external_generation_job_event() + .insert(ExternalGenerationJobEvent { + event_id, + job_id: row.job_id.clone(), + owner_user_id: row.owner_user_id.clone(), + event_kind: event_kind.to_string(), + status: row.status.clone(), + message, + worker_id: None, + created_at, + }); +} + fn map_external_generation_job_row(row: ExternalGenerationJob) -> ExternalGenerationJobSnapshot { let notification_acknowledged_at_micros = row .notification_acknowledged_at @@ -1196,6 +1979,71 @@ fn map_external_generation_job_row(row: ExternalGenerationJob) -> ExternalGenera } } +fn map_external_generation_job_summary_row( + row: ExternalGenerationJobSummary, +) -> ExternalGenerationJobSummarySnapshot { + ExternalGenerationJobSummarySnapshot { + job_id: row.job_id, + job_kind: row.job_kind, + owner_user_id: row.owner_user_id, + source_module: row.source_module, + source_entity_id: row.source_entity_id, + request_label: row.request_label, + request_prompt: row.request_prompt, + status: row.status, + last_error_message: row.last_error_message, + created_at_micros: row.created_at.to_micros_since_unix_epoch(), + started_at_micros: row + .started_at + .map(|value| value.to_micros_since_unix_epoch()), + completed_at_micros: row + .completed_at + .map(|value| value.to_micros_since_unix_epoch()), + updated_at_micros: row.updated_at.to_micros_since_unix_epoch(), + price_mud_points: row.price_mud_points, + refund_ledger_id: row.refund_ledger_id, + notification_acknowledged_at_micros: row + .notification_acknowledged_at + .map(|value| value.to_micros_since_unix_epoch()), + } +} + +fn map_external_generation_job_summary_to_compat_snapshot( + summary: ExternalGenerationJobSummarySnapshot, +) -> ExternalGenerationJobSnapshot { + let request_payload_json = summary + .request_prompt + .as_ref() + .map(|prompt| serde_json::json!({ "prompt": prompt }).to_string()) + .unwrap_or_else(|| "{}".to_string()); + ExternalGenerationJobSnapshot { + job_id: summary.job_id, + dedupe_key: String::new(), + job_kind: summary.job_kind, + owner_user_id: summary.owner_user_id, + source_module: summary.source_module, + source_entity_id: summary.source_entity_id, + request_label: summary.request_label, + request_payload_json, + status: summary.status, + attempt: 0, + max_attempts: 0, + last_error_message: summary.last_error_message, + worker_id: None, + lease_expires_at_micros: None, + available_at_micros: summary.updated_at_micros, + result_payload_json: None, + created_at_micros: summary.created_at_micros, + started_at_micros: summary.started_at_micros, + completed_at_micros: summary.completed_at_micros, + updated_at_micros: summary.updated_at_micros, + lease_token: None, + price_mud_points: summary.price_mud_points, + refund_ledger_id: summary.refund_ledger_id, + notification_acknowledged_at_micros: summary.notification_acknowledged_at_micros, + } +} + fn single_external_generation_job_result( job: ExternalGenerationJobSnapshot, ) -> ExternalGenerationJobProcedureResult { @@ -1224,6 +2072,72 @@ fn failed_external_generation_job_result(message: String) -> ExternalGenerationJ } } +fn single_external_generation_job_summary_result( + job: ExternalGenerationJobSummarySnapshot, +) -> ExternalGenerationJobSummaryProcedureResult { + ExternalGenerationJobSummaryProcedureResult { + ok: true, + job: Some(job), + jobs: Vec::new(), + pending_count: 0, + running_count: 0, + unacknowledged_terminal_count: 0, + now_micros: 0, + error_message: None, + } +} + +fn failed_external_generation_job_summary_result( + message: String, +) -> ExternalGenerationJobSummaryProcedureResult { + ExternalGenerationJobSummaryProcedureResult { + ok: false, + job: None, + jobs: Vec::new(), + pending_count: 0, + running_count: 0, + unacknowledged_terminal_count: 0, + now_micros: 0, + error_message: Some(message), + } +} + +fn failed_external_generation_job_summary_backfill_result( + dry_run: bool, + message: String, +) -> ExternalGenerationJobSummaryBackfillProcedureResult { + ExternalGenerationJobSummaryBackfillProcedureResult { + ok: false, + dry_run, + scanned_count: 0, + selected_count: 0, + upserted_count: 0, + next_cursor_job_id: None, + has_more: false, + error_message: Some(message), + } +} + +fn failed_external_generation_job_payload_compaction_result( + dry_run: bool, + message: String, +) -> ExternalGenerationJobPayloadCompactionProcedureResult { + ExternalGenerationJobPayloadCompactionProcedureResult { + ok: false, + dry_run, + scanned_count: 0, + matched_count: 0, + updated_count: 0, + before_bytes: 0, + after_bytes: 0, + inline_media_count: 0, + invalid_json_count: 0, + next_cursor_job_id: None, + has_more: false, + error_message: Some(message), + } +} + fn validate_required(field: &str, value: &str) -> Result<(), String> { if value.trim().is_empty() { return Err(format!("{field} 不能为空")); @@ -1231,6 +2145,170 @@ fn validate_required(field: &str, value: &str) -> Result<(), String> { Ok(()) } +fn validate_external_generation_persisted_payload_for_source( + source_module: &str, + field: &str, + value: &str, +) -> Result { + if is_external_generation_editor_source(source_module) { + validate_external_generation_payload_json(field, value) + } else { + validate_required(field, value)?; + Ok(value.trim().to_string()) + } +} + +fn is_external_generation_editor_source(source_module: &str) -> bool { + source_module.trim() == EXTERNAL_GENERATION_EDITOR_SOURCE_MODULE +} + +fn validate_external_generation_payload_json(field: &str, value: &str) -> Result { + let normalized = value.trim(); + if normalized.is_empty() { + return Err(format!("{field} 不能为空")); + } + let payload_bytes = normalized.len(); + if payload_bytes > MAX_EXTERNAL_GENERATION_PAYLOAD_BYTES { + return Err(format!( + "{field} JSON 大小为 {payload_bytes} 字节,超过持久化上限 {MAX_EXTERNAL_GENERATION_PAYLOAD_BYTES} 字节;请移除冗余数据并改传 objectKey 或 resourceId" + )); + } + let payload = serde_json::from_str::(normalized) + .map_err(|error| format!("{field} 不是合法 JSON: {error}"))?; + if contains_external_generation_inline_media_reference(&payload) { + return Err(format!( + "{field} 禁止包含 data: 或 blob: 内联媒体引用,请先上传对象存储并改传 objectKey 或 resourceId" + )); + } + Ok(normalized.to_string()) +} + +fn validate_optional_external_generation_payload_json( + field: &str, + value: Option<&str>, +) -> Result, String> { + let Some(value) = value else { + return Ok(None); + }; + if value.trim().is_empty() { + return Ok(None); + } + validate_external_generation_payload_json(field, value).map(Some) +} + +fn contains_external_generation_inline_media_reference(value: &serde_json::Value) -> bool { + match value { + serde_json::Value::String(value) => is_external_generation_inline_media_reference(value), + serde_json::Value::Array(values) => values + .iter() + .any(contains_external_generation_inline_media_reference), + serde_json::Value::Object(values) => values.iter().any(|(key, value)| { + is_external_generation_inline_media_reference(key) + || contains_external_generation_inline_media_reference(value) + }), + serde_json::Value::Null | serde_json::Value::Bool(_) | serde_json::Value::Number(_) => { + false + } + } +} + +fn is_external_generation_inline_media_reference(value: &str) -> bool { + value + .trim_start() + .as_bytes() + .get(..5) + .is_some_and(|prefix| { + prefix.eq_ignore_ascii_case(b"data:") || prefix.eq_ignore_ascii_case(b"blob:") + }) +} + +struct ExternalGenerationPayloadCompactionOutcome { + compacted_json: Option, + before_bytes: u64, + after_bytes: u64, + inline_media_count: u64, + invalid_json: bool, +} + +fn compact_external_generation_payload_json( + payload_json: &str, +) -> ExternalGenerationPayloadCompactionOutcome { + let before_bytes = payload_json.len() as u64; + let Ok(mut payload) = serde_json::from_str::(payload_json) else { + return ExternalGenerationPayloadCompactionOutcome { + compacted_json: None, + before_bytes, + after_bytes: before_bytes, + inline_media_count: 0, + invalid_json: true, + }; + }; + let inline_media_count = compact_external_generation_inline_media_references(&mut payload); + if inline_media_count == 0 { + return ExternalGenerationPayloadCompactionOutcome { + compacted_json: None, + before_bytes, + after_bytes: before_bytes, + inline_media_count: 0, + invalid_json: false, + }; + } + let Ok(compacted_json) = serde_json::to_string(&payload) else { + return ExternalGenerationPayloadCompactionOutcome { + compacted_json: None, + before_bytes, + after_bytes: before_bytes, + inline_media_count: 0, + invalid_json: true, + }; + }; + ExternalGenerationPayloadCompactionOutcome { + after_bytes: compacted_json.len() as u64, + compacted_json: Some(compacted_json), + before_bytes, + inline_media_count, + invalid_json: false, + } +} + +fn compact_external_generation_inline_media_references(value: &mut serde_json::Value) -> u64 { + match value { + serde_json::Value::String(text) => { + if is_external_generation_inline_media_reference(text) { + *text = INLINE_MEDIA_REMOVED_PLACEHOLDER.to_string(); + 1 + } else { + 0 + } + } + serde_json::Value::Array(values) => values.iter_mut().fold(0u64, |count, value| { + count.saturating_add(compact_external_generation_inline_media_references(value)) + }), + serde_json::Value::Object(values) => { + let source = std::mem::take(values); + let mut compacted = serde_json::Map::new(); + let mut count = 0u64; + for (index, (key, mut nested)) in source.into_iter().enumerate() { + let mut next_key = key; + if is_external_generation_inline_media_reference(&next_key) { + count = count.saturating_add(1); + next_key = format!("__inline_media_key_removed_{index}"); + while compacted.contains_key(&next_key) { + next_key.push('_'); + } + } + count = count.saturating_add(compact_external_generation_inline_media_references( + &mut nested, + )); + compacted.insert(next_key, nested); + } + *values = compacted; + count + } + serde_json::Value::Null | serde_json::Value::Bool(_) | serde_json::Value::Number(_) => 0, + } +} + fn duration_between_micros(later: i64, earlier: i64, field: &str) -> Result { let duration = later.saturating_sub(earlier); if duration <= 0 { @@ -1519,6 +2597,232 @@ mod tests { assert!(duration_between_micros(1_000, 1_000, "duration").is_err()); } + #[test] + fn persisted_payload_validation_rejects_inline_media_recursively() { + let error = validate_external_generation_payload_json( + "external_generation_job.request_payload_json", + r#"{"prompt":"保留 data 和 blob 普通文本","nested":[{"url":" \nDaTa:image/png;base64,AAAA"}]}"#, + ) + .expect_err("嵌套 Data URL 不得进入持久任务"); + + assert!(error.contains("禁止包含 data: 或 blob:")); + assert!( + validate_external_generation_payload_json( + "external_generation_job.request_payload_json", + r#"{"sourceImageObjectKey":"users/user-1/source.png","resourceId":"resource-1"}"#, + ) + .is_ok() + ); + } + + #[test] + fn persisted_payload_validation_rejects_invalid_or_oversized_json() { + assert!( + validate_external_generation_payload_json( + "external_generation_job.request_payload_json", + "not-json", + ) + .expect_err("持久任务参数必须是合法 JSON") + .contains("不是合法 JSON") + ); + let oversized = format!( + r#"{{"prompt":"{}"}}"#, + "x".repeat(MAX_EXTERNAL_GENERATION_PAYLOAD_BYTES) + ); + assert!( + validate_external_generation_payload_json( + "external_generation_job.result_payload_json", + &oversized, + ) + .expect_err("request 和 result 使用同一持久化上限") + .contains("超过持久化上限") + ); + } + + #[test] + fn persisted_payload_guard_does_not_break_non_editor_transient_references() { + let puzzle_payload = r#"{"reference_image_src":"data:image/png;base64,AAAA"}"#; + + assert_eq!( + validate_external_generation_persisted_payload_for_source( + "puzzle", + "external_generation_job.request_payload_json", + puzzle_payload, + ), + Ok(puzzle_payload.to_string()) + ); + assert!( + validate_external_generation_persisted_payload_for_source( + EXTERNAL_GENERATION_EDITOR_SOURCE_MODULE, + "external_generation_job.request_payload_json", + puzzle_payload, + ) + .is_err() + ); + } + + #[test] + fn job_summary_contract_never_serializes_request_or_result_payload() { + let mut row = external_generation_job_fixture(EXTERNAL_GENERATION_STATUS_COMPLETED); + row.request_payload_json = + r#"{"prompt":"一只橙色陶罐猫","source":"data:image/png;base64,AAAA"}"#.to_string(); + row.result_payload_json = Some(r#"{"image":"data:image/png;base64,BBBB"}"#.to_string()); + let summary_row = build_external_generation_job_summary_row(&row, None); + let summary = map_external_generation_job_summary_row(summary_row); + let serialized = + serde_json::to_value(spacetimedb::sats::ser::serde::SerializeWrapper(&summary)) + .expect("summary 应可序列化"); + + assert_eq!(summary.request_prompt.as_deref(), Some("一只橙色陶罐猫")); + assert!(serialized.get("request_payload_json").is_none()); + assert!(serialized.get("result_payload_json").is_none()); + for internal_field in [ + "dedupe_key", + "attempt", + "max_attempts", + "worker_id", + "lease_expires_at_micros", + "available_at_micros", + ] { + assert!( + serialized.get(internal_field).is_none(), + "summary 不应复制 worker 内部字段 {internal_field}" + ); + } + } + + #[test] + fn job_summary_never_copies_inline_media_as_request_prompt() { + let mut row = external_generation_job_fixture(EXTERNAL_GENERATION_STATUS_COMPLETED); + row.request_payload_json = r#"{"prompt":"data:image/png;base64,AAAA"}"#.to_string(); + + let extracted = build_external_generation_job_summary_row(&row, None); + let cached = build_external_generation_job_summary_row( + &row, + Some(Some(" blob:https://example.test/id".to_string())), + ); + + assert!(extracted.request_prompt.is_none()); + assert!(cached.request_prompt.is_none()); + } + + #[test] + fn job_summary_bounds_and_redacts_error_messages() { + let mut row = external_generation_job_fixture(EXTERNAL_GENERATION_STATUS_FAILED); + row.last_error_message = + Some(r#"provider response: {"image":"data:image/png;base64,AAAA"}"#.to_string()); + let redacted = build_external_generation_job_summary_row(&row, None); + assert_eq!( + redacted.last_error_message.as_deref(), + Some(INLINE_MEDIA_ERROR_REDACTED_MESSAGE) + ); + + row.last_error_message = Some("x".repeat(MAX_EXTERNAL_GENERATION_ERROR_MESSAGE_CHARS + 10)); + let bounded = build_external_generation_job_summary_row(&row, None) + .last_error_message + .expect("非空错误应保留有界摘要"); + assert_eq!( + bounded.chars().count(), + MAX_EXTERNAL_GENERATION_ERROR_MESSAGE_CHARS + 1 + ); + assert!(bounded.ends_with('…')); + assert_eq!( + normalize_external_generation_error_message("metadata: unavailable").as_deref(), + Some("metadata: unavailable") + ); + } + + #[test] + fn job_summary_top_n_keeps_memory_bounded_and_ordered() { + let mut rows = Vec::new(); + for index in 1..=5 { + let mut row = external_generation_job_fixture(EXTERNAL_GENERATION_STATUS_COMPLETED); + row.job_id = format!("extgen-{index}"); + row.completed_at = Some(micros(index)); + row.updated_at = micros(index); + retain_external_generation_job_summary_top_n( + &mut rows, + build_external_generation_job_summary_row(&row, None), + 2, + ); + assert!(rows.len() <= 2); + } + + assert_eq!( + rows.into_iter().map(|row| row.job_id).collect::>(), + vec!["extgen-5".to_string(), "extgen-4".to_string()] + ); + } + + #[test] + fn active_jobs_are_never_payload_compaction_candidates() { + for status in [ + EXTERNAL_GENERATION_STATUS_PENDING, + EXTERNAL_GENERATION_STATUS_RUNNING, + ] { + let mut row = external_generation_job_fixture(status); + row.source_module = EXTERNAL_GENERATION_EDITOR_SOURCE_MODULE.to_string(); + assert!(!should_compact_external_generation_job_payloads(&row, None)); + } + + let mut completed = external_generation_job_fixture(EXTERNAL_GENERATION_STATUS_COMPLETED); + completed.source_module = EXTERNAL_GENERATION_EDITOR_SOURCE_MODULE.to_string(); + completed.completed_at = Some(micros(2_000)); + assert!(should_compact_external_generation_job_payloads( + &completed, + Some(2_000) + )); + assert!(!should_compact_external_generation_job_payloads( + &completed, + Some(1_999) + )); + + let non_editor = external_generation_job_fixture(EXTERNAL_GENERATION_STATUS_COMPLETED); + assert!(!should_compact_external_generation_job_payloads( + &non_editor, + None + )); + } + + #[test] + fn maintenance_selector_bounds_scanned_rows_and_advances_by_last_scanned_job() { + let rows = (1..=4).map(|index| { + let mut row = external_generation_job_fixture(EXTERNAL_GENERATION_STATUS_COMPLETED); + row.job_id = format!("extgen-{index}"); + row + }); + + let (selected, next_cursor, has_more, scanned_count) = + select_external_generation_job_ids_for_maintenance(rows, 2, |row| { + row.job_id != "extgen-1" + }); + + assert_eq!(selected, vec!["extgen-2".to_string()]); + assert_eq!(next_cursor.as_deref(), Some("extgen-2")); + assert!(has_more); + assert_eq!(scanned_count, 2); + } + + #[test] + fn payload_compaction_replaces_nested_inline_media_and_preserves_prompt() { + let payload = r#"{"prompt":"保留这段展示提示","nested":[{"url":"data:image/png;base64,AAAA"},{"deep":{"source":" BLOB:https://example.test/id"}}]}"#; + let outcome = compact_external_generation_payload_json(payload); + let compacted = outcome + .compacted_json + .as_deref() + .expect("含内联媒体的 JSON 应生成压缩结果"); + let value: serde_json::Value = serde_json::from_str(compacted).expect("压缩结果仍是 JSON"); + + assert_eq!(outcome.inline_media_count, 2); + assert!(outcome.after_bytes < outcome.before_bytes); + assert_eq!(value["prompt"], "保留这段展示提示"); + assert_eq!(value["nested"][0]["url"], INLINE_MEDIA_REMOVED_PLACEHOLDER); + assert_eq!( + value["nested"][1]["deep"]["source"], + INLINE_MEDIA_REMOVED_PLACEHOLDER + ); + } + fn external_generation_job_fixture(status: &str) -> ExternalGenerationJob { ExternalGenerationJob { job_id: "extgen-test".to_string(), diff --git a/server-rs/crates/spacetime-module/src/migration.rs b/server-rs/crates/spacetime-module/src/migration.rs index 9b9d195d0..0dc48da85 100644 --- a/server-rs/crates/spacetime-module/src/migration.rs +++ b/server-rs/crates/spacetime-module/src/migration.rs @@ -185,6 +185,7 @@ macro_rules! migration_tables { ai_result_reference, ai_task_event, external_generation_job, + external_generation_job_summary, external_generation_job_event, runtime_snapshot, runtime_setting, @@ -193,6 +194,7 @@ macro_rules! migration_tables { feature_gate_config, user_browse_history, profile_dashboard_state, + profile_daily_free_points, profile_wallet_ledger, asset_operation_wallet_settlement, profile_wallet_config, @@ -843,7 +845,10 @@ fn authorize_database_migration_operator_tx( Ok(()) } -fn require_migration_operator(ctx: &ReducerContext, caller: Identity) -> Result<(), String> { +pub(crate) fn require_migration_operator( + ctx: &ReducerContext, + caller: Identity, +) -> Result<(), String> { if is_database_migration_operator(ctx, caller) { Ok(()) } else { diff --git a/server-rs/crates/spacetime-module/src/runtime/profile.rs b/server-rs/crates/spacetime-module/src/runtime/profile.rs index 737dd319c..7dd00c720 100644 --- a/server-rs/crates/spacetime-module/src/runtime/profile.rs +++ b/server-rs/crates/spacetime-module/src/runtime/profile.rs @@ -1,5 +1,5 @@ use crate::*; -use std::collections::{HashMap, HashSet}; +use std::collections::{HashMap, HashSet, VecDeque}; const PUBLIC_WORK_PLAY_DAY_MICROS: i64 = 86_400_000_000; const PUBLIC_WORK_RECENT_PLAY_WINDOW_DAYS: i64 = 7; @@ -26,6 +26,17 @@ pub struct ProfileDashboardState { pub(crate) updated_at: Timestamp, } +#[spacetimedb::table(accessor = profile_daily_free_points)] +#[derive(Clone)] +pub struct ProfileDailyFreePoints { + #[primary_key] + pub(crate) user_id: String, + pub(crate) day_key: i64, + pub(crate) granted_points: u64, + pub(crate) remaining_points: u64, + pub(crate) updated_at: Timestamp, +} + #[spacetimedb::table( accessor = profile_wallet_ledger, index(accessor = by_profile_wallet_ledger_user_id, btree(columns = [user_id])), @@ -1843,6 +1854,59 @@ fn build_public_work_like_id(source_type: &str, profile_id: &str, user_id: &str) mod tests { use super::*; + fn wallet_ledger_snapshot( + ledger_id: &str, + amount_delta: i64, + balance_after: u64, + created_at_micros: i64, + ) -> RuntimeProfileWalletLedgerEntrySnapshot { + RuntimeProfileWalletLedgerEntrySnapshot { + wallet_ledger_id: ledger_id.to_string(), + user_id: "user-1".to_string(), + amount_delta, + balance_after, + source_type: RuntimeProfileWalletLedgerSourceType::PointsRecharge, + created_at_micros, + metadata_json: "{}".to_string(), + } + } + + #[test] + fn wallet_ledger_sort_follows_balance_settlement_chain_when_payment_time_is_delayed() { + let mut entries = vec![ + wallet_ledger_snapshot("daily-free", 20, 97, 1), + wallet_ledger_snapshot("recharge-180-delayed", 180, 607, 2), + wallet_ledger_snapshot("recharge-60", 60, 157, 3), + wallet_ledger_snapshot("recharge-270", 270, 427, 4), + ]; + + sort_profile_wallet_ledger_entries(&mut entries, 607); + + assert_eq!( + entries + .iter() + .map(|entry| entry.wallet_ledger_id.as_str()) + .collect::>(), + vec![ + "recharge-180-delayed", + "recharge-270", + "recharge-60", + "daily-free", + ] + ); + } + + #[test] + fn wallet_ledger_records_settlement_time_instead_of_delayed_business_event_time() { + let paid_at = Timestamp::from_micros_since_unix_epoch(100); + let settled_at = Timestamp::from_micros_since_unix_epoch(200); + + assert_eq!( + profile_wallet_ledger_recorded_at(paid_at, settled_at), + settled_at + ); + } + fn asset_operation_wallet_ledger( ledger_id: &str, user_id: &str, @@ -2039,16 +2103,67 @@ mod tests { assert_eq!(repeated.badge_label, ""); assert_eq!(repeated.description, "60泥点"); assert_eq!(untouched.product_id, "points_180"); - assert_eq!(untouched.bonus_points, 180); - assert_eq!(untouched.badge_label, "首充双倍"); - assert_eq!(untouched.description, "首充送180泥点"); + assert_eq!(untouched.bonus_points, 90); + assert_eq!(untouched.badge_label, "首充加赠"); + assert_eq!(untouched.description, "首充加赠90泥点"); + } + + #[test] + fn legacy_default_recharge_products_migrate_without_overwriting_admin_config() { + let timestamp = Timestamp::from_micros_since_unix_epoch(1_000_000); + let build_row = |points_amount: u64| ProfileRechargeProductConfig { + product_id: format!("points_{points_amount}"), + title: format!("{points_amount}泥点"), + price_cents: points_amount * 10, + kind: RuntimeProfileRechargeProductKind::Points, + points_amount, + bonus_points: points_amount, + duration_days: 0, + badge_label: "首充双倍".to_string(), + description: format!("首充送{points_amount}泥点"), + tier: RuntimeProfileMembershipTier::Normal, + enabled: true, + sort_order: 0, + created_by: PROFILE_RECHARGE_PRODUCT_SYSTEM_USER_ID.to_string(), + created_at: timestamp, + updated_by: PROFILE_RECHARGE_PRODUCT_SYSTEM_USER_ID.to_string(), + updated_at: timestamp, + membership_period_points: 0, + membership_period_days: 0, + membership_queue_limit: 0, + membership_discount_bps: 0, + }; + + assert_eq!( + resolve_default_point_product_migration(&build_row(180)), + Some(DefaultPointProductMigration { + bonus_points: 90, + badge_label: "首充加赠", + description: "首充加赠90泥点", + enabled: true, + }), + ); + assert_eq!( + resolve_default_point_product_migration(&build_row(1_280)), + Some(DefaultPointProductMigration { + bonus_points: 1_280, + badge_label: "首充双倍", + description: "", + enabled: false, + }), + ); + + let mut admin_row = build_row(300); + admin_row.updated_by = "admin-1".to_string(); + assert_eq!(resolve_default_point_product_migration(&admin_row), None); } #[test] fn membership_wallet_split_metadata_keeps_cycle_reset_for_refund_restore() { - let metadata = metadata_with_membership_wallet_consumption_split( + let metadata = metadata_with_profile_wallet_consumption_split( r#"{"externalGenerationJobId":"job-1"}"#, 150, + DailyFreePointMutation::none(), MembershipCyclePointMutation { points: 120, cycle_resets_at_micros: Some(123_000), @@ -2062,6 +2177,12 @@ mod tests { .and_then(JsonValue::as_str), Some("job-1"), ); + assert_eq!( + parsed + .get("dailyFreePointsDelta") + .and_then(JsonValue::as_i64), + Some(0), + ); assert_eq!( parsed .get("membershipPeriodPointsDelta") @@ -2089,6 +2210,190 @@ mod tests { ); } + #[test] + fn wallet_split_consumes_daily_free_before_membership_and_permanent_points() { + let metadata = metadata_with_profile_wallet_consumption_split( + "{}", + 150, + DailyFreePointMutation { + points: 20, + day_key: Some(20_280), + }, + MembershipCyclePointMutation { + points: 120, + cycle_resets_at_micros: Some(456_000), + }, + ); + let parsed = serde_json::from_str::(&metadata).expect("metadata json"); + + assert_eq!(parsed["dailyFreePointsDelta"], json!(-20)); + assert_eq!(parsed["dailyFreeDayKey"], json!(20_280)); + assert_eq!(parsed["membershipPeriodPointsDelta"], json!(-120)); + assert_eq!(parsed["permanentPointsDelta"], json!(-10)); + assert_eq!( + daily_free_refund_restore_candidate_from_consume_metadata(&metadata), + DailyFreePointMutation { + points: 20, + day_key: Some(20_280), + }, + ); + } + + #[test] + fn cross_day_daily_free_refund_keeps_the_original_permanent_remainder_permanent() { + let metadata = metadata_with_profile_wallet_refund_split( + "{}", + 30, + DailyFreePointMutation { + points: 20, + day_key: Some(20_281), + }, + MembershipCyclePointMutation::none(), + ); + let parsed = serde_json::from_str::(&metadata).expect("metadata json"); + + assert_eq!(parsed["dailyFreePointsDelta"], json!(20)); + assert_eq!(parsed["dailyFreeDayKey"], json!(20_281)); + assert_eq!(parsed["membershipPeriodPointsDelta"], json!(0)); + assert_eq!(parsed["permanentPointsDelta"], json!(10)); + } + + #[test] + fn daily_free_refund_restore_candidate_requires_a_negative_delta_and_day_key() { + assert_eq!( + daily_free_refund_restore_candidate_from_consume_metadata( + r#"{"dailyFreePointsDelta":-20,"dailyFreeDayKey":20280}"#, + ), + DailyFreePointMutation { + points: 20, + day_key: Some(20_280), + }, + ); + assert_eq!( + daily_free_refund_restore_candidate_from_consume_metadata( + r#"{"dailyFreePointsDelta":0,"dailyFreeDayKey":20280}"#, + ), + DailyFreePointMutation::none(), + ); + assert_eq!( + daily_free_refund_restore_candidate_from_consume_metadata("not-json"), + DailyFreePointMutation::none(), + ); + } + + #[test] + fn daily_free_refund_restores_same_day_and_stacks_after_cross_day() { + assert_eq!( + resolve_daily_free_refund_restore_plan(20_280, 20_280, 20_280, 20, 5, 20, 20), + Some(DailyFreeRefundRestorePlan { + restored_points: 15, + target_day_key: 20_280, + granted_points_delta: 0, + }), + ); + assert_eq!( + resolve_daily_free_refund_restore_plan(20_280, 20_281, 20_281, 20, 20, 20, 20), + Some(DailyFreeRefundRestorePlan { + restored_points: 20, + target_day_key: 20_281, + granted_points_delta: 20, + }), + ); + assert_eq!( + resolve_daily_free_refund_restore_plan(20_279, 20_281, 20_281, 40, 40, 20, 20), + Some(DailyFreeRefundRestorePlan { + restored_points: 20, + target_day_key: 20_281, + granted_points_delta: 20, + }), + ); + assert_eq!( + resolve_daily_free_refund_restore_plan(20_281, 20_281, 20_281, 60, 50, 10, 10), + Some(DailyFreeRefundRestorePlan { + restored_points: 10, + target_day_key: 20_281, + granted_points_delta: 0, + }), + ); + assert_eq!( + resolve_daily_free_refresh_plan(Some(20_281), 60, 20_282), + Some(DailyFreeRefreshPlan { + expired_points: 60, + granted_points: 20, + reset: true, + }), + ); + assert_eq!( + resolve_daily_free_refund_restore_plan(20_282, 20_281, 20_281, 20, 20, 20, 20), + None, + ); + } + + #[test] + fn daily_free_reset_boundary_is_beijing_midnight() { + let day_key = 20_280; + let resets_at_micros = profile_daily_free_points_resets_at_micros(day_key); + + assert_eq!(PROFILE_DAILY_FREE_POINTS_PER_DAY, 20); + assert_eq!( + runtime_profile_beijing_day_key(resets_at_micros.saturating_sub(1)), + day_key, + ); + assert_eq!( + runtime_profile_beijing_day_key(resets_at_micros), + day_key + 1, + ); + } + + #[test] + fn daily_free_refresh_plan_grants_once_and_replaces_cross_day_remainder() { + assert_eq!( + resolve_daily_free_refresh_plan(None, 0, 20_280), + Some(DailyFreeRefreshPlan { + expired_points: 0, + granted_points: 20, + reset: false, + }), + ); + assert_eq!( + resolve_daily_free_refresh_plan(Some(20_280), 7, 20_280), + None, + ); + assert_eq!( + resolve_daily_free_refresh_plan(Some(20_280), 7, 20_281), + Some(DailyFreeRefreshPlan { + expired_points: 7, + granted_points: 20, + reset: true, + }), + ); + assert_eq!( + resolve_daily_free_refresh_plan(Some(20_281), 7, 20_280), + None, + ); + } + + #[test] + fn legacy_wallet_snapshot_keeps_current_daily_free_points() { + assert_eq!( + merge_legacy_wallet_balance_with_daily_free_points(100, 20), + 120, + ); + assert_eq!( + merge_legacy_wallet_balance_with_daily_free_points(100, 0), + 100, + ); + assert!(!profile_wallet_ledger_source_blocks_legacy_snapshot_sync( + RuntimeProfileWalletLedgerSourceType::DailyFreeGrant, + )); + assert!(!profile_wallet_ledger_source_blocks_legacy_snapshot_sync( + RuntimeProfileWalletLedgerSourceType::DailyFreeReset, + )); + assert!(profile_wallet_ledger_source_blocks_legacy_snapshot_sync( + RuntimeProfileWalletLedgerSourceType::AssetOperationConsume, + )); + } + #[test] fn membership_refund_restore_candidate_requires_negative_cycle_delta() { assert_eq!( @@ -2440,6 +2745,7 @@ fn sync_profile_dashboard_from_snapshot( game_state: Option<&serde_json::Map>, saved_at: Timestamp, ) { + refresh_profile_daily_free_points(ctx, &snapshot.user_id, ctx.timestamp); let current_state = ctx .db .profile_dashboard_state() @@ -2453,6 +2759,13 @@ fn sync_profile_dashboard_from_snapshot( .as_ref() .map(|row| row.total_play_time_ms) .unwrap_or(0); + let daily_free_remaining_points = ctx + .db + .profile_daily_free_points() + .user_id() + .find(&snapshot.user_id) + .map(|row| row.remaining_points) + .unwrap_or(0); let has_business_wallet_ledger = has_profile_business_wallet_ledger(ctx, &snapshot.user_id); let synced_wallet_balance = if has_business_wallet_ledger { None @@ -2460,6 +2773,12 @@ fn sync_profile_dashboard_from_snapshot( game_state .and_then(|state| state.get("playerCurrency")) .map(|value| module_runtime::read_runtime_json_non_negative_u64(Some(value))) + .map(|legacy_balance| { + merge_legacy_wallet_balance_with_daily_free_points( + legacy_balance, + daily_free_remaining_points, + ) + }) }; let next_wallet_balance = synced_wallet_balance.unwrap_or(previous_wallet_balance); let mut next_total_play_time_ms = previous_total_play_time_ms; @@ -2648,7 +2967,7 @@ fn get_profile_dashboard_snapshot( ) -> Result { let validated_input = build_runtime_profile_dashboard_get_input(input.user_id) .map_err(|error| error.to_string())?; - refresh_profile_membership_cycle(ctx, &validated_input.user_id, ctx.timestamp); + refresh_profile_wallet_expiring_points(ctx, &validated_input.user_id, ctx.timestamp); let state = ctx .db .profile_dashboard_state() @@ -2660,6 +2979,8 @@ fn get_profile_dashboard_snapshot( .by_profile_played_world_user_id() .filter(&validated_input.user_id) .count() as u32; + let daily_free_points = + build_profile_daily_free_points_snapshot(ctx, &validated_input.user_id, ctx.timestamp); Ok(match state { Some(existing) => RuntimeProfileDashboardSnapshot { @@ -2668,6 +2989,7 @@ fn get_profile_dashboard_snapshot( total_play_time_ms: existing.total_play_time_ms, played_world_count, updated_at_micros: Some(existing.updated_at.to_micros_since_unix_epoch()), + daily_free_points, }, None => RuntimeProfileDashboardSnapshot { user_id: validated_input.user_id, @@ -2675,6 +2997,7 @@ fn get_profile_dashboard_snapshot( total_play_time_ms: 0, played_world_count, updated_at_micros: None, + daily_free_points, }, }) } @@ -2685,7 +3008,7 @@ fn list_profile_wallet_ledger_entries( ) -> Result, String> { let validated_input = build_runtime_profile_wallet_ledger_list_input(input.user_id) .map_err(|error| error.to_string())?; - refresh_profile_membership_cycle(ctx, &validated_input.user_id, ctx.timestamp); + refresh_profile_wallet_expiring_points(ctx, &validated_input.user_id, ctx.timestamp); let mut entries = ctx .db @@ -2695,15 +3018,60 @@ fn list_profile_wallet_ledger_entries( .map(|row| build_profile_wallet_ledger_snapshot_from_row(&row)) .collect::>(); + let current_balance = profile_wallet_balance(ctx, &validated_input.user_id); + sort_profile_wallet_ledger_entries(&mut entries, current_balance); + entries.truncate(PROFILE_WALLET_LEDGER_LIST_LIMIT); + + Ok(entries) +} + +fn sort_profile_wallet_ledger_entries( + entries: &mut Vec, + current_balance: u64, +) { entries.sort_by(|left, right| { right .created_at_micros .cmp(&left.created_at_micros) .then_with(|| left.wallet_ledger_id.cmp(&right.wallet_ledger_id)) }); - entries.truncate(PROFILE_WALLET_LEDGER_LIST_LIMIT); - Ok(entries) + let mut positions_by_balance = HashMap::>::new(); + for (position, entry) in entries.iter().enumerate() { + positions_by_balance + .entry(entry.balance_after) + .or_default() + .push_back(position); + } + + let mut remaining = std::mem::take(entries) + .into_iter() + .map(Some) + .collect::>(); + let mut ordered = Vec::with_capacity(remaining.len()); + let mut expected_balance = current_balance; + + while ordered.len() < remaining.len() { + let Some(position) = positions_by_balance + .get_mut(&expected_balance) + .and_then(VecDeque::pop_front) + else { + break; + }; + let Some(entry) = remaining[position].take() else { + break; + }; + let previous_balance = i128::from(entry.balance_after) - i128::from(entry.amount_delta); + let Ok(previous_balance) = u64::try_from(previous_balance) else { + ordered.push(entry); + break; + }; + ordered.push(entry); + expected_balance = previous_balance; + } + + ordered.extend(remaining.into_iter().flatten()); + *entries = ordered; } fn get_profile_play_stats_snapshot( @@ -3908,7 +4276,7 @@ fn build_profile_recharge_center_snapshot( user_id: &str, ) -> RuntimeProfileRechargeCenterSnapshot { ensure_default_profile_recharge_product_config(ctx); - refresh_profile_membership_cycle(ctx, user_id, ctx.timestamp); + refresh_profile_wallet_expiring_points(ctx, user_id, ctx.timestamp); let wallet_balance = ctx .db .profile_dashboard_state() @@ -3947,6 +4315,7 @@ fn build_profile_recharge_center_snapshot( latest_order: latest_profile_recharge_order(ctx, user_id) .map(|row| build_profile_recharge_order_snapshot_from_row(&row)), has_points_recharged, + daily_free_points: build_profile_daily_free_points_snapshot(ctx, user_id, ctx.timestamp), } } @@ -4352,7 +4721,7 @@ fn build_profile_task_center_snapshot( updated_at: Timestamp, ) -> Result { ensure_default_profile_task_config(ctx); - refresh_profile_membership_cycle(ctx, user_id, updated_at); + refresh_profile_wallet_expiring_points(ctx, user_id, updated_at); let day_key = runtime_profile_beijing_day_key(updated_at.to_micros_since_unix_epoch()); let mut configs = ctx.db.profile_task_config().iter().collect::>(); configs.sort_by(|left, right| { @@ -4748,6 +5117,7 @@ fn ensure_default_profile_task_config(ctx: &ReducerContext) -> ProfileTaskConfig fn ensure_default_profile_recharge_product_config(ctx: &ReducerContext) { if ctx.db.profile_recharge_product_config().count() > 0 { + migrate_legacy_default_profile_recharge_product_config(ctx); return; } @@ -4784,6 +5154,97 @@ fn ensure_default_profile_recharge_product_config(ctx: &ReducerContext) { } } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct DefaultPointProductMigration { + bonus_points: u64, + badge_label: &'static str, + description: &'static str, + enabled: bool, +} + +fn resolve_default_point_product_migration( + row: &ProfileRechargeProductConfig, +) -> Option { + if row.kind != RuntimeProfileRechargeProductKind::Points + || row.tier != RuntimeProfileMembershipTier::Normal + || row.updated_by != PROFILE_RECHARGE_PRODUCT_SYSTEM_USER_ID + || !row.enabled + || row.duration_days != 0 + || row.bonus_points != row.points_amount + || row.badge_label != "首充双倍" + || row.description != format!("首充送{}泥点", row.points_amount) + { + return None; + } + + let expected_product_id = format!("points_{}", row.points_amount); + let expected_title = format!("{}泥点", row.points_amount); + if row.product_id != expected_product_id + || row.title != expected_title + || row.price_cents != row.points_amount.saturating_mul(10) + { + return None; + } + + match row.points_amount { + 60 => Some(DefaultPointProductMigration { + bonus_points: 0, + badge_label: "", + description: "60泥点", + enabled: true, + }), + 180 => Some(DefaultPointProductMigration { + bonus_points: 90, + badge_label: "首充加赠", + description: "首充加赠90泥点", + enabled: true, + }), + 300 => Some(DefaultPointProductMigration { + bonus_points: 150, + badge_label: "首充加赠", + description: "首充加赠150泥点", + enabled: true, + }), + 680 => Some(DefaultPointProductMigration { + bonus_points: 340, + badge_label: "首充加赠", + description: "首充加赠340泥点", + enabled: true, + }), + 1_280 | 3_280 => Some(DefaultPointProductMigration { + bonus_points: row.bonus_points, + badge_label: "首充双倍", + description: "", + enabled: false, + }), + _ => None, + } +} + +fn migrate_legacy_default_profile_recharge_product_config(ctx: &ReducerContext) { + let rows = ctx + .db + .profile_recharge_product_config() + .iter() + .collect::>(); + for mut row in rows { + let Some(migration) = resolve_default_point_product_migration(&row) else { + continue; + }; + row.bonus_points = migration.bonus_points; + row.badge_label = migration.badge_label.to_string(); + if !migration.description.is_empty() { + row.description = migration.description.to_string(); + } + row.enabled = migration.enabled; + row.updated_at = ctx.timestamp; + ctx.db + .profile_recharge_product_config() + .product_id() + .update(row); + } +} + fn profile_recharge_product_config_rows( ctx: &ReducerContext, include_disabled: bool, @@ -4861,6 +5322,21 @@ impl MembershipCyclePointMutation { } } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct DailyFreePointMutation { + points: u64, + day_key: Option, +} + +impl DailyFreePointMutation { + fn none() -> Self { + Self { + points: 0, + day_key: None, + } + } +} + struct MembershipCycleInitialization { row: ProfileMembership, granted_points_delta: u64, @@ -5121,7 +5597,7 @@ fn membership_cycle_metadata( .unwrap_or_else(|_| PROFILE_INVITE_CODE_METADATA_DEFAULT_JSON.to_string()) } -fn update_profile_wallet_balance_for_membership_cycle( +fn update_profile_wallet_balance_for_expiring_points( ctx: &ReducerContext, user_id: &str, expired_points: u64, @@ -5194,6 +5670,162 @@ fn update_profile_wallet_balance_for_membership_cycle( }); } +fn daily_free_points_metadata( + action: &str, + expired_points: u64, + granted_points: u64, + day_key: i64, +) -> String { + serde_json::to_string(&json!({ + "action": action, + "dailyFreeDayKey": day_key, + "expiredDailyFreePoints": expired_points, + "grantedDailyFreePoints": granted_points, + })) + .unwrap_or_else(|_| PROFILE_INVITE_CODE_METADATA_DEFAULT_JSON.to_string()) +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct DailyFreeRefreshPlan { + expired_points: u64, + granted_points: u64, + reset: bool, +} + +fn resolve_daily_free_refresh_plan( + current_day_key: Option, + current_remaining_points: u64, + day_key: i64, +) -> Option { + match current_day_key { + Some(current_day_key) if current_day_key >= day_key => None, + Some(_) => Some(DailyFreeRefreshPlan { + expired_points: current_remaining_points, + granted_points: PROFILE_DAILY_FREE_POINTS_PER_DAY, + reset: true, + }), + None => Some(DailyFreeRefreshPlan { + expired_points: 0, + granted_points: PROFILE_DAILY_FREE_POINTS_PER_DAY, + reset: false, + }), + } +} + +fn refresh_profile_daily_free_points(ctx: &ReducerContext, user_id: &str, now: Timestamp) { + let day_key = runtime_profile_beijing_day_key(now.to_micros_since_unix_epoch()); + let current = ctx + .db + .profile_daily_free_points() + .user_id() + .find(&user_id.to_string()); + let Some(plan) = resolve_daily_free_refresh_plan( + current.as_ref().map(|row| row.day_key), + current + .as_ref() + .map(|row| row.remaining_points) + .unwrap_or(0), + day_key, + ) else { + return; + }; + + match current { + Some(row) => { + debug_assert!(plan.reset); + ctx.db + .profile_daily_free_points() + .user_id() + .update(ProfileDailyFreePoints { + day_key, + granted_points: plan.granted_points, + remaining_points: plan.granted_points, + updated_at: now, + ..row + }); + update_profile_wallet_balance_for_expiring_points( + ctx, + user_id, + plan.expired_points, + plan.granted_points, + RuntimeProfileWalletLedgerSourceType::DailyFreeReset, + &format!("daily-free-reset:{user_id}:{day_key}"), + now, + daily_free_points_metadata( + "reset", + plan.expired_points, + plan.granted_points, + day_key, + ), + ); + } + None => { + debug_assert!(!plan.reset); + ctx.db + .profile_daily_free_points() + .insert(ProfileDailyFreePoints { + user_id: user_id.to_string(), + day_key, + granted_points: plan.granted_points, + remaining_points: plan.granted_points, + updated_at: now, + }); + update_profile_wallet_balance_for_expiring_points( + ctx, + user_id, + 0, + plan.granted_points, + RuntimeProfileWalletLedgerSourceType::DailyFreeGrant, + &format!("daily-free-grant:{user_id}:{day_key}"), + now, + daily_free_points_metadata("grant", 0, plan.granted_points, day_key), + ); + } + } +} + +fn profile_daily_free_points_resets_at_micros(day_key: i64) -> i64 { + day_key + .saturating_add(1) + .saturating_mul(PROFILE_RUNTIME_DAY_MICROS) + .saturating_sub(PROFILE_TASK_BEIJING_OFFSET_MICROS) +} + +fn build_profile_daily_free_points_snapshot( + ctx: &ReducerContext, + user_id: &str, + now: Timestamp, +) -> RuntimeProfileDailyFreePointsSnapshot { + let day_key = runtime_profile_beijing_day_key(now.to_micros_since_unix_epoch()); + ctx.db + .profile_daily_free_points() + .user_id() + .find(&user_id.to_string()) + .map(|row| RuntimeProfileDailyFreePointsSnapshot { + day_key: row.day_key, + granted_points: row.granted_points, + remaining_points: row.remaining_points, + resets_at_micros: profile_daily_free_points_resets_at_micros(row.day_key), + updated_at_micros: row.updated_at.to_micros_since_unix_epoch(), + }) + .unwrap_or(RuntimeProfileDailyFreePointsSnapshot { + day_key, + granted_points: PROFILE_DAILY_FREE_POINTS_PER_DAY, + remaining_points: PROFILE_DAILY_FREE_POINTS_PER_DAY, + resets_at_micros: profile_daily_free_points_resets_at_micros(day_key), + updated_at_micros: now.to_micros_since_unix_epoch(), + }) +} + +fn refresh_profile_wallet_expiring_points( + ctx: &ReducerContext, + user_id: &str, + membership_now: Timestamp, +) { + refresh_profile_daily_free_points(ctx, user_id, ctx.timestamp); + refresh_profile_membership_cycle(ctx, user_id, membership_now); +} + fn refresh_profile_membership_cycle(ctx: &ReducerContext, user_id: &str, now: Timestamp) { let Some(mut row) = ctx .db @@ -5214,7 +5846,7 @@ fn refresh_profile_membership_cycle(ctx: &ReducerContext, user_id: &str, now: Ti row.updated_at = now; let ledger_id = format!("membership-period-expire:{user_id}:{expires_at_micros}"); upsert_profile_membership_row(ctx, row); - update_profile_wallet_balance_for_membership_cycle( + update_profile_wallet_balance_for_expiring_points( ctx, user_id, expired_points, @@ -5240,7 +5872,7 @@ fn refresh_profile_membership_cycle(ctx: &ReducerContext, user_id: &str, now: Ti row = initialization.row; let cycle_resets_at = row.cycle_resets_at.clone(); upsert_profile_membership_row(ctx, row); - update_profile_wallet_balance_for_membership_cycle( + update_profile_wallet_balance_for_expiring_points( ctx, user_id, 0, @@ -5294,7 +5926,7 @@ fn refresh_profile_membership_cycle(ctx: &ReducerContext, user_id: &str, now: Ti let reset_micros = next_cycle_started_at.to_micros_since_unix_epoch(); let ledger_id = format!("membership-period-reset:{user_id}:{reset_micros}"); upsert_profile_membership_row(ctx, row); - update_profile_wallet_balance_for_membership_cycle( + update_profile_wallet_balance_for_expiring_points( ctx, user_id, expired_points, @@ -5310,7 +5942,7 @@ fn build_profile_membership_snapshot( ctx: &ReducerContext, user_id: &str, ) -> RuntimeProfileMembershipSnapshot { - refresh_profile_membership_cycle(ctx, user_id, ctx.timestamp); + refresh_profile_wallet_expiring_points(ctx, user_id, ctx.timestamp); let now_micros = ctx.timestamp.to_micros_since_unix_epoch(); let membership = ctx .db @@ -5376,7 +6008,7 @@ fn apply_profile_membership_purchase( product: &RuntimeProfileRechargeProductSnapshot, purchased_at: Timestamp, ) -> Result { - refresh_profile_membership_cycle(ctx, user_id, purchased_at); + refresh_profile_wallet_expiring_points(ctx, user_id, purchased_at); let tier = product.tier; let duration_days = product.duration_days; let period_days = membership_product_period_days(product); @@ -5460,7 +6092,7 @@ fn apply_profile_membership_purchase( "membership-period-grant:{user_id}:{}:{}", purchased_at_micros, product.product_id ); - update_profile_wallet_balance_for_membership_cycle( + update_profile_wallet_balance_for_expiring_points( ctx, user_id, 0, @@ -5925,6 +6557,142 @@ fn consume_profile_membership_cycle_points( } } +fn consume_profile_daily_free_points( + ctx: &ReducerContext, + user_id: &str, + amount: u64, + consumed_at: Timestamp, +) -> DailyFreePointMutation { + if amount == 0 { + return DailyFreePointMutation::none(); + } + let day_key = runtime_profile_beijing_day_key(consumed_at.to_micros_since_unix_epoch()); + let Some(mut row) = ctx + .db + .profile_daily_free_points() + .user_id() + .find(&user_id.to_string()) + else { + return DailyFreePointMutation::none(); + }; + if row.day_key != day_key || row.remaining_points == 0 { + return DailyFreePointMutation::none(); + } + + let consumed = row.remaining_points.min(amount); + row.remaining_points -= consumed; + row.updated_at = consumed_at; + ctx.db.profile_daily_free_points().user_id().update(row); + DailyFreePointMutation { + points: consumed, + day_key: Some(day_key), + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct DailyFreeRefundRestorePlan { + restored_points: u64, + target_day_key: i64, + granted_points_delta: u64, +} + +fn resolve_daily_free_refund_restore_plan( + consumed_day_key: i64, + current_day_key: i64, + row_day_key: i64, + granted_points: u64, + remaining_points: u64, + candidate_points: u64, + refund_amount: u64, +) -> Option { + if candidate_points == 0 || refund_amount == 0 || row_day_key != current_day_key { + return None; + } + + let candidate_points = candidate_points.min(refund_amount); + if consumed_day_key == current_day_key { + let restored_points = candidate_points.min(granted_points.saturating_sub(remaining_points)); + return (restored_points > 0).then_some(DailyFreeRefundRestorePlan { + restored_points, + target_day_key: current_day_key, + granted_points_delta: 0, + }); + } + if consumed_day_key > current_day_key { + return None; + } + + Some(DailyFreeRefundRestorePlan { + restored_points: candidate_points, + target_day_key: current_day_key, + granted_points_delta: candidate_points, + }) +} + +fn restore_profile_daily_free_points_for_refund( + ctx: &ReducerContext, + user_id: &str, + amount: u64, + refund_ledger_id: &str, + refunded_at: Timestamp, +) -> DailyFreePointMutation { + if amount == 0 { + return DailyFreePointMutation::none(); + } + let Some(consume_ledger_id) = refund_ledger_id + .strip_prefix(ASSET_OPERATION_REFUND_LEDGER_PREFIX) + .map(|suffix| format!("{ASSET_OPERATION_CONSUME_LEDGER_PREFIX}{suffix}")) + else { + return DailyFreePointMutation::none(); + }; + let Some(consume_ledger) = ctx + .db + .profile_wallet_ledger() + .wallet_ledger_id() + .find(&consume_ledger_id) + else { + return DailyFreePointMutation::none(); + }; + let Some(metadata_json) = consume_ledger.metadata_json.as_deref() else { + return DailyFreePointMutation::none(); + }; + let restore_candidate = + daily_free_refund_restore_candidate_from_consume_metadata(metadata_json); + let Some(expected_day_key) = restore_candidate.day_key else { + return DailyFreePointMutation::none(); + }; + let current_day_key = runtime_profile_beijing_day_key(refunded_at.to_micros_since_unix_epoch()); + + let Some(mut row) = ctx + .db + .profile_daily_free_points() + .user_id() + .find(&user_id.to_string()) + else { + return DailyFreePointMutation::none(); + }; + let Some(plan) = resolve_daily_free_refund_restore_plan( + expected_day_key, + current_day_key, + row.day_key, + row.granted_points, + row.remaining_points, + restore_candidate.points, + amount, + ) else { + return DailyFreePointMutation::none(); + }; + + row.granted_points = row.granted_points.saturating_add(plan.granted_points_delta); + row.remaining_points = row.remaining_points.saturating_add(plan.restored_points); + row.updated_at = refunded_at; + ctx.db.profile_daily_free_points().user_id().update(row); + DailyFreePointMutation { + points: plan.restored_points, + day_key: Some(plan.target_day_key), + } +} + fn restore_profile_membership_cycle_points_for_refund( ctx: &ReducerContext, user_id: &str, @@ -6024,10 +6792,34 @@ fn membership_refund_restore_candidate_from_consume_metadata( } } -fn metadata_with_membership_wallet_delta_split( +fn daily_free_refund_restore_candidate_from_consume_metadata( metadata_json: &str, +) -> DailyFreePointMutation { + let Some(metadata) = serde_json::from_str::(metadata_json) + .ok() + .filter(JsonValue::is_object) + else { + return DailyFreePointMutation::none(); + }; + let daily_free_delta = metadata + .get("dailyFreePointsDelta") + .and_then(JsonValue::as_i64) + .unwrap_or(0); + if daily_free_delta >= 0 { + return DailyFreePointMutation::none(); + } + DailyFreePointMutation { + points: daily_free_delta.unsigned_abs(), + day_key: metadata.get("dailyFreeDayKey").and_then(JsonValue::as_i64), + } +} + +fn metadata_with_profile_wallet_delta_split( + metadata_json: &str, + daily_free_delta: i64, membership_period_delta: i64, permanent_delta: i64, + daily_free_day_key: Option, cycle_resets_at_micros: Option, ) -> String { let mut metadata = serde_json::from_str::(metadata_json) @@ -6035,11 +6827,15 @@ fn metadata_with_membership_wallet_delta_split( .filter(JsonValue::is_object) .unwrap_or_else(|| json!({})); if let Some(object) = metadata.as_object_mut() { + object.insert("dailyFreePointsDelta".to_string(), json!(daily_free_delta)); object.insert( "membershipPeriodPointsDelta".to_string(), json!(membership_period_delta), ); object.insert("permanentPointsDelta".to_string(), json!(permanent_delta)); + if let Some(daily_free_day_key) = daily_free_day_key { + object.insert("dailyFreeDayKey".to_string(), json!(daily_free_day_key)); + } if let Some(cycle_resets_at_micros) = cycle_resets_at_micros { object.insert( "cycleResetsAtMicros".to_string(), @@ -6050,38 +6846,46 @@ fn metadata_with_membership_wallet_delta_split( serde_json::to_string(&metadata).unwrap_or_else(|_| metadata_json.to_string()) } -fn metadata_with_membership_wallet_consumption_split( +fn metadata_with_profile_wallet_consumption_split( metadata_json: &str, total_consumed: u64, + daily_free_consumed: DailyFreePointMutation, membership_period_consumed: MembershipCyclePointMutation, ) -> String { if total_consumed == 0 { return metadata_json.to_string(); } - let membership_points = membership_period_consumed.points; - let permanent_consumed = total_consumed.saturating_sub(membership_points); - metadata_with_membership_wallet_delta_split( + let permanent_consumed = total_consumed + .saturating_sub(daily_free_consumed.points) + .saturating_sub(membership_period_consumed.points); + metadata_with_profile_wallet_delta_split( metadata_json, - -(membership_points as i64), + -(daily_free_consumed.points as i64), + -(membership_period_consumed.points as i64), -(permanent_consumed as i64), + daily_free_consumed.day_key, membership_period_consumed.cycle_resets_at_micros, ) } -fn metadata_with_membership_wallet_refund_split( +fn metadata_with_profile_wallet_refund_split( metadata_json: &str, total_refunded: u64, + daily_free_refunded: DailyFreePointMutation, membership_period_refunded: MembershipCyclePointMutation, ) -> String { if total_refunded == 0 { return metadata_json.to_string(); } - let membership_points = membership_period_refunded.points; - let permanent_refunded = total_refunded.saturating_sub(membership_points); - metadata_with_membership_wallet_delta_split( + let permanent_refunded = total_refunded + .saturating_sub(daily_free_refunded.points) + .saturating_sub(membership_period_refunded.points); + metadata_with_profile_wallet_delta_split( metadata_json, - membership_points as i64, + daily_free_refunded.points as i64, + membership_period_refunded.points as i64, permanent_refunded as i64, + daily_free_refunded.day_key, membership_period_refunded.cycle_resets_at_micros, ) } @@ -6096,7 +6900,9 @@ fn apply_profile_wallet_signed_delta( idempotent: bool, metadata_json: &str, ) -> Result { - refresh_profile_membership_cycle(ctx, user_id, created_at); + let settled_at = ctx.timestamp; + let ledger_recorded_at = profile_wallet_ledger_recorded_at(created_at, settled_at); + refresh_profile_wallet_expiring_points(ctx, user_id, settled_at); if idempotent { if let Some(existing) = ctx .db @@ -6125,42 +6931,72 @@ fn apply_profile_wallet_signed_delta( let created_state_at = current .as_ref() .map(|row| row.created_at) - .unwrap_or(created_at); + .unwrap_or(ledger_recorded_at); + let daily_free_consumed = if amount_delta < 0 + && !matches!( + source_type, + RuntimeProfileWalletLedgerSourceType::MembershipPeriodReset + | RuntimeProfileWalletLedgerSourceType::DailyFreeReset + ) { + consume_profile_daily_free_points(ctx, user_id, amount_delta.unsigned_abs(), settled_at) + } else { + DailyFreePointMutation::none() + }; let membership_period_consumed = if amount_delta < 0 - && source_type != RuntimeProfileWalletLedgerSourceType::MembershipPeriodReset - { + && !matches!( + source_type, + RuntimeProfileWalletLedgerSourceType::MembershipPeriodReset + | RuntimeProfileWalletLedgerSourceType::DailyFreeReset + ) { consume_profile_membership_cycle_points( ctx, user_id, - amount_delta.unsigned_abs(), - created_at, + amount_delta + .unsigned_abs() + .saturating_sub(daily_free_consumed.points), + settled_at, ) } else { MembershipCyclePointMutation::none() }; + let daily_free_refunded = if amount_delta > 0 + && source_type == RuntimeProfileWalletLedgerSourceType::AssetOperationRefund + { + restore_profile_daily_free_points_for_refund( + ctx, + user_id, + amount_delta as u64, + ledger_id, + settled_at, + ) + } else { + DailyFreePointMutation::none() + }; let membership_period_refunded = if amount_delta > 0 && source_type == RuntimeProfileWalletLedgerSourceType::AssetOperationRefund { restore_profile_membership_cycle_points_for_refund( ctx, user_id, - amount_delta as u64, + (amount_delta as u64).saturating_sub(daily_free_refunded.points), ledger_id, - created_at, + settled_at, ) } else { MembershipCyclePointMutation::none() }; let ledger_metadata_json = if amount_delta < 0 { - metadata_with_membership_wallet_consumption_split( + metadata_with_profile_wallet_consumption_split( metadata_json, amount_delta.unsigned_abs(), + daily_free_consumed, membership_period_consumed, ) } else if amount_delta > 0 { - metadata_with_membership_wallet_refund_split( + metadata_with_profile_wallet_refund_split( metadata_json, amount_delta as u64, + daily_free_refunded, membership_period_refunded, ) } else { @@ -6179,7 +7015,7 @@ fn apply_profile_wallet_signed_delta( wallet_balance: next_balance, total_play_time_ms: existing.total_play_time_ms, created_at: existing.created_at, - updated_at: created_at, + updated_at: ledger_recorded_at, }); } else { ctx.db @@ -6189,7 +7025,7 @@ fn apply_profile_wallet_signed_delta( wallet_balance: next_balance, total_play_time_ms: 0, created_at: created_state_at, - updated_at: created_at, + updated_at: ledger_recorded_at, }); } @@ -6199,13 +7035,20 @@ fn apply_profile_wallet_signed_delta( amount_delta, balance_after: next_balance, source_type, - created_at, + created_at: ledger_recorded_at, metadata_json: Some(ledger_metadata_json), }); Ok(next_balance) } +fn profile_wallet_ledger_recorded_at( + _business_event_at: Timestamp, + settled_at: Timestamp, +) -> Timestamp { + settled_at +} + fn validate_idempotent_profile_wallet_ledger( existing: &ProfileWalletLedger, expected_user_id: &str, @@ -6256,10 +7099,28 @@ fn has_profile_business_wallet_ledger(ctx: &ReducerContext, user_id: &str) -> bo .filter(user_id) .any(|row| { row.user_id == user_id - && row.source_type != RuntimeProfileWalletLedgerSourceType::SnapshotSync + && profile_wallet_ledger_source_blocks_legacy_snapshot_sync(row.source_type) }) } +fn profile_wallet_ledger_source_blocks_legacy_snapshot_sync( + source_type: RuntimeProfileWalletLedgerSourceType, +) -> bool { + !matches!( + source_type, + RuntimeProfileWalletLedgerSourceType::SnapshotSync + | RuntimeProfileWalletLedgerSourceType::DailyFreeGrant + | RuntimeProfileWalletLedgerSourceType::DailyFreeReset + ) +} + +fn merge_legacy_wallet_balance_with_daily_free_points( + legacy_balance: u64, + daily_free_remaining_points: u64, +) -> u64 { + legacy_balance.saturating_add(daily_free_remaining_points) +} + fn latest_profile_recharge_order( ctx: &ReducerContext, user_id: &str, diff --git a/src/App.test.tsx b/src/App.test.tsx index 65056755e..c9378b342 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -161,7 +161,9 @@ describe('App title sync', () => { test('主站阶段变化会同步浏览器与宿主标题', () => { renderApp(); - expect(appTitleMock.syncAppTitle).toHaveBeenLastCalledWith('陶泥儿'); + expect(appTitleMock.syncAppTitle).toHaveBeenLastCalledWith( + '陶泥儿 Genarrative|游戏美术AI创作工具与美术Agent工作台', + ); act(() => { fireEvent.click(screen.getByRole('button', { name: '打开拼图创作' })); @@ -187,7 +189,9 @@ describe('App title sync', () => { fireEvent.click(screen.getByRole('button', { name: '退出 RPG' })); }); - expect(appTitleMock.syncAppTitle).toHaveBeenLastCalledWith('陶泥儿'); + expect(appTitleMock.syncAppTitle).toHaveBeenLastCalledWith( + '陶泥儿 Genarrative|游戏美术AI创作工具与美术Agent工作台', + ); }); test('启动时回读宿主 runtime 后刷新壳能力 UI', async () => { diff --git a/src/components/auth/PlatformAuthModalShell.test.tsx b/src/components/auth/PlatformAuthModalShell.test.tsx index 55765e8f7..41fa0aede 100644 --- a/src/components/auth/PlatformAuthModalShell.test.tsx +++ b/src/components/auth/PlatformAuthModalShell.test.tsx @@ -30,7 +30,15 @@ test('renders auth modal shell with platform theme and auth card chrome', () => expect(dialog.className).toContain('!max-w-md'); expect(within(dialog).getByText('登录表单')).toBeTruthy(); - fireEvent.click(dialog.parentElement as HTMLElement); + const backdrop = dialog.parentElement as HTMLElement; + fireEvent.pointerDown(within(dialog).getByText('登录表单')); + fireEvent.pointerUp(backdrop); + fireEvent.click(backdrop); + expect(onClose).not.toHaveBeenCalled(); + + fireEvent.pointerDown(backdrop); + fireEvent.pointerUp(backdrop); + fireEvent.click(backdrop); expect(onClose).toHaveBeenCalledTimes(1); }); diff --git a/src/components/common/PlatformMudPointWalletEntry.test.tsx b/src/components/common/PlatformMudPointWalletEntry.test.tsx new file mode 100644 index 000000000..1156a20d4 --- /dev/null +++ b/src/components/common/PlatformMudPointWalletEntry.test.tsx @@ -0,0 +1,120 @@ +/* @vitest-environment jsdom */ + +import { act, fireEvent, render, screen, within } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { expect, test, vi } from 'vitest'; + +import { PlatformMudPointWalletEntry } from './PlatformMudPointWalletEntry'; +import { formatMudPointCount } from './platformMudPointWalletModel'; + +const breakdown = { + totalPoints: 207, + permanentPoints: 100, + limitedPoints: 80, + limitedExpiresAt: '2026-07-06T16:00:00Z', + dailyFreePoints: 27, + dailyFreeResetPoints: 20, + dailyFreeResetsAt: '2026-07-12T16:00:00Z', +}; + +test('formats mud point counts consistently', () => { + expect(formatMudPointCount(12_345)).toBe('12,345'); + expect(formatMudPointCount(12_345, true)).toBe('1.2万'); +}); + +test('shows only permanent and daily free points in the shared wallet panel', async () => { + const user = userEvent.setup(); + const onRequestDetails = vi.fn(); + const onRecharge = vi.fn(); + const onOpenLedger = vi.fn(); + + render( + , + ); + + const balanceButton = screen.getByRole('button', { name: '泥点 207' }); + await user.hover(balanceButton); + + const details = screen.getByRole('dialog', { name: '泥点账户详情' }); + expect(details.className).toContain('rounded-[1.12rem]'); + expect(within(details).getByText('不限时泥点')).toBeTruthy(); + expect(within(details).getByText('按量充值、兑换码获得')).toBeTruthy(); + expect(within(details).getByText('100')).toBeTruthy(); + expect(within(details).queryByText('限时泥点')).toBeNull(); + expect(within(details).queryByText('2026-07-07 到期')).toBeNull(); + expect(within(details).getByText('每日免费泥点')).toBeTruthy(); + expect(within(details).getByText('27')).toBeTruthy(); + expect(within(details).getByText('每天重置为 20 泥点')).toBeTruthy(); + expect(onRequestDetails).not.toHaveBeenCalled(); + + await user.click(within(details).getByRole('button', { name: '使用详情' })); + expect(onOpenLedger).toHaveBeenCalledTimes(1); + + await user.click(screen.getByRole('button', { name: '充值' })); + expect(onRecharge).toHaveBeenCalledTimes(1); +}); + +test('keeps the desktop panel open while moving across the gap without click pinning', () => { + vi.useFakeTimers(); + + try { + render( + , + ); + + const balanceButton = screen.getByRole('button', { name: '泥点 207' }); + const root = balanceButton.closest('.platform-mud-point-wallet-entry'); + expect(root).toBeTruthy(); + + fireEvent.mouseEnter(balanceButton); + const details = screen.getByRole('dialog', { name: '泥点账户详情' }); + + fireEvent.mouseLeave(root as HTMLElement, { relatedTarget: null }); + fireEvent.mouseEnter(details); + act(() => vi.advanceTimersByTime(120)); + expect(screen.getByRole('dialog', { name: '泥点账户详情' })).toBeTruthy(); + + balanceButton.focus(); + fireEvent.click(balanceButton); + expect(screen.getByRole('dialog', { name: '泥点账户详情' })).toBeTruthy(); + + fireEvent.mouseLeave(root as HTMLElement, { relatedTarget: null }); + act(() => vi.advanceTimersByTime(120)); + expect(screen.queryByRole('dialog', { name: '泥点账户详情' })).toBeNull(); + } finally { + vi.useRealTimers(); + } +}); + +test('requests the balance breakdown when a compact entry opens', async () => { + const user = userEvent.setup(); + const onRequestDetails = vi.fn(); + + render( + , + ); + + await user.click(screen.getByRole('button', { name: '泥点 20' })); + expect(onRequestDetails).toHaveBeenCalledTimes(1); + expect(screen.getByText('余额明细暂不可用')).toBeTruthy(); +}); diff --git a/src/components/common/PlatformMudPointWalletEntry.tsx b/src/components/common/PlatformMudPointWalletEntry.tsx new file mode 100644 index 000000000..6f2b0f218 --- /dev/null +++ b/src/components/common/PlatformMudPointWalletEntry.tsx @@ -0,0 +1,269 @@ +import { ChevronRight, ReceiptText } from 'lucide-react'; +import { + type FocusEvent, + type MouseEvent, + useCallback, + useEffect, + useRef, + useState, +} from 'react'; + +import type { ProfileMudPointBalance } from '../../../packages/shared/src/contracts/runtime'; +import { formatMudPointCount } from './platformMudPointWalletModel'; + +const MUD_POINT_ICON_SRC = '/creation-home/topbar-wallet.png'; + +export type PlatformMudPointWalletEntryProps = { + balance: number | null; + breakdown?: ProfileMudPointBalance | null; + isLoading?: boolean; + error?: string | null; + variant?: 'desktop' | 'mobile' | 'editor'; + className?: string; + onRequestDetails: () => void; + onRecharge: () => void; + onOpenLedger: () => void; +}; + +function MudPointBalanceRow({ + label, + points, + detail, +}: { + label: string; + points: number; + detail?: string | null; +}) { + return ( +
+
+
+ {label} +
+ {detail ? ( +
+ {detail} +
+ ) : null} +
+
+ {formatMudPointCount(points)} +
+
+ ); +} + +export function PlatformMudPointWalletEntry({ + balance, + breakdown, + isLoading = false, + variant = 'desktop', + className, + onRequestDetails, + onRecharge, + onOpenLedger, +}: PlatformMudPointWalletEntryProps) { + const rootRef = useRef(null); + const isOpenRef = useRef(false); + const closeTimerRef = useRef(null); + const [isOpen, setIsOpen] = useState(false); + const isCompact = variant === 'mobile'; + const displayedBalance = breakdown?.totalPoints ?? balance; + const balanceLabel = + displayedBalance === null + ? '--' + : formatMudPointCount(displayedBalance, true); + const exactBalanceLabel = + displayedBalance === null ? '--' : formatMudPointCount(displayedBalance); + + const cancelPendingClose = useCallback(() => { + if (closeTimerRef.current !== null) { + window.clearTimeout(closeTimerRef.current); + closeTimerRef.current = null; + } + }, []); + + const closeDetails = useCallback(() => { + cancelPendingClose(); + isOpenRef.current = false; + setIsOpen(false); + }, [cancelPendingClose]); + + const requestAndOpen = useCallback(() => { + cancelPendingClose(); + if (isOpenRef.current) { + return; + } + isOpenRef.current = true; + setIsOpen(true); + if (!breakdown && !isLoading) { + onRequestDetails(); + } + }, [breakdown, cancelPendingClose, isLoading, onRequestDetails]); + + useEffect(() => cancelPendingClose, [cancelPendingClose]); + + useEffect(() => { + if (!isOpen) { + return; + } + const handlePointerDown = (event: PointerEvent) => { + if (!rootRef.current?.contains(event.target as Node)) { + closeDetails(); + } + }; + const handleKeyDown = (event: KeyboardEvent) => { + if (event.key === 'Escape') { + closeDetails(); + } + }; + document.addEventListener('pointerdown', handlePointerDown); + document.addEventListener('keydown', handleKeyDown); + return () => { + document.removeEventListener('pointerdown', handlePointerDown); + document.removeEventListener('keydown', handleKeyDown); + }; + }, [closeDetails, isOpen]); + + const closeAfterFocusLeaves = (event: FocusEvent) => { + const nextTarget = event.relatedTarget; + if ( + !(nextTarget instanceof Node) || + !event.currentTarget.contains(nextTarget) + ) { + closeDetails(); + } + }; + const closeAfterPointerLeaves = (event: MouseEvent) => { + const nextTarget = event.relatedTarget; + if ( + nextTarget instanceof Node && + event.currentTarget.contains(nextTarget) + ) { + return; + } + cancelPendingClose(); + closeTimerRef.current = window.setTimeout(() => { + closeTimerRef.current = null; + closeDetails(); + }, 120); + }; + return ( +
+
+ +
+ + {isOpen ? ( +
+
+
+ 泥点 {exactBalanceLabel} +
+ +
+ + {breakdown ? ( + <> + + + + ) : ( +
+ {isLoading ? '余额读取中' : '余额明细暂不可用'} +
+ )} + + +
+ ) : null} +
+ ); +} diff --git a/src/components/common/UnifiedModal.test.tsx b/src/components/common/UnifiedModal.test.tsx index 7e1637764..adbca4290 100644 --- a/src/components/common/UnifiedModal.test.tsx +++ b/src/components/common/UnifiedModal.test.tsx @@ -39,6 +39,24 @@ test('closes through backdrop and escape', () => { expect(onClose).toHaveBeenCalledTimes(2); }); +test('keeps the modal open when a pointer press starts inside and releases over the backdrop', () => { + const onClose = vi.fn(); + render( + + + , + ); + + const dialog = screen.getByRole('dialog'); + const backdrop = dialog.parentElement as HTMLElement; + + fireEvent.pointerDown(screen.getByRole('button', { name: '窗口内容' })); + fireEvent.pointerUp(backdrop); + fireEvent.click(backdrop); + + expect(onClose).not.toHaveBeenCalled(); +}); + test('supports disabling escape close while keeping the custom close button chrome', () => { const onClose = vi.fn(); render( diff --git a/src/components/common/UnifiedModal.tsx b/src/components/common/UnifiedModal.tsx index 6129beab1..93b63f894 100644 --- a/src/components/common/UnifiedModal.tsx +++ b/src/components/common/UnifiedModal.tsx @@ -4,6 +4,7 @@ import { type ReactNode, useEffect, useId, + useRef, } from 'react'; import { createPortal } from 'react-dom'; @@ -118,6 +119,7 @@ function UnifiedModalContent({ const generatedTitleId = useId(); const descriptionId = useId(); const titleId = titleIdProp ?? generatedTitleId; + const backdropPointerSequenceRef = useRef(null); useEffect(() => { if (!open || closeDisabled || !closeOnEscape) { @@ -175,10 +177,26 @@ function UnifiedModalContent({
{ + backdropPointerSequenceRef.current = + event.target === event.currentTarget; + }} + onPointerUpCapture={(event) => { + backdropPointerSequenceRef.current = + backdropPointerSequenceRef.current === true && + event.target === event.currentTarget; + }} + onPointerCancelCapture={() => { + backdropPointerSequenceRef.current = false; + }} onClick={(event) => { + const pointerSequenceStayedOnBackdrop = + backdropPointerSequenceRef.current !== false; + backdropPointerSequenceRef.current = null; if ( closeOnBackdrop && !closeDisabled && + pointerSequenceStayedOnBackdrop && event.target === event.currentTarget ) { onClose(); diff --git a/src/components/common/platformMudPointWalletModel.ts b/src/components/common/platformMudPointWalletModel.ts new file mode 100644 index 000000000..0236383c0 --- /dev/null +++ b/src/components/common/platformMudPointWalletModel.ts @@ -0,0 +1,10 @@ +export function formatMudPointCount(value: number, compact = false) { + const normalizedValue = Math.max(0, Math.round(value)); + if (compact && normalizedValue >= 100_000_000) { + return `${(normalizedValue / 100_000_000).toFixed(1)}亿`; + } + if (compact && normalizedValue >= 10_000) { + return `${(normalizedValue / 10_000).toFixed(1)}万`; + } + return normalizedValue.toLocaleString('zh-CN'); +} diff --git a/src/components/creation-home/CreationLandingView.test.tsx b/src/components/creation-home/CreationLandingView.test.tsx index 970e66e09..1fa684dad 100644 --- a/src/components/creation-home/CreationLandingView.test.tsx +++ b/src/components/creation-home/CreationLandingView.test.tsx @@ -156,16 +156,34 @@ describe('CreationLandingView', () => { renderCreationLanding(); expect(screen.getByRole('main', { name: '陶泥儿创作主页' })).toBeTruthy(); + expect(screen.getAllByRole('heading', { level: 1 })).toHaveLength(1); expect( screen.getByRole('heading', { - name: '陶泥儿 - 开启全民精品游戏创作', + level: 1, + name: '陶泥儿 · 开启全民精品游戏创作', }), ).toBeTruthy(); expect( - screen.getByText('登录即送100泥点,可以免费制作50个素材'), + screen.getByText('陶泥儿 Genarrative|游戏美术 AI 创作工具'), + ).toBeTruthy(); + const subtitle = screen.getByText( + /面向个人创作者的游戏美术 AI 工作台/u, + ); + expect(subtitle.textContent).toContain('美术 Agent'); + expect(subtitle.textContent).toContain('无限画布'); + expect(subtitle.textContent).toContain('角色、场景、UI 与宣发素材'); + expect( + screen.getByText('登录即送 100 泥点,可以免费制作 50 个素材'), + ).toBeTruthy(); + expect( + screen.getByRole('heading', { + level: 2, + name: '游戏美术 AI 创作工具', + }), + ).toBeTruthy(); + expect( + screen.getByRole('heading', { level: 3, name: '游戏视觉规范' }), ).toBeTruthy(); - expect(screen.getByRole('heading', { name: '创作工具' })).toBeTruthy(); - expect(screen.getByText('游戏视觉规范')).toBeTruthy(); expect(screen.getByRole('heading', { name: '陶泥儿精选' })).toBeTruthy(); expect(screen.getByRole('tab', { name: '全部' })).toBeTruthy(); expect(screen.queryByRole('tab', { name: '素材包' })).toBeNull(); @@ -310,6 +328,10 @@ describe('CreationLandingView', () => { await user.click(screen.getByRole('button', { name: /游戏特效/u })); const dialog = screen.getByRole('dialog', { name: '抱歉' }); + expect(screen.getAllByRole('heading', { level: 1 })).toHaveLength(1); + expect( + within(dialog).getByRole('heading', { level: 2, name: '抱歉' }), + ).toBeTruthy(); expect(dialog.textContent).toContain('功能还在调试中'); expect(dialog.textContent).toContain('暂未开放'); expect(dialog.closest('.platform-theme--light')).toBeTruthy(); diff --git a/src/components/creation-home/CreationLandingView.tsx b/src/components/creation-home/CreationLandingView.tsx index d4c989cdb..c965d55df 100644 --- a/src/components/creation-home/CreationLandingView.tsx +++ b/src/components/creation-home/CreationLandingView.tsx @@ -858,9 +858,20 @@ export function CreationLandingView({
- 陶泥儿创作工具 -

陶泥儿 - 开启全民精品游戏创作

-

登录即送100泥点,可以免费制作50个素材

+ + 陶泥儿 Genarrative|游戏美术 AI 创作工具 + +

陶泥儿 · 开启全民精品游戏创作

+
+

+ 面向个人创作者的游戏美术 AI 工作台。 +
+ 用美术 Agent 与无限画布,快速制作角色、场景、UI 与宣发素材。 +

+

+ 登录即送 100 泥点,可以免费制作 50 个素材 +

+
-

创作工具

+

游戏美术 AI 创作工具

@@ -1084,7 +1095,7 @@ export function CreationLandingView({ alt="" className="platform-mobile-home-welcome-dialog__icon" /> -

抱歉

+

抱歉

功能还在调试中
diff --git a/src/components/image-editor/ImageCanvasEditorShellView.test.tsx b/src/components/image-editor/ImageCanvasEditorShellView.test.tsx index bbfb494b6..48feebceb 100644 --- a/src/components/image-editor/ImageCanvasEditorShellView.test.tsx +++ b/src/components/image-editor/ImageCanvasEditorShellView.test.tsx @@ -99,8 +99,11 @@ function createTopbarProps(): ImageCanvasTopbarViewProps { isProjectRenameSaving: false, projectRenameError: null, layers: [], - walletBalanceLabel: '0泥点', + walletBalance: 0, + walletBreakdown: null, isWalletBalanceLoading: false, + isWalletDetailsLoading: false, + walletDetailsError: null, currentUser: null, assetExportStatus: null, isExportingAssets: false, @@ -111,7 +114,9 @@ function createTopbarProps(): ImageCanvasTopbarViewProps { resetProjectRenameError: vi.fn(), exportCanvasAssets: vi.fn(), onOpenShortcuts: vi.fn(), - onOpenWallet: vi.fn(), + onRequestWalletDetails: vi.fn(), + onRecharge: vi.fn(), + onOpenWalletLedger: vi.fn(), onOpenAccount: vi.fn(), }; } diff --git a/src/components/image-editor/ImageCanvasEditorView.test.tsx b/src/components/image-editor/ImageCanvasEditorView.test.tsx index 34cf5cd9f..ed9cf0dda 100644 --- a/src/components/image-editor/ImageCanvasEditorView.test.tsx +++ b/src/components/image-editor/ImageCanvasEditorView.test.tsx @@ -12,7 +12,6 @@ import userEvent from '@testing-library/user-event'; import JSZip from 'jszip'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import type { EditorAgentConversationClient } from './useEditorAgentConversation'; import { ApiClientError, AuthUiContext, @@ -25,6 +24,7 @@ import { readZipText, setupImageCanvasEditorViewTestLifecycle, } from './ImageCanvasEditorView.test-utils'; +import type { EditorAgentConversationClient } from './useEditorAgentConversation'; type EditorAgentListConversations = EditorAgentConversationClient['listConversations']; @@ -110,6 +110,8 @@ const renameEditorProjectMock = vi.hoisted(() => vi.fn()); const saveEditorProjectLayoutMock = vi.hoisted(() => vi.fn()); const getPlatformProfileDashboardMock = vi.hoisted(() => vi.fn()); const loadFrontendRuntimeConfigMock = vi.hoisted(() => vi.fn()); +const getRpgProfileRechargeCenterMock = vi.hoisted(() => vi.fn()); +const getRpgProfileWalletLedgerMock = vi.hoisted(() => vi.fn()); vi.mock('../../services/image-editor/editorProjectClient', async () => { const actual = await vi.importActual< @@ -140,6 +142,17 @@ vi.mock('../../services/platform-entry/platformProfileClient', () => ({ getPlatformProfileDashboard: getPlatformProfileDashboardMock, })); +vi.mock('../../services/rpg-entry/rpgProfileClient', async () => { + const actual = await vi.importActual< + typeof import('../../services/rpg-entry/rpgProfileClient') + >('../../services/rpg-entry/rpgProfileClient'); + return { + ...actual, + getRpgProfileRechargeCenter: getRpgProfileRechargeCenterMock, + getRpgProfileWalletLedger: getRpgProfileWalletLedgerMock, + }; +}); + vi.mock('../../services/frontendRuntimeConfigService', () => ({ loadFrontendRuntimeConfig: loadFrontendRuntimeConfigMock, })); @@ -276,6 +289,46 @@ describe('ImageCanvasEditorView', () => { playedWorldCount: 0, updatedAt: null, }); + getRpgProfileRechargeCenterMock.mockResolvedValue({ + walletBalance: 1234, + mudPointBalance: { + totalPoints: 1234, + permanentPoints: 1000, + limitedPoints: 214, + limitedExpiresAt: '2026-07-31T16:00:00Z', + dailyFreePoints: 20, + dailyFreeResetPoints: 20, + dailyFreeResetsAt: '2026-07-12T16:00:00Z', + }, + membership: { + status: 'normal', + tier: 'normal', + startedAt: null, + expiresAt: null, + updatedAt: null, + cycleStartedAt: null, + cycleResetsAt: null, + cycleGrantedPoints: 0, + cycleRemainingPoints: 0, + cyclePeriodDays: 30, + }, + pointProducts: [], + membershipProducts: [], + benefits: [], + latestOrder: null, + hasPointsRecharged: false, + }); + getRpgProfileWalletLedgerMock.mockResolvedValue({ + entries: [ + { + id: 'editor-ledger-1', + amountDelta: -5, + balanceAfter: 1234, + sourceType: 'asset_operation_consume', + createdAt: '2026-07-12T08:00:00Z', + }, + ], + }); }); afterEach(() => { @@ -285,6 +338,8 @@ describe('ImageCanvasEditorView', () => { deleteEditorAgentConversationMock.mockReset(); streamEditorAgentMessageMock.mockReset(); getPlatformProfileDashboardMock.mockReset(); + getRpgProfileRechargeCenterMock.mockReset(); + getRpgProfileWalletLedgerMock.mockReset(); loadFrontendRuntimeConfigMock.mockReset(); }); @@ -495,7 +550,7 @@ describe('ImageCanvasEditorView', () => { , ); - expect(await screen.findByLabelText('泥点余额 1,234泥点')).toBeTruthy(); + expect(await screen.findByLabelText('泥点 1,234')).toBeTruthy(); expect(getPlatformProfileDashboardMock).toHaveBeenCalledWith({ authImpact: 'local', skipRefresh: true, @@ -540,7 +595,7 @@ describe('ImageCanvasEditorView', () => { expect(openAccountModal).toHaveBeenCalledTimes(1); }); - it('opens the account recharge entry from the canvas topbar mud point button', async () => { + it('opens the shared wallet breakdown and ledger from the canvas topbar', async () => { render( { ); const walletButton = await screen.findByRole('button', { - name: '泥点余额 1,234泥点', + name: '泥点 1,234', }); fireEvent.click(walletButton); + const details = await screen.findByRole('dialog', { + name: '泥点账户详情', + }); + expect(within(details).getByText('不限时泥点')).toBeTruthy(); + expect(within(details).queryByText('限时泥点')).toBeNull(); + expect(within(details).getByText('每日免费泥点')).toBeTruthy(); + + fireEvent.click(within(details).getByRole('button', { name: '使用详情' })); expect( - await screen.findByRole('dialog', { name: '账户充值' }), + await screen.findByRole('dialog', { name: '泥点账单' }), ).toBeTruthy(); - expect(screen.queryByPlaceholderText('输入兑换码')).toBeNull(); + expect(getRpgProfileWalletLedgerMock).toHaveBeenCalledTimes(1); }); it('suspends canvas interaction during account payment dialogs and restores completed quick edit selections', async () => { @@ -662,11 +725,11 @@ describe('ImageCanvasEditorView', () => { const viewport = screen.getByLabelText('画布工作区'); fireEvent.click( await screen.findByRole('button', { - name: '泥点余额 1,234泥点', + name: '充值', }), ); expect( - await screen.findByRole('dialog', { name: '账户充值' }), + await screen.findByRole('dialog', { name: '购买更多泥点' }), ).toBeTruthy(); fireEvent.keyDown(window, { key: 'Delete', code: 'Delete' }); @@ -692,7 +755,7 @@ describe('ImageCanvasEditorView', () => { ).toBe('true'); expect(viewport.getAttribute('aria-disabled')).toBe('true'); - fireEvent.click(screen.getByRole('button', { name: '关闭账户充值' })); + fireEvent.click(screen.getByRole('button', { name: '关闭购买更多泥点' })); const resumedToolbar = await screen.findByRole('toolbar', { name: '快速编辑框选工具', diff --git a/src/components/image-editor/ImageCanvasEditorView.tsx b/src/components/image-editor/ImageCanvasEditorView.tsx index 015198250..57e8b93a6 100644 --- a/src/components/image-editor/ImageCanvasEditorView.tsx +++ b/src/components/image-editor/ImageCanvasEditorView.tsx @@ -9,29 +9,29 @@ import { useState, } from 'react'; -import type { ExternalGenerationTaskRecord } from '../../../packages/shared/src/contracts/externalGeneration'; import type { EditorAgentGenerationResultEvent } from '../../../packages/shared/src/contracts/editorAgent'; +import type { ExternalGenerationTaskRecord } from '../../../packages/shared/src/contracts/externalGeneration'; +import { loadFrontendRuntimeConfig } from '../../services/frontendRuntimeConfigService'; import { createEditorAsset, createEditorProjectResource, type EditorAssetSnapshot, type EditorProjectSnapshot, - loadEditorProject, loadEditorGenerationPricing, + loadEditorProject, } from '../../services/image-editor/editorProjectClient'; -import { loadFrontendRuntimeConfig } from '../../services/frontendRuntimeConfigService'; import { shouldShowRechargeEntry } from '../../services/payment/paymentPlatform'; import { getPlatformProfileDashboard } from '../../services/platform-entry/platformProfileClient'; import { useAuthUi } from '../auth/AuthUiContext'; import { PlatformDangerConfirmDialog } from '../common/PlatformDangerConfirmDialog'; import { PlatformProfileRechargeModal } from '../platform-entry/PlatformProfileRechargeModal'; import { PlatformProfileRewardCodeRedeemModal } from '../platform-entry/PlatformProfileRewardCodeRedeemModal'; +import { PlatformProfileWalletLedgerModal } from '../platform-entry/PlatformProfileWalletLedgerModal'; import { PlatformRechargePaymentConfirmationMask, PlatformRechargePaymentResultDialog, } from '../platform-entry/PlatformRechargePaymentStatusDialogs'; import { usePlatformProfileCenterController } from '../platform-entry/usePlatformProfileCenterController'; -import { formatDashboardCount } from '../rpg-entry/rpgEntryProfileDashboardPresentation'; import { canvasAssetKindOrNull, DEFAULT_CANVAS_BACKGROUND_COLOR, @@ -79,11 +79,11 @@ import { } from './useImageCanvasAssetCanvasBridge'; import { useImageCanvasAssetExportWorkflow } from './useImageCanvasAssetExportWorkflow'; import { useImageCanvasAssetLibrary } from './useImageCanvasAssetLibrary'; +import { useImageCanvasContextStore } from './useImageCanvasContextStore.ts'; import { useImageCanvasEditorChrome } from './useImageCanvasEditorChrome'; import { useImageCanvasGenerationSurface } from './useImageCanvasGenerationSurface'; import { useImageCanvasKeyboardShortcuts } from './useImageCanvasKeyboardShortcuts'; import { useImageCanvasLayerCommands } from './useImageCanvasLayerCommands'; -import { useImageCanvasContextStore } from './useImageCanvasContextStore.ts'; import { useImageCanvasProjectPersistence } from './useImageCanvasProjectPersistence'; import { useImageCanvasStageController } from './useImageCanvasStageController'; import { useImageCanvasStageInteractions } from './useImageCanvasStageInteractions'; @@ -295,9 +295,7 @@ export function ImageCanvasEditorView({ }: ImageCanvasEditorViewProps = {}) { const authUi = useAuthUi(); const [, setGenerationPricingVersion] = useState(0); - const [walletBalanceLabel, setWalletBalanceLabel] = useState( - null, - ); + const [walletBalance, setWalletBalance] = useState(null); const [isWalletBalanceLoading, setIsWalletBalanceLoading] = useState(false); const editorRootRef = useRef(null); const canvasViewportRef = useRef(null); @@ -479,37 +477,40 @@ export function ImageCanvasEditorView({ clearAuthOnUnauthorized: false, }) .then((dashboard) => { - setWalletBalanceLabel( - `${formatDashboardCount(dashboard.walletBalance)}泥点`, - ); + setWalletBalance(dashboard.walletBalance); }) .catch(() => undefined); }, []); const { - activeRechargeTab, buyRechargeProduct, closeNativeWechatPayment, confirmNativeWechatPayment, isLoadingRechargeCenter, + isLoadingWalletLedger, isRechargeOpen, isRewardCodeOpen, isSubmittingRewardCode, + isWalletLedgerOpen, loadRechargeCenter, + loadWalletLedger, nativeWechatPayment, openRechargeOrRewardCodeModal, + openWalletLedgerPanel, rechargeCenter, rechargeError, rechargePaymentResult, rewardCodeError, rewardCodeInput, rewardCodeSuccess, - setActiveRechargeTab, setIsRechargeOpen, setIsRewardCodeOpen, + setIsWalletLedgerOpen, setRechargePaymentResult, setRewardCodeInput, submittingRechargeProductId, submitRewardCode, + walletLedger, + walletLedgerError, wechatRechargeOrderConfirmationState, } = usePlatformProfileCenterController({ activeTab: 'editor-canvas', @@ -522,6 +523,7 @@ export function ImageCanvasEditorView({ const isAccountPaymentModalOpen = isRewardCodeOpen || isRechargeOpen || + isWalletLedgerOpen || Boolean(nativeWechatPayment) || Boolean(rechargePaymentResult) || Boolean(wechatRechargeOrderConfirmationState); @@ -541,7 +543,7 @@ export function ImageCanvasEditorView({ }, [authUi]); useEffect(() => { if (!authUi?.canAccessProtectedData || !authUi.user?.id) { - setWalletBalanceLabel(null); + setWalletBalance(null); setIsWalletBalanceLoading(false); return; } @@ -563,15 +565,13 @@ export function ImageCanvasEditorView({ if (!isMounted || currentRequestId !== requestId) { return; } - setWalletBalanceLabel( - `${formatDashboardCount(dashboard.walletBalance)}泥点`, - ); + setWalletBalance(dashboard.walletBalance); }) .catch(() => { if (!isMounted || currentRequestId !== requestId) { return; } - setWalletBalanceLabel(null); + setWalletBalance(null); }) .finally(() => { if (!isMounted || currentRequestId !== requestId) { @@ -2029,8 +2029,11 @@ export function ImageCanvasEditorView({ isProjectRenameSaving, projectRenameError, layers, - walletBalanceLabel, + walletBalance, + walletBreakdown: rechargeCenter?.mudPointBalance ?? null, isWalletBalanceLoading, + isWalletDetailsLoading: isLoadingRechargeCenter, + walletDetailsError: rechargeError, currentUser: authUi?.user, assetExportStatus, isExportingAssets, @@ -2041,7 +2044,9 @@ export function ImageCanvasEditorView({ resetProjectRenameError, exportCanvasAssets, onOpenShortcuts: () => setIsShortcutDialogOpen(true), - onOpenWallet: openAccountPaymentModal, + onRequestWalletDetails: loadRechargeCenter, + onRecharge: openAccountPaymentModal, + onOpenWalletLedger: openWalletLedgerPanel, onOpenAccount: () => { if (authUi?.user) { authUi.openAccountModal(); @@ -2231,8 +2236,6 @@ export function ImageCanvasEditorView({ error={rechargeError} submittingProductId={submittingRechargeProductId} nativePayment={nativeWechatPayment} - activeTab={activeRechargeTab} - onTabChange={setActiveRechargeTab} onClose={() => setIsRechargeOpen(false)} onRetry={loadRechargeCenter} onBuy={buyRechargeProduct} @@ -2240,6 +2243,16 @@ export function ImageCanvasEditorView({ onCloseNativePayment={closeNativeWechatPayment} /> ) : null} + {isWalletLedgerOpen ? ( + setIsWalletLedgerOpen(false)} + onRetry={loadWalletLedger} + /> + ) : null} {rechargePaymentResult ? ( { const sourceLayer = createSourceLayer({ width: 320, height: 240, - originalWidth: 1024, - originalHeight: 768, + originalWidth: 1537, + originalHeight: 1025, }); const layer = applyImageEditResultToSourceLayer({ generated: createGenerated({ @@ -313,10 +313,10 @@ describe('ImageCanvasGenerationLayerModel', () => { id: 'layer-source', title: '源图', src: 'data:image/png;base64,edited', - width: 1536, - height: 1024, - originalWidth: 1536, - originalHeight: 1024, + width: 320, + height: 240, + originalWidth: 1537, + originalHeight: 1025, resourceId: 'resource-edited', sourceResourceId: 'resource-source', objectKey: 'generated/edited.png', diff --git a/src/components/image-editor/ImageCanvasGenerationLayerModel.ts b/src/components/image-editor/ImageCanvasGenerationLayerModel.ts index d13b56521..b88025f29 100644 --- a/src/components/image-editor/ImageCanvasGenerationLayerModel.ts +++ b/src/components/image-editor/ImageCanvasGenerationLayerModel.ts @@ -247,18 +247,10 @@ export function applyImageEditResultToSourceLayer({ sourceLayer: CanvasLayer; generationInputs: CanvasGenerationInputs; }): CanvasLayer { - const originalWidth = - generated.width || sourceLayer.originalWidth || sourceLayer.width; - const originalHeight = - generated.height || sourceLayer.originalHeight || sourceLayer.height; - const { width, height } = resolveLayerResolutionSize( - originalWidth, - originalHeight, - { - width: sourceLayer.width, - height: sourceLayer.height, - }, - ); + const originalWidth = sourceLayer.originalWidth || sourceLayer.width; + const originalHeight = sourceLayer.originalHeight || sourceLayer.height; + const width = sourceLayer.width; + const height = sourceLayer.height; return applyGeneratedMetadata( { ...sourceLayer, diff --git a/src/components/image-editor/ImageCanvasGenerationSubmissionModel.test.ts b/src/components/image-editor/ImageCanvasGenerationSubmissionModel.test.ts index 85c6078e4..fc221c4c4 100644 --- a/src/components/image-editor/ImageCanvasGenerationSubmissionModel.test.ts +++ b/src/components/image-editor/ImageCanvasGenerationSubmissionModel.test.ts @@ -122,6 +122,8 @@ describe('ImageCanvasGenerationSubmissionModel', () => { const sourceLayer = createLayer({ objectKey: 'generated-character-drafts/editor/source.png', assetKind: 'character', + originalWidth: 1537, + originalHeight: 1025, }); const plan = buildImageGenerationSubmissionPlan({ @@ -150,7 +152,7 @@ describe('ImageCanvasGenerationSubmissionModel', () => { normalizedPrompt: '把当前图改成雨天', sourceLayer, editInput: { - size: '1024x768', + size: '1537x1025', model: IMAGE_MODEL_GPT_IMAGE_2, }, result: { diff --git a/src/components/image-editor/ImageCanvasGenerationSubmissionModel.ts b/src/components/image-editor/ImageCanvasGenerationSubmissionModel.ts index 6f2d1c139..7552c7837 100644 --- a/src/components/image-editor/ImageCanvasGenerationSubmissionModel.ts +++ b/src/components/image-editor/ImageCanvasGenerationSubmissionModel.ts @@ -41,11 +41,8 @@ import { DEFAULT_VIDEO_WEB_SEARCH_ENABLED, ICON_DESCRIPTION_LIMIT, IMAGE_MODEL_GPT_IMAGE_2, - inferEditorImageAspectRatio, - inferEditorImageSizeLabel, normalizeEditorImageModel, resolveCharacterAnimationSourceImageSrc, - resolveEditorImageGenerationPixelSize, SEEDANCE_VIDEO_REFERENCE_LIMITS, SPEC_GENERATION_ASPECT_RATIO, SPEC_GENERATION_IMAGE_SIZE, @@ -260,29 +257,12 @@ export function buildImageGenerationSubmissionPlan({ const basePrompt = dialog.prompt.trim() || '快速编辑图片'; const normalizedQuickEditPrompt = basePrompt; const imageModel = IMAGE_MODEL_GPT_IMAGE_2; - const aspectRatio = - dialog.aspectRatio ?? - inferEditorImageAspectRatio( - sourceLayer.originalWidth, - sourceLayer.originalHeight, - ); - const imageSize = - dialog.imageSize ?? - inferEditorImageSizeLabel( - sourceLayer.originalWidth, - sourceLayer.originalHeight, - ); - const outputSize = resolveEditorImageGenerationPixelSize({ - model: imageModel, - aspectRatio, - imageSize, - }); return { kind: 'quick-edit', normalizedPrompt: normalizedQuickEditPrompt, sourceLayer, editInput: { - size: `${outputSize.width}x${outputSize.height}`, + size: `${sourceLayer.originalWidth}x${sourceLayer.originalHeight}`, model: imageModel, }, result: { diff --git a/src/components/image-editor/ImageCanvasTopbarView.test.tsx b/src/components/image-editor/ImageCanvasTopbarView.test.tsx index ba0c69689..1bbf80ec6 100644 --- a/src/components/image-editor/ImageCanvasTopbarView.test.tsx +++ b/src/components/image-editor/ImageCanvasTopbarView.test.tsx @@ -1,12 +1,23 @@ /* @vitest-environment jsdom */ -import { fireEvent, render, screen } from '@testing-library/react'; +import { fireEvent, render, screen, within } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { describe, expect, it, vi } from 'vitest'; import type { CanvasLayer } from './ImageCanvasEditorTypes'; import { ImageCanvasTopbarView } from './ImageCanvasTopbarView'; import { useImageCanvasContextStore } from './useImageCanvasContextStore.ts'; +const walletBreakdown = { + totalPoints: 1_234, + permanentPoints: 1_000, + limitedPoints: 214, + limitedExpiresAt: '2026-07-31T16:00:00Z', + dailyFreePoints: 20, + dailyFreeResetPoints: 20, + dailyFreeResetsAt: '2026-07-12T16:00:00Z', +}; + function createLayer(overrides: Partial = {}): CanvasLayer { const id = overrides.id ?? 'layer-a'; return { @@ -27,9 +38,7 @@ function createLayer(overrides: Partial = {}): CanvasLayer { } function renderTopbar( - overrides: Partial< - Parameters[0] - > = {}, + overrides: Partial[0]> = {}, ) { useImageCanvasContextStore.getState().setProjectId('project-a'); const props: Parameters[0] = { @@ -39,8 +48,11 @@ function renderTopbar( isProjectRenameSaving: false, projectRenameError: null, layers: [], - walletBalanceLabel: '1,234泥点', + walletBalance: 1_234, + walletBreakdown, isWalletBalanceLoading: false, + isWalletDetailsLoading: false, + walletDetailsError: null, currentUser: null, assetExportStatus: null, isExportingAssets: false, @@ -51,7 +63,9 @@ function renderTopbar( resetProjectRenameError: vi.fn(), exportCanvasAssets: vi.fn(), onOpenShortcuts: vi.fn(), - onOpenWallet: vi.fn(), + onRequestWalletDetails: vi.fn(), + onRecharge: vi.fn(), + onOpenWalletLedger: vi.fn(), onOpenAccount: vi.fn(), ...overrides, }; @@ -80,22 +94,36 @@ describe('ImageCanvasTopbarView', () => { expect(props.onOpenShortcuts).toHaveBeenCalledTimes(1); }); - it('shows the current mud point balance in the topbar', () => { + it('shows the shared mud point breakdown and wallet actions', async () => { + const user = userEvent.setup(); const props = renderTopbar({ - walletBalanceLabel: '1.2万泥点', + walletBalance: 12_345, + walletBreakdown: { + ...walletBreakdown, + totalPoints: 12_345, + }, }); const walletChip = screen.getByRole('button', { - name: '泥点余额 1.2万泥点', + name: '泥点 12,345', }); - expect(walletChip.textContent).toBe('1.2万泥点'); + expect(walletChip.textContent).toBe('泥点 1.2万'); expect(walletChip.querySelector('img')?.getAttribute('src')).toBe( '/creation-home/topbar-wallet.png', ); - fireEvent.click(walletChip); + await user.hover(walletChip); + const details = screen.getByRole('dialog', { name: '泥点账户详情' }); + expect(within(details).getByText('不限时泥点')).toBeTruthy(); + expect(within(details).queryByText('限时泥点')).toBeNull(); + expect(within(details).getByText('每日免费泥点')).toBeTruthy(); - expect(props.onOpenWallet).toHaveBeenCalledTimes(1); + await user.click(within(details).getByRole('button', { name: '使用详情' })); + expect(props.onOpenWalletLedger).toHaveBeenCalledTimes(1); + + await user.click(screen.getByRole('button', { name: '充值' })); + expect(props.onRecharge).toHaveBeenCalledTimes(1); + expect(props.onRequestWalletDetails).not.toHaveBeenCalled(); }); it('shows the current user avatar beside the mud point balance', () => { @@ -151,8 +179,11 @@ describe('ImageCanvasTopbarView', () => { isProjectRenameSaving={false} projectRenameError={null} layers={[]} - walletBalanceLabel="0泥点" + walletBalance={0} + walletBreakdown={null} isWalletBalanceLoading={false} + isWalletDetailsLoading={false} + walletDetailsError={null} currentUser={null} assetExportStatus={null} isExportingAssets={false} @@ -163,14 +194,19 @@ describe('ImageCanvasTopbarView', () => { resetProjectRenameError={vi.fn()} exportCanvasAssets={exportCanvasAssets} onOpenShortcuts={vi.fn()} - onOpenWallet={vi.fn()} + onRequestWalletDetails={vi.fn()} + onRecharge={vi.fn()} + onOpenWalletLedger={vi.fn()} onOpenAccount={vi.fn()} />, ); expect( - (screen.getByRole('button', { name: '下载画布素材' }) as HTMLButtonElement) - .disabled, + ( + screen.getByRole('button', { + name: '下载画布素材', + }) as HTMLButtonElement + ).disabled, ).toBe(true); rerender( @@ -181,8 +217,11 @@ describe('ImageCanvasTopbarView', () => { isProjectRenameSaving={false} projectRenameError={null} layers={[createLayer()]} - walletBalanceLabel="0泥点" + walletBalance={0} + walletBreakdown={null} isWalletBalanceLoading={false} + isWalletDetailsLoading={false} + walletDetailsError={null} currentUser={null} assetExportStatus={{ tone: 'success', @@ -196,7 +235,9 @@ describe('ImageCanvasTopbarView', () => { resetProjectRenameError={vi.fn()} exportCanvasAssets={exportCanvasAssets} onOpenShortcuts={vi.fn()} - onOpenWallet={vi.fn()} + onRequestWalletDetails={vi.fn()} + onRecharge={vi.fn()} + onOpenWalletLedger={vi.fn()} onOpenAccount={vi.fn()} />, ); diff --git a/src/components/image-editor/ImageCanvasTopbarView.tsx b/src/components/image-editor/ImageCanvasTopbarView.tsx index d1698c9ce..72fa0e4ab 100644 --- a/src/components/image-editor/ImageCanvasTopbarView.tsx +++ b/src/components/image-editor/ImageCanvasTopbarView.tsx @@ -7,6 +7,8 @@ import { X, } from 'lucide-react'; +import type { ProfileMudPointBalance } from '../../../packages/shared/src/contracts/runtime'; +import { PlatformMudPointWalletEntry } from '../common/PlatformMudPointWalletEntry'; import { PlatformStatusMessage } from '../common/PlatformStatusMessage'; import { PlatformTextField } from '../common/PlatformTextField'; import { EditorIconButton } from './ImageCanvasEditorPrimitives'; @@ -21,8 +23,11 @@ export type ImageCanvasTopbarViewProps = { isProjectRenameSaving: boolean; projectRenameError: string | null; layers: CanvasLayer[]; - walletBalanceLabel: string | null; + walletBalance: number | null; + walletBreakdown?: ProfileMudPointBalance | null; isWalletBalanceLoading: boolean; + isWalletDetailsLoading: boolean; + walletDetailsError: string | null; currentUser: | { id: string; @@ -41,13 +46,13 @@ export type ImageCanvasTopbarViewProps = { resetProjectRenameError: () => void; exportCanvasAssets: () => void | Promise; onOpenShortcuts: () => void; - onOpenWallet: () => void; + onRequestWalletDetails: () => void; + onRecharge: () => void; + onOpenWalletLedger: () => void; onOpenAccount: () => void; }; -function buildCanvasUserCode( - user: ImageCanvasTopbarViewProps['currentUser'], -) { +function buildCanvasUserCode(user: ImageCanvasTopbarViewProps['currentUser']) { if (user?.publicUserCode?.trim()) { return user.publicUserCode.trim(); } @@ -63,8 +68,11 @@ export function ImageCanvasTopbarView({ isProjectRenameSaving, projectRenameError, layers, - walletBalanceLabel, + walletBalance, + walletBreakdown, isWalletBalanceLoading, + isWalletDetailsLoading, + walletDetailsError, currentUser, assetExportStatus, isExportingAssets, @@ -75,17 +83,15 @@ export function ImageCanvasTopbarView({ resetProjectRenameError, exportCanvasAssets, onOpenShortcuts, - onOpenWallet, + onRequestWalletDetails, + onRecharge, + onOpenWalletLedger, onOpenAccount, }: ImageCanvasTopbarViewProps) { const projectId = useImageCanvasContextStore((state) => state.projectId); const hasExportableLayer = layers.some( (layer) => layer.src.trim().length > 0, ); - const walletDisplayLabel = walletBalanceLabel ?? '--泥点'; - const walletAriaLabel = walletBalanceLabel - ? `泥点余额 ${walletBalanceLabel}` - : '泥点余额读取中'; const userDisplayName = currentUser?.displayName?.trim() || '登录'; const userAvatarUrl = currentUser?.avatarUrl?.trim() || null; const userAvatarLabel = userDisplayName.slice(0, 1).toUpperCase(); @@ -198,22 +204,16 @@ export function ImageCanvasTopbarView({ {assetExportStatus.message} ) : null} - +

- -
- - @@ -4578,6 +4437,8 @@ export function RpgEntryHomeView({ const desktopHomeContent: ReactNode = (
+ + {platformError ? ( setIsRechargeOpen(false)} onRetry={loadRechargeCenter} onBuy={buyRechargeProduct} @@ -5041,10 +4900,16 @@ export function RpgEntryHomeView({ const desktopTopbarActions: ReactNode = (
{isAuthenticated ? ( - ) : null}
+ {isMobileRecommendTab && isAuthenticated ? ( +
+ +
+ ) : null} {rechargePaymentConfirmationMask} ); @@ -5307,19 +5182,6 @@ export function RpgEntryHomeView({ {apiKeysModal} {rechargePaymentResultModal} {categoryFilterDialog} - {isTaskCenterOpen ? ( - setIsTaskCenterOpen(false)} - onRetry={loadTaskCenter} - onClaim={claimTaskReward} - /> - ) : null} {profilePopupPanel ? ( { }); expect( - buildWalletLedgerPresentation( - { entries: [incomeEntry, outcomeEntry] }, - 12, - ), + buildWalletLedgerPresentation({ entries: [incomeEntry, outcomeEntry] }, 12), ).toEqual({ balance: 80, balanceLabel: '80泥点', @@ -137,9 +134,20 @@ test('profile funds ViewModel builds wallet ledger presentation', () => { test('profile funds ViewModel formats recharge product and membership labels', () => { expect(formatRechargePrice(600)).toBe('¥6'); expect(formatRechargePrice(650)).toBe('¥6.50'); - expect(buildRechargeProductValueLabel(buildRechargeProduct())).toBe( - '60+60泥点', - ); + expect(buildRechargeProductValueLabel(buildRechargeProduct())).toBe('60泥点'); + expect( + buildRechargeProductValueLabel( + buildRechargeProduct({ + productId: 'points_180', + title: '180泥点', + priceCents: 1800, + pointsAmount: 180, + bonusPoints: 90, + badgeLabel: '首充加赠', + description: '首充加赠90泥点', + }), + ), + ).toBe('180+90泥点'); expect( buildRechargeProductValueLabel( buildRechargeProduct({ @@ -205,9 +213,7 @@ test('profile funds ViewModel formats recharge product and membership labels', ( cycleResetsAt: '2026-06-02T00:00:00.000Z', }), (value) => - value === '2026-06-03T00:00:00.000Z' - ? '06/03 08:00' - : '06/02 08:00', + value === '2026-06-03T00:00:00.000Z' ? '06/03 08:00' : '06/02 08:00', ), ).toBe('会员至 06/03 08:00 · 06/02 08:00重置'); expect( diff --git a/src/components/rpg-entry/rpgEntryProfileFundsViewModel.ts b/src/components/rpg-entry/rpgEntryProfileFundsViewModel.ts index e871cdc11..ebf4f93b9 100644 --- a/src/components/rpg-entry/rpgEntryProfileFundsViewModel.ts +++ b/src/components/rpg-entry/rpgEntryProfileFundsViewModel.ts @@ -13,6 +13,8 @@ const PROFILE_WALLET_LEDGER_SOURCE_LABELS = { snapshot_sync: '账户同步', membership_period_grant: '会员周期发放', membership_period_reset: '会员周期重置', + daily_free_grant: '每日免费发放', + daily_free_reset: '每日免费重置', asset_operation_consume: '资产操作消耗', asset_operation_refund: '资产操作退回', redeem_code_reward: '兑换码奖励', diff --git a/src/index.css b/src/index.css index 3866fdca9..8df8a0ccb 100644 --- a/src/index.css +++ b/src/index.css @@ -3426,10 +3426,10 @@ html[data-mobile-keyboard-open='true'] .platform-mobile-bottom-dock { .creation-landing__eyebrow { width: fit-content; color: #8c796c; - font-size: clamp(0.86rem, 1.2vw, 1.02rem); + font-size: 17px; font-weight: 860; - letter-spacing: 0.56em; - text-indent: 0.56em; + letter-spacing: 0.18em; + text-indent: 0.18em; } .creation-landing__hero h1 { @@ -3443,13 +3443,33 @@ html[data-mobile-keyboard-open='true'] .platform-mobile-bottom-dock { line-height: 1.04; } -.creation-landing__hero p { +.creation-landing__hero-detail { + display: grid; + max-width: 54rem; + justify-items: center; + gap: 0.42rem; +} + +.creation-landing__hero-subtitle, +.creation-landing__hero-benefit { margin: 0; color: #8a7466; - font-size: clamp(1rem, 1.45vw, 1.28rem); - font-weight: 820; - letter-spacing: 0.38em; - text-indent: 0.38em; + text-indent: 0; +} + +.creation-landing__hero-subtitle { + font-size: 20px; + font-weight: 620; + letter-spacing: 0.04em; + line-height: 1.7; +} + +.creation-landing__hero-benefit { + color: #9a6f56; + font-size: 16px; + font-weight: 680; + letter-spacing: 0.08em; + line-height: 1.55; } .creation-landing__hero-actions { @@ -4339,10 +4359,20 @@ html[data-mobile-keyboard-open='true'] .platform-mobile-bottom-dock { padding-top: 1.6rem; } - .creation-landing__eyebrow, - .creation-landing__hero p { - letter-spacing: 0.18em; - text-indent: 0.18em; + .creation-landing__eyebrow { + letter-spacing: 0.1em; + text-indent: 0.1em; + } + + .creation-landing__hero-subtitle { + font-size: 0.95rem; + letter-spacing: 0.02em; + line-height: 1.62; + } + + .creation-landing__hero-benefit { + font-size: 0.8rem; + letter-spacing: 0.04em; } .creation-landing__hero-actions { @@ -12744,6 +12774,8 @@ button.image-canvas-editor__reference-chip:disabled { .platform-desktop-shell--workbench .platform-desktop-topbar { display: flex; + z-index: 80; + overflow: visible; gap: 0.58rem; min-height: 3.62rem; border: 0; @@ -12868,16 +12900,11 @@ button.image-canvas-editor__reference-chip:disabled { display: none; } -.platform-desktop-shell--workbench .creation-home-topbar-art--wallet { - width: 1.32rem; - height: 1.32rem; - margin: -0.14rem -0.18rem -0.14rem -0.3rem; -} - -.platform-desktop-shell--workbench - .platform-desktop-create-wallet-chip - .platform-icon-badge { - display: none; +.platform-mobile-recommend-wallet-entry { + position: fixed; + top: max(0.75rem, calc(env(safe-area-inset-top, 0px) + 0.35rem)); + right: max(0.75rem, calc(env(safe-area-inset-right, 0px) + 0.35rem)); + z-index: 70; } .platform-desktop-shell--workbench @@ -13842,13 +13869,6 @@ button.image-canvas-editor__reference-chip:disabled { max-width: min(48vw, 10rem); } - .platform-mobile-create-wallet-chip > span:last-child { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - .platform-mobile-create-wallet-chip, .platform-desktop-create-wallet-chip { min-width: 0; diff --git a/src/services/appTitle.test.ts b/src/services/appTitle.test.ts index a1e4cff78..59f7bf3d5 100644 --- a/src/services/appTitle.test.ts +++ b/src/services/appTitle.test.ts @@ -3,6 +3,7 @@ import { afterEach, describe, expect, test, vi } from 'vitest'; import { + APP_HOME_TITLE, resolveAppTitleForSelectionStage, syncAppTitle, } from './appTitle'; @@ -22,7 +23,10 @@ afterEach(() => { describe('appTitle', () => { test('按平台阶段生成可读页面标题', () => { - expect(resolveAppTitleForSelectionStage('platform')).toBe('陶泥儿'); + expect(resolveAppTitleForSelectionStage('platform')).toBe(APP_HOME_TITLE); + expect(resolveAppTitleForSelectionStage('creation-home')).toBe( + APP_HOME_TITLE, + ); expect(resolveAppTitleForSelectionStage('work-detail')).toBe( '作品详情 - 陶泥儿', ); diff --git a/src/services/appTitle.ts b/src/services/appTitle.ts index 4a411b5d1..ef25a00d0 100644 --- a/src/services/appTitle.ts +++ b/src/services/appTitle.ts @@ -2,6 +2,8 @@ import type { SelectionStage } from '../components/platform-entry'; import { setHostAppTitle } from './host-bridge/hostBridge'; const APP_TITLE_BRAND = '陶泥儿'; +export const APP_HOME_TITLE = + '陶泥儿 Genarrative|游戏美术AI创作工具与美术Agent工作台'; const APP_TITLE_BY_SELECTION_STAGE: Partial> = { 'agent-workspace': 'RPG 创作', @@ -21,6 +23,7 @@ const APP_TITLE_BY_SELECTION_STAGE: Partial> = { 'creative-agent-workspace': '创意 Agent', 'custom-world-generating': 'RPG 生成中', 'custom-world-result': 'RPG 结果', + 'creation-home': APP_HOME_TITLE, detail: '世界详情', 'jump-hop-gallery-detail': '跳一跳详情', 'jump-hop-generating': '跳一跳生成中', @@ -31,7 +34,7 @@ const APP_TITLE_BY_SELECTION_STAGE: Partial> = { 'match3d-generating': '抓大鹅生成中', 'match3d-result': '抓大鹅结果', 'match3d-runtime': '抓大鹅', - platform: APP_TITLE_BRAND, + platform: APP_HOME_TITLE, 'profile-feedback': '反馈与投诉', 'puzzle-agent-workspace': '拼图创作', 'puzzle-clear-generating': '拼消消生成中', @@ -61,7 +64,7 @@ const APP_TITLE_BY_SELECTION_STAGE: Partial> = { export function resolveAppTitleForSelectionStage(stage: SelectionStage) { const title = APP_TITLE_BY_SELECTION_STAGE[stage] ?? APP_TITLE_BRAND; - if (title === APP_TITLE_BRAND) { + if (title === APP_TITLE_BRAND || title === APP_HOME_TITLE) { return title; } diff --git a/src/services/rpg-entry/rpgProfileClient.ts b/src/services/rpg-entry/rpgProfileClient.ts index 3e221aad9..ce49fd4db 100644 --- a/src/services/rpg-entry/rpgProfileClient.ts +++ b/src/services/rpg-entry/rpgProfileClient.ts @@ -10,8 +10,8 @@ import type { PlatformBrowseHistoryWriteEntry, ProfileDashboardSummary, ProfilePlayStatsResponse, - ProfileRechargeOrder, ProfileRechargeCenterResponse, + ProfileRechargeOrder, ProfileReferralInviteCenterResponse, ProfileSaveArchiveListResponse, ProfileSaveArchiveResumeResponse, @@ -128,7 +128,7 @@ export function getRpgProfileRechargeCenter( return requestRpgRuntimeJson( '/profile/recharge-center', { method: 'GET' }, - '读取账户充值失败', + '读取泥点购买信息失败', options, ); }