Merge branch 'codex/cache-view-procedure-hotpaths'
This commit is contained in:
36
.dockerignore
Normal file
36
.dockerignore
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
.git
|
||||||
|
.codex-temp
|
||||||
|
.codex-logs
|
||||||
|
.codex-runlogs
|
||||||
|
.idea
|
||||||
|
.vite
|
||||||
|
node_modules
|
||||||
|
target
|
||||||
|
dist
|
||||||
|
coverage
|
||||||
|
logs
|
||||||
|
tmp
|
||||||
|
*.log
|
||||||
|
/*.png
|
||||||
|
/*.jpg
|
||||||
|
/*.jpeg
|
||||||
|
/*.webp
|
||||||
|
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.secrets.local
|
||||||
|
.env.secrets.*
|
||||||
|
spacetime.local.json
|
||||||
|
deploy/container/api-server.env
|
||||||
|
|
||||||
|
server-rs/target
|
||||||
|
server-rs/target-*
|
||||||
|
server-rs/.data
|
||||||
|
server-rs/.spacetimedb
|
||||||
|
|
||||||
|
public/generated-*
|
||||||
|
|
||||||
|
scripts/loadtest/data/*.local.json
|
||||||
|
scripts/loadtest/data/k6-*.log
|
||||||
|
scripts/loadtest/data/k6-*summary*.md
|
||||||
|
scripts/loadtest/data/latest-*-prefix.txt
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -33,6 +33,7 @@ temp*build*/
|
|||||||
.worktrees/
|
.worktrees/
|
||||||
.env.secrets.local
|
.env.secrets.local
|
||||||
spacetime.local.json
|
spacetime.local.json
|
||||||
|
deploy/container/api-server.env
|
||||||
|
|
||||||
# Local load-test data extracted from private migration files
|
# Local load-test data extracted from private migration files
|
||||||
scripts/loadtest/data/*.local.json
|
scripts/loadtest/data/*.local.json
|
||||||
|
|||||||
@@ -16,6 +16,25 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 2026-05-17 容器化方案只作为隔离压测与预发模拟路径
|
||||||
|
|
||||||
|
- 背景:Windows 本机直连极高 VU 压测会放大本地连接与发送缓冲行为,和线上 Linux + Nginx + systemd 拓扑不一致;需要一个更接近生产网络层的模拟方案,但不能扰动当前生产发布链路。
|
||||||
|
- 决策:新增 `deploy/container/` 容器化方案,使用 Docker Compose 组合 Linux release `api-server`、容器 Nginx、`otelcol-contrib` debug exporter 和可选 k6。该方案只用于本机或预发压测模拟,不替换当前生产 `systemd + Nginx + Jenkins` 路径。
|
||||||
|
- 隔离边界:容器方案使用独立 `deploy/container/api-server.env`、独立 Nginx 配置、独立 compose 命令和默认 `18080` 端口;真实 token 不进入镜像、不提交 Git;生产 systemd 单元、Jenkins 发布脚本和 `deploy/nginx/` 模板仍是正式线上来源。
|
||||||
|
- 影响范围:`deploy/container/`、`scripts/container-compose.mjs`、`package.json` 容器命令、开发运维文档和容器 build context 排除规则。
|
||||||
|
- 验证方式:执行 `npm run container:config` 展开 compose 配置;需要真实运行时再执行 `npm run container:build`、`npm run container:up`、`npm run container:k6`,并结合容器 Nginx log 与 OTLP debug exporter 判断瓶颈。
|
||||||
|
- 关联文档:`deploy/container/README.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
|
||||||
|
|
||||||
|
## 2026-05-16 公开作品列表短期由 BFF 订阅读模型缓存
|
||||||
|
|
||||||
|
- 背景:作品列表压测和实时性讨论中,曾考虑让浏览器前端直接订阅公开作品列表,减少 HTTP 拉取和 BFF 压力。
|
||||||
|
- 决策:本轮不直接把作品列表整体交给前端订阅。短期继续由 `api-server` / BFF 通过 `spacetime-client` 长期订阅 SpacetimeDB 公开 read model 并读取本地 cache,维持首屏、排序、字段归一、权限降级和 HTTP fallback。中期可以新增或统一稳定的专用公开作品列表 read model,例如 `public_work_gallery_entry`,作为前端可选直连订阅对象。
|
||||||
|
- 边界:未来前端直订阅只允许面向稳定、低基数、公开的专用 read model。前端不得直接订阅 `puzzle_work_profile`、`custom_world_profile` 等领域源表,也不得在前端自行 join、聚合或执行公开权限逻辑;这些逻辑必须先沉到后端投影 / read model。
|
||||||
|
- 后续准入:若要落地前端直订阅,必须先完成并验收权限边界、字段契约、排序 / 分页、埋点和 BFF 回退策略;缺任一项时继续走 `api-server` / BFF 订阅缓存方案。
|
||||||
|
- 影响范围:发现页、推荐流、各玩法公开广场、`api-server` 公开列表缓存、SpacetimeDB public view / public 读模型设计。
|
||||||
|
- 验证方式:新增公开作品列表订阅能力时,检查前端只消费专用 public read model 或 BFF HTTP DTO;检查源表 row shape、权限判断和跨玩法聚合没有下沉到前端页面。
|
||||||
|
- 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
|
||||||
|
|
||||||
## 2026-05-16 api-server OpenTelemetry 统一补齐 traces metrics logs
|
## 2026-05-16 api-server OpenTelemetry 统一补齐 traces metrics logs
|
||||||
|
|
||||||
- 背景:压测与运行观测需要把 HTTP、SpacetimeDB 调用和应用日志串起来,同时保留本地 `journalctl` / 文件日志做故障排障。
|
- 背景:压测与运行观测需要把 HTTP、SpacetimeDB 调用和应用日志串起来,同时保留本地 `journalctl` / 文件日志做故障排障。
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ npm run check:server-rs-ddd
|
|||||||
## 生产压测与观测默认口径
|
## 生产压测与观测默认口径
|
||||||
|
|
||||||
- 作品列表 50 HTTP req/s 压测使用 `scripts/loadtest/README.md` 中的 K6 命令;当前脚本一次 iteration 请求两个公开列表接口,因此目标 50 HTTP req/s 对应 `PEAK_RPS=25`。
|
- 作品列表 50 HTTP req/s 压测使用 `scripts/loadtest/README.md` 中的 K6 命令;当前脚本一次 iteration 请求两个公开列表接口,因此目标 50 HTTP req/s 对应 `PEAK_RPS=25`。
|
||||||
- 生产 `api-server` 默认 backlog、worker threads、systemd 限制、Nginx upstream timing log 和 OTLP 开关以 `docs/【开发运维】本地开发验证与生产运维-2026-05-15.md` 为准。
|
- 生产 `api-server` 默认 backlog、worker threads、HTTP 并发背压、systemd 限制、Nginx upstream timing log 和 OTLP 开关以 `docs/【开发运维】本地开发验证与生产运维-2026-05-15.md` 为准。
|
||||||
- OpenTelemetry 现阶段可选发送 traces / metrics / logs,但不会取代本地 `journalctl -u genarrative-api.service`、`logs/api-server/` 与 `/var/log/nginx/genarrative.*.log`。
|
- OpenTelemetry 现阶段可选发送 traces / metrics / logs,但不会取代本地 `journalctl -u genarrative-api.service`、`logs/api-server/` 与 `/var/log/nginx/genarrative.*.log`。
|
||||||
- 指标 label 不写 raw URI、userId、profileId 或 request_id;request_id 只用于 trace/log 串联。
|
- 指标 label 不写 raw URI、userId、profileId 或 request_id;request_id 只用于 trace/log 串联。
|
||||||
|
|
||||||
|
|||||||
@@ -95,9 +95,33 @@
|
|||||||
|
|
||||||
- 现象:`/api/runtime/puzzle/gallery` 每个请求都走 `spacetime-client.list_puzzle_gallery()` 调用 SpacetimeDB procedure,导致 SpacetimeDB WASM 侧重复组装全量列表,客户端再映射一遍;历史实现还出现过 procedure JSON 字符串往返。
|
- 现象:`/api/runtime/puzzle/gallery` 每个请求都走 `spacetime-client.list_puzzle_gallery()` 调用 SpacetimeDB procedure,导致 SpacetimeDB WASM 侧重复组装全量列表,客户端再映射一遍;历史实现还出现过 procedure JSON 字符串往返。
|
||||||
- 原因:`api-server` 的服务器端 `spacetime-client` 没有订阅可公开读取的 gallery 投影,虽然 SDK 支持 client cache,但请求路径仍把列表读取当作 procedure 调用。
|
- 原因:`api-server` 的服务器端 `spacetime-client` 没有订阅可公开读取的 gallery 投影,虽然 SDK 支持 client cache,但请求路径仍把列表读取当作 procedure 调用。
|
||||||
- 处理:`spacetime-module` 中用 public view `puzzle_gallery_view` 暴露已发布拼图作品;`spacetime-client` 建连接后订阅 `SELECT * FROM puzzle_gallery_view` 和 `SELECT * FROM public_work_play_daily_stat WHERE source_type = 'puzzle'` 并等待 `on_applied`,HTTP gallery 只从 `connection.db().puzzle_gallery_view().iter()` 本地 cache 读取和排序,再用已同步的 `public_work_play_daily_stat` 在本地聚合 7 日播放数。旧 `list_puzzle_gallery` procedure 只作兼容,不再作为 HTTP gallery 主路径。
|
- 处理:`spacetime-module` 中用 public view `puzzle_gallery_card_view` 暴露已发布拼图作品的列表卡片字段,不携带 `levels` / `anchor_pack` 等详情级载荷;`spacetime-client` 建连接后订阅 `SELECT * FROM puzzle_gallery_card_view` 和 `SELECT * FROM public_work_play_daily_stat WHERE source_type = 'puzzle'` 并等待 `on_applied`。HTTP gallery 通过 `PuzzleGalleryCache` 缓存最终 `PuzzleGalleryResponse` DTO:`items` 返回前 10 个完整卡片,`previewRefs` 返回后 10 个作品号引用,cache miss / TTL 过期时单飞重建,后台 cleanup task 周期清理旧响应。旧 `list_puzzle_gallery` procedure 只作兼容,不再作为 HTTP gallery 主路径。
|
||||||
- 验证:搜索 `server-rs/crates/spacetime-client/src/puzzle.rs` 不应再出现 gallery 主路径调用 `list_puzzle_gallery_then`;执行 `cargo check --manifest-path server-rs/Cargo.toml -p spacetime-client`、`cargo check --manifest-path server-rs/Cargo.toml -p api-server` 和 schema/runtime access 检查。
|
- 验证:搜索 `server-rs/crates/spacetime-client/src/puzzle.rs` 不应再出现 gallery 主路径调用 `list_puzzle_gallery_then`;搜索 `server-rs/crates/spacetime-client/src/lib.rs` 应订阅 `puzzle_gallery_card_view`;执行 `npm run spacetime:generate`、`cargo check --manifest-path server-rs/Cargo.toml -p spacetime-client`、`cargo check --manifest-path server-rs/Cargo.toml -p api-server` 和 schema/runtime access 检查。
|
||||||
- 关联:`server-rs/crates/spacetime-module/src/puzzle.rs`、`server-rs/crates/spacetime-client/src/lib.rs`、`server-rs/crates/spacetime-client/src/puzzle.rs`、`/api/runtime/puzzle/gallery`。
|
- 关联:`server-rs/crates/spacetime-module/src/puzzle.rs`、`server-rs/crates/spacetime-client/src/lib.rs`、`server-rs/crates/spacetime-client/src/puzzle.rs`、`server-rs/crates/api-server/src/puzzle_gallery_cache.rs`、`/api/runtime/puzzle/gallery`。
|
||||||
|
|
||||||
|
## Windows 本地直连高 VU 压测不要误判成业务内存泄漏
|
||||||
|
|
||||||
|
- 现象:本地 Windows release `api-server` 直连 K6 压测时,250 RPS、`PREALLOCATED_VUS=300` 能把进程 private memory 瞬时推到约 7GB;同样配置打 `/healthz` 小响应也能复现,压测结束后回落到 100MB 级。
|
||||||
|
- 原因:高水位主要来自本机直连的 K6 VU / 长连接 / Hyper 发送链路和 Windows 连接缓冲,不是 SpacetimeDB procedure、拼图 JSON 缓存或 OTEL exporter。降低到接近真实并发的 VU 后,同样 250 RPS 拼图广场 p95 约 9ms,峰值约 600MB。
|
||||||
|
- 处理:本地容量判断时让 `PREALLOCATED_VUS` / `MAX_VUS` 接近真实并发,不要把过高 VU 预分配当作默认吞吐测试;同时观察 `process.memory.*`、`process.windows.handle.count`、`genarrative.http.server.response_bodies.in_flight`、`genarrative.http.server.request_permits.available`、`genarrative.puzzle_gallery.cache.*` 和 `genarrative.spacetime.read.*`。如果内存高但 body in-flight、背压 permit、cache rebuild 和 SpacetimeDB read 都不显示积压,优先按连接 / 发送链路高水位处理。
|
||||||
|
- 验证:对照打 `/api/runtime/puzzle/gallery` 与 `/healthz`;对比 `PREALLOCATED_VUS=300 MAX_VUS=800` 和 `PREALLOCATED_VUS=20 MAX_VUS=40`;压测结束后继续采样 10 秒确认 private memory 回落。
|
||||||
|
- 关联:`scripts/loadtest/README.md`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、`server-rs/crates/api-server/src/process_metrics.rs`、`server-rs/crates/api-server/src/telemetry.rs`。
|
||||||
|
|
||||||
|
## 多玩法公开广场列表优先订阅 public view / read model
|
||||||
|
|
||||||
|
- 现象:抓大鹅、方洞挑战、视觉小说、大鱼吃小鱼等公开列表如果沿用 `list_*_works` procedure,即使只读已发布作品,也会在每个 HTTP 请求里回到 SpacetimeDB WASM 侧扫描、反序列化配置并组装列表,50RPS 以上容易变成热点。
|
||||||
|
- 原因:个人作品列表和公开广场列表复用了同一套 procedure 输入,导致公开列表为了通过 owner 校验传固定占位 owner,并把可长期同步的公开读模型当成请求期查询。
|
||||||
|
- 处理:每个公开广场新增或复用专用 public view / public read model:`match_3_d_gallery_view`、`square_hole_gallery_view`、`visual_novel_gallery_view`、`big_fish_gallery_view`。`spacetime-client` 建连接后订阅这些 view 和对应 `public_work_play_daily_stat` source_type 桶,HTTP gallery 只读本地 cache。个人作品列表、详情、发布、点赞、游玩记录和 Remix 仍走原有 procedure / reducer。
|
||||||
|
- 验证:搜索 `server-rs/crates/spacetime-client/src/{match3d,square_hole,visual_novel,big_fish}.rs`,公开 gallery 主路径应读取 `connection.db().*_gallery_view()`,不应调用 `list_*_works_with_input`;执行 `npm run spacetime:generate`、`cargo check -p spacetime-client --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:spacetime-schema`。
|
||||||
|
- 关联:`server-rs/crates/spacetime-module/src/match3d/mod.rs`、`server-rs/crates/spacetime-module/src/square_hole/mod.rs`、`server-rs/crates/spacetime-module/src/visual_novel.rs`、`server-rs/crates/spacetime-module/src/big_fish/session.rs`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
|
||||||
|
|
||||||
|
## 自定义世界广场和创作入口配置不要每次 HTTP 请求调用只读 procedure
|
||||||
|
|
||||||
|
- 现象:`/api/runtime/custom-world-gallery` 每次请求调用 `list_custom_world_gallery_entries` procedure;入口熔断中间件每个玩法请求调用 `get_creation_entry_config` procedure,50RPS 以上会把 SpacetimeDB procedure 调用变成热点。
|
||||||
|
- 原因:`custom_world_gallery_entry`、`creation_entry_config` 和 `creation_entry_type_config` 已经是可订阅读模型或配置表,但 HTTP 路径仍按“请求到来再查 procedure”处理。
|
||||||
|
- 处理:`spacetime-client` 长连接订阅 `custom_world_gallery_entry`、`public_work_play_daily_stat` 的 `custom-world` 桶、`creation_entry_config` 和 `creation_entry_type_config`;custom-world gallery 从本地 cache 排序并聚合 7 日播放数;入口配置优先读订阅 cache,cache 缺失时用最近一次成功内存快照,再兜底调用 `get_creation_entry_config` 完成旧库兼容。旧 `list_custom_world_gallery_entries` procedure 只允许作为旧库缺少 gallery 行时的一次性同步兜底。
|
||||||
|
- 验证:搜索 `server-rs/crates/spacetime-client/src/custom_world.rs`,gallery 主路径应是 `read_after_connect` 读取 `custom_world_gallery_entry()`;搜索 `server-rs/crates/spacetime-client/src/runtime.rs`,`get_creation_entry_config` 应优先读取 `creation_entry_config()` 和 `creation_entry_type_config()`。执行 `cargo check -p spacetime-client --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`。
|
||||||
|
- 关联:`server-rs/crates/spacetime-client/src/lib.rs`、`server-rs/crates/spacetime-client/src/custom_world.rs`、`server-rs/crates/spacetime-client/src/runtime.rs`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
|
||||||
|
|
||||||
## 陶泥儿 logo 生图慢请求先缩短 prompt 并单张串行
|
## 陶泥儿 logo 生图慢请求先缩短 prompt 并单张串行
|
||||||
|
|
||||||
|
|||||||
27
.hermes/todos/【后端架构】前端直订阅公开作品列表准入待办-2026-05-16.md
Normal file
27
.hermes/todos/【后端架构】前端直订阅公开作品列表准入待办-2026-05-16.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# 前端直订阅公开作品列表准入待办
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
|
||||||
|
未来可以考虑让前端直接订阅公开作品列表,以减少列表读取链路中的 HTTP 往返,并复用 SpacetimeDB 的实时同步能力。
|
||||||
|
|
||||||
|
## 当前结论
|
||||||
|
|
||||||
|
短期仍由 `api-server` / BFF 订阅 SpacetimeDB public read model,并从本地 cache 读取后对外提供 HTTP 列表接口。前端不直接订阅作品源表,也不把正式列表排序、分页、权限裁剪逻辑下放到 UI。
|
||||||
|
|
||||||
|
## 落地前置条件
|
||||||
|
|
||||||
|
- 建立专用、稳定、低基数的 public read model,例如 `public_work_gallery_entry`。
|
||||||
|
- 明确权限边界,只暴露公开列表所需字段,不泄露作者私有信息、审核内部状态或运营字段。
|
||||||
|
- 固化字段契约,明确字段含义、默认值、兼容策略和生成绑定更新流程。
|
||||||
|
- 明确排序与分页语义,避免依赖自增 ID 顺序,优先使用时间戳或显式排序字段。
|
||||||
|
- 补齐埋点方案,能区分直订阅首屏、增量更新、分页加载和 fallback 命中。
|
||||||
|
- 保留 BFF HTTP fallback,用于低版本客户端、订阅失败、权限策略调整和灰度回滚。
|
||||||
|
- 禁止前端订阅 `puzzle_work_profile`、`custom_world_profile` 等作品源表。
|
||||||
|
|
||||||
|
## 建议验收
|
||||||
|
|
||||||
|
- 文档确认直订阅只面向专用 public read model,不绕过 BFF 读取源表。
|
||||||
|
- schema、绑定、字段契约、排序分页和权限说明同步更新。
|
||||||
|
- 前端具备订阅失败后的 BFF HTTP fallback。
|
||||||
|
- 自动测试覆盖公开字段裁剪、排序分页稳定性和 fallback 路径。
|
||||||
|
- 监控可观察直订阅成功率、首屏耗时、增量更新延迟和 fallback 比例。
|
||||||
132
deploy/container/README.md
Normal file
132
deploy/container/README.md
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
# Genarrative 容器化压测与隔离部署方案
|
||||||
|
|
||||||
|
本目录只服务本机或预发的容器化模拟压测,不替换当前生产 `systemd + Nginx + Jenkins` 发布路径。生产服务器仍以 `deploy/systemd/`、`deploy/nginx/`、`scripts/jenkins-*.sh` 和 `scripts/deploy/production-api-deploy.sh` 为准。
|
||||||
|
|
||||||
|
## 拓扑
|
||||||
|
|
||||||
|
```text
|
||||||
|
Docker Compose
|
||||||
|
├─ nginx :80 -> api-server:8082,负责静态站点、/admin/、/api/ 反代、upstream timing log、连接限制
|
||||||
|
├─ api-server :8082,Linux release 构建,连接外部 SpacetimeDB
|
||||||
|
├─ otelcol :4317/4318,debug exporter,接收 traces / metrics / logs
|
||||||
|
└─ k6 profile=loadtest 时临时启动,在 compose 网络内压 nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
默认 host 端口:
|
||||||
|
|
||||||
|
- `http://127.0.0.1:18080`:容器 Nginx。
|
||||||
|
- `127.0.0.1:4317` / `127.0.0.1:4318`:容器 Collector OTLP gRPC / HTTP。
|
||||||
|
|
||||||
|
如端口冲突,可设置:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$env:GENARRATIVE_CONTAINER_HTTP_PORT="18081"
|
||||||
|
$env:GENARRATIVE_CONTAINER_OTLP_HTTP_PORT="14318"
|
||||||
|
$env:GENARRATIVE_CONTAINER_OTLP_GRPC_PORT="14317"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 初始化
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run container:init
|
||||||
|
```
|
||||||
|
|
||||||
|
该命令会从 `deploy/container/api-server.env.example` 生成本地 `deploy/container/api-server.env`。真实 token、库名和外部服务密钥只写本地 env 文件,不提交 Git。
|
||||||
|
|
||||||
|
Docker Desktop 下默认通过 `host.docker.internal:3101` 连接宿主机上 `npm run dev` 启动的 SpacetimeDB:
|
||||||
|
|
||||||
|
```env
|
||||||
|
GENARRATIVE_SPACETIME_SERVER_URL=http://host.docker.internal:3101
|
||||||
|
GENARRATIVE_SPACETIME_DATABASE=genarrative-loadtest
|
||||||
|
GENARRATIVE_SPACETIME_TOKEN=
|
||||||
|
```
|
||||||
|
|
||||||
|
Linux Docker Engine 如果不能解析 `host.docker.internal`,Compose 已配置 `host-gateway`;仍不通时把 `GENARRATIVE_SPACETIME_SERVER_URL` 改成宿主机网关 IP 或同网络内的 SpacetimeDB 地址。
|
||||||
|
|
||||||
|
## 启动与验证
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run container:config
|
||||||
|
npm run container:build
|
||||||
|
npm run container:up
|
||||||
|
npm run container:ps
|
||||||
|
curl -sS http://127.0.0.1:18080/api/runtime/puzzle/gallery
|
||||||
|
```
|
||||||
|
|
||||||
|
查看日志:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run container:logs -- nginx
|
||||||
|
npm run container:logs -- api-server
|
||||||
|
npm run container:logs -- otelcol
|
||||||
|
```
|
||||||
|
|
||||||
|
`npm run container:config` 默认只校验配置,不打印完整 env。排查 compose 展开结果时可临时使用:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run container:config -- --print
|
||||||
|
```
|
||||||
|
|
||||||
|
如果 `deploy/container/api-server.env` 已写入真实 token,不要把完整展开结果贴到公开渠道。
|
||||||
|
|
||||||
|
停止:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run container:down
|
||||||
|
```
|
||||||
|
|
||||||
|
如需同时清理容器卷:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run container:down -- -v
|
||||||
|
```
|
||||||
|
|
||||||
|
## 压测
|
||||||
|
|
||||||
|
k6 在 compose 网络内访问 `http://nginx`,避免 Windows 本机直连连接模型干扰 Linux 容器结果:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run container:k6
|
||||||
|
```
|
||||||
|
|
||||||
|
作品列表脚本一次 iteration 默认请求两个公开列表接口,因此目标 500 HTTP req/s 对应 `PEAK_RPS=250`:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$env:SCENARIO="spike"
|
||||||
|
$env:START_RPS="25"
|
||||||
|
$env:PEAK_RPS="250"
|
||||||
|
$env:HOLD="60s"
|
||||||
|
$env:END_RPS="25"
|
||||||
|
$env:PREALLOCATED_VUS="100"
|
||||||
|
$env:MAX_VUS="500"
|
||||||
|
$env:DETAIL_RATIO="0"
|
||||||
|
npm run container:k6
|
||||||
|
```
|
||||||
|
|
||||||
|
如果要压 1000 HTTP req/s,把 `PEAK_RPS` 调到 `500`;如果要压 5000 HTTP req/s,把 `PEAK_RPS` 调到 `2500`,并同时提高 `PREALLOCATED_VUS` / `MAX_VUS`,观察是否先被带宽、Nginx `limit_conn` 或 api-server 背压限制。
|
||||||
|
|
||||||
|
## OTLP
|
||||||
|
|
||||||
|
容器内 `otelcol` 默认使用 debug exporter。开启 api-server OTEL:
|
||||||
|
|
||||||
|
```env
|
||||||
|
GENARRATIVE_OTEL_ENABLED=true
|
||||||
|
OTEL_EXPORTER_OTLP_ENDPOINT=http://otelcol:4318
|
||||||
|
```
|
||||||
|
|
||||||
|
然后重建或重启容器:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run container:up
|
||||||
|
npm run container:logs -- otelcol
|
||||||
|
```
|
||||||
|
|
||||||
|
Collector 日志会输出 traces / metrics / logs。接 Rider、Jaeger、Tempo、Prometheus、Grafana 或托管平台时,另建独立 Collector 配置,不直接改生产 systemd 或 Nginx 模板。
|
||||||
|
|
||||||
|
## 隔离边界
|
||||||
|
|
||||||
|
- 不改生产 systemd 单元。
|
||||||
|
- 不改 Jenkins 发布主流程。
|
||||||
|
- 不要求真实 HTTPS 证书。
|
||||||
|
- 不把真实 `.env`、`.env.local`、`.env.secrets.local` 或 `deploy/container/api-server.env` 放入 Docker build context。
|
||||||
|
- 不在容器镜像里内置 SpacetimeDB 数据或 token。
|
||||||
49
deploy/container/api-server.Dockerfile
Normal file
49
deploy/container/api-server.Dockerfile
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
FROM rust:1.88-bookworm AS rust-builder
|
||||||
|
WORKDIR /workspace
|
||||||
|
|
||||||
|
COPY server-rs ./server-rs
|
||||||
|
RUN cargo build --release -p api-server --manifest-path server-rs/Cargo.toml && \
|
||||||
|
cp server-rs/target/release/api-server /tmp/api-server
|
||||||
|
|
||||||
|
FROM debian:bookworm-slim AS api-runtime
|
||||||
|
WORKDIR /srv/genarrative
|
||||||
|
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y --no-install-recommends ca-certificates curl && \
|
||||||
|
rm -rf /var/lib/apt/lists/* && \
|
||||||
|
useradd --system --create-home --home-dir /srv/genarrative --shell /usr/sbin/nologin genarrative
|
||||||
|
|
||||||
|
COPY --from=rust-builder /tmp/api-server /usr/local/bin/api-server
|
||||||
|
|
||||||
|
RUN mkdir -p /var/lib/genarrative/auth && \
|
||||||
|
chown -R genarrative:genarrative /srv/genarrative /var/lib/genarrative
|
||||||
|
|
||||||
|
USER genarrative
|
||||||
|
EXPOSE 8082
|
||||||
|
|
||||||
|
ENV GENARRATIVE_ENV=container \
|
||||||
|
GENARRATIVE_API_HOST=0.0.0.0 \
|
||||||
|
GENARRATIVE_API_PORT=8082 \
|
||||||
|
GENARRATIVE_AUTH_STORE_PATH=/var/lib/genarrative/auth/auth-store.json
|
||||||
|
|
||||||
|
CMD ["api-server"]
|
||||||
|
|
||||||
|
FROM node:22-bookworm-slim AS web-builder
|
||||||
|
WORKDIR /workspace
|
||||||
|
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
COPY apps/admin-web/package.json ./apps/admin-web/package.json
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
COPY index.html metadata.json tsconfig.json vite.config.ts ./
|
||||||
|
COPY src ./src
|
||||||
|
COPY public ./public
|
||||||
|
COPY media ./media
|
||||||
|
COPY packages ./packages
|
||||||
|
COPY apps/admin-web ./apps/admin-web
|
||||||
|
RUN npm run build:raw && npm run admin-web:build
|
||||||
|
|
||||||
|
FROM nginx:1.27-alpine AS nginx-runtime
|
||||||
|
COPY --from=web-builder /workspace/dist /srv/genarrative/web
|
||||||
|
COPY --from=web-builder /workspace/apps/admin-web/dist /srv/genarrative/web/admin
|
||||||
|
COPY deploy/container/nginx.conf /etc/nginx/nginx.conf
|
||||||
35
deploy/container/api-server.env.example
Normal file
35
deploy/container/api-server.env.example
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# 复制为 deploy/container/api-server.env 后填入本机或预发值。
|
||||||
|
# 该文件只用于容器隔离方案,不参与 systemd/Jenkins 生产部署。
|
||||||
|
# 不要在这里写真实 token 后提交 Git。
|
||||||
|
|
||||||
|
GENARRATIVE_ENV=container
|
||||||
|
GENARRATIVE_API_HOST=0.0.0.0
|
||||||
|
GENARRATIVE_API_PORT=8082
|
||||||
|
GENARRATIVE_API_LOG=info,tower_http=info
|
||||||
|
GENARRATIVE_API_LISTEN_BACKLOG=1024
|
||||||
|
GENARRATIVE_API_WORKER_THREADS=4
|
||||||
|
GENARRATIVE_API_MAX_CONCURRENT_REQUESTS=512
|
||||||
|
|
||||||
|
GENARRATIVE_OTEL_ENABLED=false
|
||||||
|
OTEL_SERVICE_NAME=genarrative-api
|
||||||
|
OTEL_EXPORTER_OTLP_ENDPOINT=http://otelcol:4318
|
||||||
|
OTEL_RESOURCE_ATTRIBUTES=deployment.environment=container,service.namespace=genarrative
|
||||||
|
|
||||||
|
GENARRATIVE_INTERNAL_API_SECRET=CHANGE_ME_FOR_CONTAINER
|
||||||
|
GENARRATIVE_JWT_ISSUER=genarrative-container
|
||||||
|
GENARRATIVE_JWT_SECRET=CHANGE_ME_FOR_CONTAINER
|
||||||
|
AUTH_REFRESH_COOKIE_SECURE=false
|
||||||
|
GENARRATIVE_AUTH_STORE_PATH=/var/lib/genarrative/auth/auth-store.json
|
||||||
|
|
||||||
|
# Docker Desktop 下连接宿主机 npm run dev 启动的 SpacetimeDB。
|
||||||
|
# Linux Docker Engine 可改成宿主机网关 IP,或在 compose 里接入同一网络内的 SpacetimeDB。
|
||||||
|
GENARRATIVE_SPACETIME_SERVER_URL=http://host.docker.internal:3101
|
||||||
|
GENARRATIVE_SPACETIME_DATABASE=genarrative-loadtest
|
||||||
|
GENARRATIVE_SPACETIME_TOKEN=
|
||||||
|
GENARRATIVE_SPACETIME_POOL_SIZE=8
|
||||||
|
GENARRATIVE_SPACETIME_PROCEDURE_TIMEOUT_SECONDS=45
|
||||||
|
|
||||||
|
GENARRATIVE_LLM_PROVIDER=openai-compatible
|
||||||
|
GENARRATIVE_LLM_BASE_URL=
|
||||||
|
GENARRATIVE_LLM_API_KEY=
|
||||||
|
GENARRATIVE_LLM_MODEL=
|
||||||
85
deploy/container/docker-compose.loadtest.yml
Normal file
85
deploy/container/docker-compose.loadtest.yml
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
name: genarrative-container-loadtest
|
||||||
|
|
||||||
|
services:
|
||||||
|
api-server:
|
||||||
|
build:
|
||||||
|
context: ../..
|
||||||
|
dockerfile: deploy/container/api-server.Dockerfile
|
||||||
|
target: api-runtime
|
||||||
|
env_file:
|
||||||
|
- ./api-server.env
|
||||||
|
environment:
|
||||||
|
GENARRATIVE_API_HOST: 0.0.0.0
|
||||||
|
GENARRATIVE_API_PORT: 8082
|
||||||
|
OTEL_EXPORTER_OTLP_ENDPOINT: http://otelcol:4318
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
|
volumes:
|
||||||
|
- api-auth-store:/var/lib/genarrative/auth
|
||||||
|
depends_on:
|
||||||
|
otelcol:
|
||||||
|
condition: service_started
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8082/healthz"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 12
|
||||||
|
start_period: 20s
|
||||||
|
|
||||||
|
nginx:
|
||||||
|
build:
|
||||||
|
context: ../..
|
||||||
|
dockerfile: deploy/container/api-server.Dockerfile
|
||||||
|
target: nginx-runtime
|
||||||
|
depends_on:
|
||||||
|
api-server:
|
||||||
|
condition: service_healthy
|
||||||
|
ports:
|
||||||
|
- "${GENARRATIVE_CONTAINER_HTTP_PORT:-18080}:80"
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
|
volumes:
|
||||||
|
- nginx-logs:/var/log/nginx
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "-qO-", "http://127.0.0.1/api/runtime/puzzle/gallery"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
start_period: 20s
|
||||||
|
|
||||||
|
otelcol:
|
||||||
|
image: otel/opentelemetry-collector-contrib:0.125.0
|
||||||
|
command: ["--config=/etc/otelcol/config.yaml"]
|
||||||
|
volumes:
|
||||||
|
- ./otelcol.yaml:/etc/otelcol/config.yaml:ro
|
||||||
|
ports:
|
||||||
|
- "${GENARRATIVE_CONTAINER_OTLP_GRPC_PORT:-4317}:4317"
|
||||||
|
- "${GENARRATIVE_CONTAINER_OTLP_HTTP_PORT:-4318}:4318"
|
||||||
|
|
||||||
|
k6:
|
||||||
|
image: grafana/k6:0.52.0
|
||||||
|
profiles: ["loadtest"]
|
||||||
|
depends_on:
|
||||||
|
nginx:
|
||||||
|
condition: service_healthy
|
||||||
|
environment:
|
||||||
|
BASE_URL: http://nginx
|
||||||
|
WORKS_DATA: data/works-list.sample.json
|
||||||
|
SCENARIO: ${SCENARIO:-spike}
|
||||||
|
START_RPS: ${START_RPS:-5}
|
||||||
|
PEAK_RPS: ${PEAK_RPS:-250}
|
||||||
|
HOLD: ${HOLD:-60s}
|
||||||
|
END_RPS: ${END_RPS:-5}
|
||||||
|
PREALLOCATED_VUS: ${PREALLOCATED_VUS:-100}
|
||||||
|
MAX_VUS: ${MAX_VUS:-500}
|
||||||
|
DETAIL_RATIO: ${DETAIL_RATIO:-0}
|
||||||
|
SLEEP_MIN_SECONDS: ${SLEEP_MIN_SECONDS:-0}
|
||||||
|
SLEEP_MAX_SECONDS: ${SLEEP_MAX_SECONDS:-0}
|
||||||
|
volumes:
|
||||||
|
- ../../scripts/loadtest:/scripts/loadtest:ro
|
||||||
|
working_dir: /scripts/loadtest
|
||||||
|
command: ["run", "k6-works-list.js"]
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
api-auth-store:
|
||||||
|
nginx-logs:
|
||||||
133
deploy/container/nginx.conf
Normal file
133
deploy/container/nginx.conf
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
worker_processes auto;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 4096;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
log_format genarrative_upstream
|
||||||
|
'$remote_addr - $remote_user [$time_local] "$request" '
|
||||||
|
'$status $body_bytes_sent "$http_referer" "$http_user_agent" '
|
||||||
|
'request_time=$request_time upstream_connect_time=$upstream_connect_time '
|
||||||
|
'upstream_header_time=$upstream_header_time upstream_response_time=$upstream_response_time '
|
||||||
|
'upstream_status=$upstream_status request_id=$request_id';
|
||||||
|
|
||||||
|
upstream genarrative_api {
|
||||||
|
server api-server:8082;
|
||||||
|
keepalive 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
limit_conn_zone $binary_remote_addr zone=genarrative_api_conn:10m;
|
||||||
|
|
||||||
|
sendfile on;
|
||||||
|
keepalive_timeout 65;
|
||||||
|
|
||||||
|
gzip on;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_proxied any;
|
||||||
|
gzip_comp_level 5;
|
||||||
|
gzip_min_length 1024;
|
||||||
|
gzip_types
|
||||||
|
text/plain
|
||||||
|
text/css
|
||||||
|
text/javascript
|
||||||
|
application/javascript
|
||||||
|
application/json
|
||||||
|
application/xml
|
||||||
|
application/xml+rss
|
||||||
|
image/svg+xml;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
access_log /var/log/nginx/genarrative.access.log genarrative_upstream;
|
||||||
|
error_log /var/log/nginx/genarrative.error.log warn;
|
||||||
|
limit_conn_status 429;
|
||||||
|
limit_conn_log_level warn;
|
||||||
|
|
||||||
|
root /srv/genarrative/web;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
location ^~ /admin/api/ {
|
||||||
|
default_type application/json;
|
||||||
|
limit_conn genarrative_api_conn 64;
|
||||||
|
|
||||||
|
proxy_pass http://genarrative_api/admin/api/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Request-Id $request_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /admin {
|
||||||
|
return 301 /admin/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ^~ /admin/assets/ {
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ^~ /admin/ {
|
||||||
|
try_files $uri $uri/ /admin/index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ^~ /assets/ {
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ ^/api(?:/|$) {
|
||||||
|
default_type application/json;
|
||||||
|
limit_conn genarrative_api_conn 64;
|
||||||
|
|
||||||
|
proxy_pass http://genarrative_api;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_read_timeout 3600s;
|
||||||
|
proxy_send_timeout 3600s;
|
||||||
|
add_header X-Accel-Buffering no always;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Forwarded-Host $host;
|
||||||
|
proxy_set_header X-Request-Id $request_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ ^/(generated-|healthz) {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ ^/v1/database/[^/]+/subscribe$ {
|
||||||
|
proxy_pass http://host.docker.internal:3101;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "Upgrade";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_read_timeout 3600s;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ^~ /v1/identity {
|
||||||
|
proxy_pass http://host.docker.internal:3101;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "Upgrade";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ^~ /v1/ {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
deploy/container/otelcol.yaml
Normal file
23
deploy/container/otelcol.yaml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
receivers:
|
||||||
|
otlp:
|
||||||
|
protocols:
|
||||||
|
grpc:
|
||||||
|
endpoint: 0.0.0.0:4317
|
||||||
|
http:
|
||||||
|
endpoint: 0.0.0.0:4318
|
||||||
|
|
||||||
|
exporters:
|
||||||
|
debug:
|
||||||
|
verbosity: detailed
|
||||||
|
|
||||||
|
service:
|
||||||
|
pipelines:
|
||||||
|
traces:
|
||||||
|
receivers: [otlp]
|
||||||
|
exporters: [debug]
|
||||||
|
metrics:
|
||||||
|
receivers: [otlp]
|
||||||
|
exporters: [debug]
|
||||||
|
logs:
|
||||||
|
receivers: [otlp]
|
||||||
|
exporters: [debug]
|
||||||
1
deploy/env/api-server.env.example
vendored
1
deploy/env/api-server.env.example
vendored
@@ -7,6 +7,7 @@ GENARRATIVE_API_PORT=8082
|
|||||||
GENARRATIVE_API_LOG=info,tower_http=info
|
GENARRATIVE_API_LOG=info,tower_http=info
|
||||||
GENARRATIVE_API_LISTEN_BACKLOG=1024
|
GENARRATIVE_API_LISTEN_BACKLOG=1024
|
||||||
GENARRATIVE_API_WORKER_THREADS=4
|
GENARRATIVE_API_WORKER_THREADS=4
|
||||||
|
GENARRATIVE_API_MAX_CONCURRENT_REQUESTS=512
|
||||||
GENARRATIVE_OTEL_ENABLED=false
|
GENARRATIVE_OTEL_ENABLED=false
|
||||||
OTEL_SERVICE_NAME=genarrative-api
|
OTEL_SERVICE_NAME=genarrative-api
|
||||||
OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4318
|
OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4318
|
||||||
|
|||||||
@@ -13,11 +13,15 @@ upstream genarrative_api {
|
|||||||
keepalive 64;
|
keepalive 64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
limit_conn_zone $binary_remote_addr zone=genarrative_api_conn:10m;
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name genarrative.example.com;
|
server_name genarrative.example.com;
|
||||||
access_log /var/log/nginx/genarrative.access.log genarrative_upstream;
|
access_log /var/log/nginx/genarrative.access.log genarrative_upstream;
|
||||||
error_log /var/log/nginx/genarrative.error.log warn;
|
error_log /var/log/nginx/genarrative.error.log warn;
|
||||||
|
limit_conn_status 429;
|
||||||
|
limit_conn_log_level warn;
|
||||||
|
|
||||||
gzip on;
|
gzip on;
|
||||||
gzip_vary on;
|
gzip_vary on;
|
||||||
@@ -43,6 +47,7 @@ server {
|
|||||||
|
|
||||||
location ^~ /admin/api/ {
|
location ^~ /admin/api/ {
|
||||||
default_type application/json;
|
default_type application/json;
|
||||||
|
limit_conn genarrative_api_conn 64;
|
||||||
|
|
||||||
if ($genarrative_maintenance) {
|
if ($genarrative_maintenance) {
|
||||||
return 503 '{"ok":false,"error":{"code":"MAINTENANCE","message":"服务维护中"}}';
|
return 503 '{"ok":false,"error":{"code":"MAINTENANCE","message":"服务维护中"}}';
|
||||||
@@ -83,6 +88,7 @@ server {
|
|||||||
# 临时兼容主站仍在使用的 /api/* HTTP facade;前端完成 SpacetimeDB SDK 迁移后删除。
|
# 临时兼容主站仍在使用的 /api/* HTTP facade;前端完成 SpacetimeDB SDK 迁移后删除。
|
||||||
location ~ ^/api(?:/|$) {
|
location ~ ^/api(?:/|$) {
|
||||||
default_type application/json;
|
default_type application/json;
|
||||||
|
limit_conn genarrative_api_conn 64;
|
||||||
|
|
||||||
if ($genarrative_maintenance) {
|
if ($genarrative_maintenance) {
|
||||||
return 503 '{"ok":false,"error":{"code":"MAINTENANCE","message":"服务维护中"}}';
|
return 503 '{"ok":false,"error":{"code":"MAINTENANCE","message":"服务维护中"}}';
|
||||||
|
|||||||
@@ -11,11 +11,15 @@ upstream genarrative_api {
|
|||||||
keepalive 64;
|
keepalive 64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
limit_conn_zone $binary_remote_addr zone=genarrative_api_conn:10m;
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name genarrative.example.com;
|
server_name genarrative.example.com;
|
||||||
access_log /var/log/nginx/genarrative.access.log genarrative_upstream;
|
access_log /var/log/nginx/genarrative.access.log genarrative_upstream;
|
||||||
error_log /var/log/nginx/genarrative.error.log warn;
|
error_log /var/log/nginx/genarrative.error.log warn;
|
||||||
|
limit_conn_status 429;
|
||||||
|
limit_conn_log_level warn;
|
||||||
|
|
||||||
location /.well-known/acme-challenge/ {
|
location /.well-known/acme-challenge/ {
|
||||||
root /var/www/html;
|
root /var/www/html;
|
||||||
@@ -59,6 +63,7 @@ server {
|
|||||||
|
|
||||||
location ^~ /admin/api/ {
|
location ^~ /admin/api/ {
|
||||||
default_type application/json;
|
default_type application/json;
|
||||||
|
limit_conn genarrative_api_conn 64;
|
||||||
|
|
||||||
if ($genarrative_maintenance) {
|
if ($genarrative_maintenance) {
|
||||||
return 503 '{"ok":false,"error":{"code":"MAINTENANCE","message":"服务维护中"}}';
|
return 503 '{"ok":false,"error":{"code":"MAINTENANCE","message":"服务维护中"}}';
|
||||||
@@ -99,6 +104,7 @@ server {
|
|||||||
# 临时兼容主站仍在使用的 /api/* HTTP facade;前端完成 SpacetimeDB SDK 迁移后删除。
|
# 临时兼容主站仍在使用的 /api/* HTTP facade;前端完成 SpacetimeDB SDK 迁移后删除。
|
||||||
location ~ ^/api(?:/|$) {
|
location ~ ^/api(?:/|$) {
|
||||||
default_type application/json;
|
default_type application/json;
|
||||||
|
limit_conn genarrative_api_conn 64;
|
||||||
|
|
||||||
if ($genarrative_maintenance) {
|
if ($genarrative_maintenance) {
|
||||||
return 503 '{"ok":false,"error":{"code":"MAINTENANCE","message":"服务维护中"}}';
|
return 503 '{"ok":false,"error":{"code":"MAINTENANCE","message":"服务维护中"}}';
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ npm run check:server-rs-ddd
|
|||||||
3. 删除字段、改名、重排字段、改类型或修改字段属性前,必须先询问用户并确认迁移计划。
|
3. 删除字段、改名、重排字段、改类型或修改字段属性前,必须先询问用户并确认迁移计划。
|
||||||
4. Vec 字段不要直接写无法 const 求值的 default;需要默认空集合时优先使用 `Option<Vec<T>>` 加 `#[default(None::<Vec<T>>)]`,业务层归一为空数组。
|
4. Vec 字段不要直接写无法 const 求值的 default;需要默认空集合时优先使用 `Option<Vec<T>>` 加 `#[default(None::<Vec<T>>)]`,业务层归一为空数组。
|
||||||
5. 运行态读表必须按已声明索引访问。只要 table 上存在覆盖查询前缀的 `#[index(...)]` 或主键 / unique accessor,列表、详情、快照组装和计数都先用对应 accessor `.filter(...)` / `.find(...)`,再在内存中处理索引无法覆盖的残余条件;不得用 `.iter().filter(...)` 扫整表替代现成索引。
|
5. 运行态读表必须按已声明索引访问。只要 table 上存在覆盖查询前缀的 `#[index(...)]` 或主键 / unique accessor,列表、详情、快照组装和计数都先用对应 accessor `.filter(...)` / `.find(...)`,再在内存中处理索引无法覆盖的残余条件;不得用 `.iter().filter(...)` 扫整表替代现成索引。
|
||||||
6. 面向公开列表的只读投影优先做成 public view,并由 `api-server` 的 `spacetime-client` 长期订阅后读本地 cache。不要让 HTTP 列表接口每次请求都调用 procedure 重新组装全量列表;需要请求时间窗口的轻量统计可订阅公开统计表后在 `api-server` 本地聚合,需要写入副作用的详情、点赞、游玩记录仍可走 procedure / reducer。
|
6. 面向公开列表的只读投影优先做成 public view / public 读模型表,并由 `api-server` 的 `spacetime-client` 长期订阅后读本地 cache。短期不把作品列表整体交给浏览器前端直接订阅;不要让 HTTP 列表接口每次请求都调用 procedure 重新组装全量列表。需要请求时间窗口的轻量统计可订阅公开统计表后在 `api-server` 本地聚合,需要写入副作用的详情、点赞、游玩记录仍可走 procedure / reducer。中期如要让前端可选直连订阅,只能新增或统一稳定的专用 public read model,例如 `public_work_gallery_entry`,并保持字段、排序键、公开权限和降级语义由后端投影定义;前端不得直接订阅 `puzzle_work_profile`、`custom_world_profile` 等领域源表,也不得自己做 join、聚合或权限逻辑。首屏、排序、字段归一、权限降级和 HTTP fallback 仍由 `api-server` BFF 维持。
|
||||||
7. 多列索引按 SpacetimeDB 绑定生成的元组参数直接传入,例如 `.filter((source_type, profile_id, played_day))`;前缀查询只传前缀元组,例如 `.filter((scope_kind, scope_id.as_str()))`。不要为了绕过类型问题退回整表遍历。
|
7. 多列索引按 SpacetimeDB 绑定生成的元组参数直接传入,例如 `.filter((source_type, profile_id, played_day))`;前缀查询只传前缀元组,例如 `.filter((scope_kind, scope_id.as_str()))`。不要为了绕过类型问题退回整表遍历。
|
||||||
8. procedure result 必须返回 typed snapshot / typed value。`spacetime-client` mapper 不得再通过 `row_json/session_json/work_json/items_json/run_json/event_json/feedback_json: Option<String>` 做跨层 JSON 字符串传输,也不得在 mapper 里反序列化旧 `*JsonRecord` 兼容结构。业务内部持久化字段如 `profile_payload_json`、`levels_json` 等不属于 procedure result 载荷例外,仍按各自表契约处理。
|
8. procedure result 必须返回 typed snapshot / typed value。`spacetime-client` mapper 不得再通过 `row_json/session_json/work_json/items_json/run_json/event_json/feedback_json: Option<String>` 做跨层 JSON 字符串传输,也不得在 mapper 里反序列化旧 `*JsonRecord` 兼容结构。业务内部持久化字段如 `profile_payload_json`、`levels_json` 等不属于 procedure result 载荷例外,仍按各自表契约处理。
|
||||||
9. 修改后运行:
|
9. 修改后运行:
|
||||||
@@ -243,6 +243,7 @@ npm run check:server-rs-ddd
|
|||||||
|
|
||||||
- Rust 结构体:`BigFishCreationSession`
|
- Rust 结构体:`BigFishCreationSession`
|
||||||
- 源码:`server-rs/crates/spacetime-module/src/big_fish/tables.rs`
|
- 源码:`server-rs/crates/spacetime-module/src/big_fish/tables.rs`
|
||||||
|
- 索引:`by_big_fish_session_owner_user_id`、`by_big_fish_session_stage`。公开广场 view 使用 `by_big_fish_session_stage` 读取已发布会话,避免扫整表。
|
||||||
|
|
||||||
### `big_fish_event`
|
### `big_fish_event`
|
||||||
|
|
||||||
@@ -254,6 +255,13 @@ npm run check:server-rs-ddd
|
|||||||
- Rust 结构体:`BigFishRuntimeRun`
|
- Rust 结构体:`BigFishRuntimeRun`
|
||||||
- 源码:`server-rs/crates/spacetime-module/src/big_fish/tables.rs`
|
- 源码:`server-rs/crates/spacetime-module/src/big_fish/tables.rs`
|
||||||
|
|
||||||
|
### SpacetimeDB view:`big_fish_gallery_view`
|
||||||
|
|
||||||
|
- Rust view:`big_fish_gallery_view`
|
||||||
|
- 返回类型:`Vec<BigFishWorkSummarySnapshot>`
|
||||||
|
- 源码:`server-rs/crates/spacetime-module/src/big_fish/session.rs`
|
||||||
|
- 说明:大鱼吃小鱼公开广场列表投影,只从 `Published` creation session 组装公开卡片字段;`api-server` 的 `spacetime-client` 长期订阅 `SELECT * FROM big_fish_gallery_view` 与 `SELECT * FROM public_work_play_daily_stat WHERE source_type = 'big-fish'` 后,从本地 cache 构造 `/api/runtime/big-fish/gallery` 响应。公开列表不再每个 HTTP 请求调用 `list_big_fish_works` procedure;个人作品列表、详情、点赞、游玩记录和 Remix 仍按原有 procedure / reducer 路径处理。
|
||||||
|
|
||||||
### `chapter_progression`
|
### `chapter_progression`
|
||||||
|
|
||||||
- Rust 结构体:`ChapterProgression`
|
- Rust 结构体:`ChapterProgression`
|
||||||
@@ -293,6 +301,7 @@ npm run check:server-rs-ddd
|
|||||||
|
|
||||||
- Rust 结构体:`CustomWorldGalleryEntry`
|
- Rust 结构体:`CustomWorldGalleryEntry`
|
||||||
- 源码:`server-rs/crates/spacetime-module/src/custom_world/mod.rs`
|
- 源码:`server-rs/crates/spacetime-module/src/custom_world/mod.rs`
|
||||||
|
- 作用:自定义世界公开作品列表读模型。`api-server` 的 `spacetime-client` 长期订阅 `SELECT * FROM custom_world_gallery_entry` 与 `SELECT * FROM public_work_play_daily_stat WHERE source_type = 'custom-world'`,`/api/runtime/custom-world-gallery` 从本地 cache 排序并聚合 `recentPlayCount7d`,不再每个 HTTP 请求调用 `list_custom_world_gallery_entries` procedure。旧 procedure 只用于兼容旧库缺少 gallery 读模型行时的一次性同步兜底。
|
||||||
|
|
||||||
### `custom_world_profile`
|
### `custom_world_profile`
|
||||||
|
|
||||||
@@ -339,6 +348,13 @@ npm run check:server-rs-ddd
|
|||||||
- Rust 结构体:`Match3DWorkProfileRow`
|
- Rust 结构体:`Match3DWorkProfileRow`
|
||||||
- 源码:`server-rs/crates/spacetime-module/src/match3d/tables.rs`
|
- 源码:`server-rs/crates/spacetime-module/src/match3d/tables.rs`
|
||||||
|
|
||||||
|
### SpacetimeDB view:`match_3_d_gallery_view`
|
||||||
|
|
||||||
|
- Rust view:`match3d_gallery_view`
|
||||||
|
- 返回类型:`Vec<Match3DGalleryViewRow>`
|
||||||
|
- 源码:`server-rs/crates/spacetime-module/src/match3d/mod.rs`
|
||||||
|
- 说明:抓大鹅公开广场列表投影,只暴露 `publication_status = published` 的作品卡片字段;`api-server` 的 `spacetime-client` 长期订阅 `SELECT * FROM match_3_d_gallery_view` 与 `SELECT * FROM public_work_play_daily_stat WHERE source_type = 'match3d'` 后,从本地 cache 构造 `/api/runtime/match3d/gallery` 响应。公开列表不再每个 HTTP 请求调用 `list_match3d_works` procedure;个人作品列表、详情、发布、点赞、游玩记录和 Remix 仍按原有 procedure / reducer 路径处理。
|
||||||
|
|
||||||
### `npc_state`
|
### `npc_state`
|
||||||
|
|
||||||
- Rust 结构体:`NpcState`
|
- Rust 结构体:`NpcState`
|
||||||
@@ -465,12 +481,54 @@ npm run check:server-rs-ddd
|
|||||||
- Rust 结构体:`PuzzleWorkProfileRow`
|
- Rust 结构体:`PuzzleWorkProfileRow`
|
||||||
- 源码:`server-rs/crates/spacetime-module/src/puzzle.rs`
|
- 源码:`server-rs/crates/spacetime-module/src/puzzle.rs`
|
||||||
|
|
||||||
### `puzzle_gallery_view`
|
### SpacetimeDB view:`puzzle_gallery_view`
|
||||||
|
|
||||||
- Rust view:`puzzle_gallery_view`
|
- Rust view:`puzzle_gallery_view`
|
||||||
- 返回类型:`Vec<PuzzleWorkProfile>`
|
- 返回类型:`Vec<PuzzleWorkProfile>`
|
||||||
- 源码:`server-rs/crates/spacetime-module/src/puzzle.rs`
|
- 源码:`server-rs/crates/spacetime-module/src/puzzle.rs`
|
||||||
- 说明:拼图广场公开列表投影,只暴露 `publication_status = Published` 的作品;`api-server` 的 `spacetime-client` 长期订阅 `SELECT * FROM puzzle_gallery_view` 与 `SELECT * FROM public_work_play_daily_stat WHERE source_type = 'puzzle'` 后,从本地 cache 构造 `/api/runtime/puzzle/gallery` 响应,并在本地按当前请求时间聚合 `recentPlayCount7d`,不再每个 HTTP 请求调用 `list_puzzle_gallery` procedure。
|
- 说明:拼图广场公开详情兼容投影,只暴露 `publication_status = Published` 的作品,但返回完整 `PuzzleWorkProfile`,包含 levels / anchor_pack 等详情级字段;公开列表主路径不再订阅该 view。
|
||||||
|
|
||||||
|
### SpacetimeDB view:`puzzle_gallery_card_view`
|
||||||
|
|
||||||
|
- Rust view:`puzzle_gallery_card_view`
|
||||||
|
- 返回类型:`Vec<PuzzleGalleryCardViewRow>`
|
||||||
|
- 源码:`server-rs/crates/spacetime-module/src/puzzle.rs`
|
||||||
|
- 说明:拼图广场公开列表卡片投影,只暴露前端列表卡片需要的公开字段,不携带 levels / anchor_pack 等详情级载荷;`api-server` 的 `spacetime-client` 长期订阅 `SELECT * FROM puzzle_gallery_card_view` 与 `SELECT * FROM public_work_play_daily_stat WHERE source_type = 'puzzle'` 后,从本地 cache 构造 `/api/runtime/puzzle/gallery` 响应,并在本地按当前请求时间聚合 `recentPlayCount7d`,不再每个 HTTP 请求调用 `list_puzzle_gallery` procedure。
|
||||||
|
|
||||||
|
### 拼图公开列表 HTTP 窗口缓存
|
||||||
|
|
||||||
|
- 接口:`GET /api/runtime/puzzle/gallery`
|
||||||
|
- 响应契约:保留 `items` 字段兼容旧前端;当前 `items` 只返回前 10 个完整卡片,新增 `previewRefs` 返回后 10 个 `workId/profileId` 引用,并返回 `hasMore`、`nextCursor` 与 `totalCount`。
|
||||||
|
- 缓存策略:`api-server` 在 `PuzzleGalleryCache` 中缓存最终 `PuzzleGalleryResponse` 的预序列化 data JSON。缓存 miss / 过期时单飞重建,避免并发请求重复排序、映射、DTO 深拷贝和 `serde_json::Value` 树构造;开启响应 envelope 时只按请求拼接轻量 meta,缓存短 TTL 刷新 `recentPlayCount7d`,后台 cleanup task 周期清理超过最大空闲窗口的旧响应。OTLP 通过 `genarrative.puzzle_gallery.cache.*`、`genarrative.spacetime.read.*`、`genarrative.http.server.response_bodies.in_flight` 和 `genarrative.http.server.request_permits.available` 区分缓存重建、SpacetimeDB 本地订阅读、响应 body 生命周期和 HTTP 背压状态。
|
||||||
|
- 详情路径:公开详情、点赞、游玩记录和 Remix 仍按原有 procedure / reducer 路径处理;前端拿到 `previewRefs` 后如果需要展开更多内容,应优先使用后续列表窗口能力或详情 cache,不要把自动详情预取变成新的 procedure 热点。
|
||||||
|
|
||||||
|
### api-server 长期订阅读模型
|
||||||
|
|
||||||
|
`spacetime-client` 建立每个池连接时会等待下列订阅初始同步:
|
||||||
|
|
||||||
|
- `SELECT * FROM puzzle_gallery_card_view`
|
||||||
|
- `SELECT * FROM custom_world_gallery_entry`
|
||||||
|
- `SELECT * FROM match_3_d_gallery_view`
|
||||||
|
- `SELECT * FROM square_hole_gallery_view`
|
||||||
|
- `SELECT * FROM visual_novel_gallery_view`
|
||||||
|
- `SELECT * FROM big_fish_gallery_view`
|
||||||
|
|
||||||
|
下列订阅用于统计或配置缓存,订阅失败不会让公开列表连接整体不可用,调用方保留兼容兜底:
|
||||||
|
|
||||||
|
- `SELECT * FROM public_work_play_daily_stat WHERE source_type = 'puzzle'`
|
||||||
|
- `SELECT * FROM public_work_play_daily_stat WHERE source_type = 'custom-world'`
|
||||||
|
- `SELECT * FROM public_work_play_daily_stat WHERE source_type = 'match3d'`
|
||||||
|
- `SELECT * FROM public_work_play_daily_stat WHERE source_type = 'square-hole'`
|
||||||
|
- `SELECT * FROM public_work_play_daily_stat WHERE source_type = 'visual-novel'`
|
||||||
|
- `SELECT * FROM public_work_play_daily_stat WHERE source_type = 'big-fish'`
|
||||||
|
- `SELECT * FROM creation_entry_config`
|
||||||
|
- `SELECT * FROM creation_entry_type_config`
|
||||||
|
|
||||||
|
拼图、自定义世界、抓大鹅、方洞挑战、视觉小说和大鱼吃小鱼的公开列表 HTTP 路由都从订阅 cache 读取公开 read model / view。各玩法的个人作品列表、详情、发布、点赞、游玩记录、Remix 和其它需要鉴权或写入副作用的路径继续走 procedure / reducer;不要为了公开列表性能把这些 owner-specific 或 mutation 语义混进 public view。
|
||||||
|
|
||||||
|
`GET /api/creation-entry/config` 和入口熔断优先从订阅 cache 读取创作入口配置;cache 缺失时使用最近一次成功读取的内存快照,再兜底调用 `get_creation_entry_config` procedure 完成空库种子或旧库兼容。
|
||||||
|
|
||||||
|
未来可选:若发现页、推荐流和各玩法广场需要统一给浏览器前端直接订阅公开作品列表,只新增 / 统一专用 public read model,例如 `public_work_gallery_entry`。该 read model 必须是后端投影后的公开作品卡片契约,覆盖作品类型、公开作品号、标题、摘要、封面、作者展示名、排序键、公开统计和入口开关后的可见性,不暴露玩法领域源表 row shape。前端可选择订阅这个稳定投影来减少 HTTP 拉取,但不能订阅 `puzzle_work_profile`、`custom_world_profile` 等源表后自行拼装列表;BFF 仍保留首屏、SEO / 分享、旧客户端、订阅失败和灰度期间的 HTTP fallback。
|
||||||
|
|
||||||
### `quest_log`
|
### `quest_log`
|
||||||
|
|
||||||
@@ -517,6 +575,13 @@ npm run check:server-rs-ddd
|
|||||||
- Rust 结构体:`SquareHoleWorkProfileRow`
|
- Rust 结构体:`SquareHoleWorkProfileRow`
|
||||||
- 源码:`server-rs/crates/spacetime-module/src/square_hole/tables.rs`
|
- 源码:`server-rs/crates/spacetime-module/src/square_hole/tables.rs`
|
||||||
|
|
||||||
|
### SpacetimeDB view:`square_hole_gallery_view`
|
||||||
|
|
||||||
|
- Rust view:`square_hole_gallery_view`
|
||||||
|
- 返回类型:`Vec<SquareHoleGalleryViewRow>`
|
||||||
|
- 源码:`server-rs/crates/spacetime-module/src/square_hole/mod.rs`
|
||||||
|
- 说明:方洞挑战公开广场列表投影,只暴露 `publication_status = published` 的作品卡片字段;`api-server` 的 `spacetime-client` 长期订阅 `SELECT * FROM square_hole_gallery_view` 与 `SELECT * FROM public_work_play_daily_stat WHERE source_type = 'square-hole'` 后,从本地 cache 构造 `/api/runtime/square-hole/gallery` 响应。公开列表不再每个 HTTP 请求调用 `list_square_hole_works` procedure;个人作品列表、详情、发布、点赞、游玩记录和 Remix 仍按原有 procedure / reducer 路径处理。
|
||||||
|
|
||||||
### `story_event`
|
### `story_event`
|
||||||
|
|
||||||
- Rust 结构体:`StoryEvent`
|
- Rust 结构体:`StoryEvent`
|
||||||
@@ -581,3 +646,10 @@ npm run check:server-rs-ddd
|
|||||||
|
|
||||||
- Rust 结构体:`VisualNovelWorkProfileRow`
|
- Rust 结构体:`VisualNovelWorkProfileRow`
|
||||||
- 源码:`server-rs/crates/spacetime-module/src/visual_novel.rs`
|
- 源码:`server-rs/crates/spacetime-module/src/visual_novel.rs`
|
||||||
|
|
||||||
|
### SpacetimeDB view:`visual_novel_gallery_view`
|
||||||
|
|
||||||
|
- Rust view:`visual_novel_gallery_view`
|
||||||
|
- 返回类型:`Vec<VisualNovelGalleryViewRow>`
|
||||||
|
- 源码:`server-rs/crates/spacetime-module/src/visual_novel.rs`
|
||||||
|
- 说明:视觉小说公开广场列表投影,只暴露 `publication_status = published` 的作品卡片字段,不把完整 `draft` 暴露给公开列表订阅;`api-server` 的 `spacetime-client` 长期订阅 `SELECT * FROM visual_novel_gallery_view` 与 `SELECT * FROM public_work_play_daily_stat WHERE source_type = 'visual-novel'` 后,从本地 cache 构造 `/api/runtime/visual-novel/gallery` 响应。公开列表不再每个 HTTP 请求调用 `list_visual_novel_works` procedure;个人历史、详情、运行态和发布仍按原有 procedure / reducer 路径处理。
|
||||||
|
|||||||
@@ -154,11 +154,27 @@ Jenkins 按 web / api / Spacetime module / build / deploy / publish 拆分
|
|||||||
50 HTTP req/s 首版压测优化口径:
|
50 HTTP req/s 首版压测优化口径:
|
||||||
|
|
||||||
- `api-server` 生产模板默认 `GENARRATIVE_API_LISTEN_BACKLOG=1024`、`GENARRATIVE_API_WORKER_THREADS=4`;本地未设置 worker threads 时继续使用 Tokio 默认值。
|
- `api-server` 生产模板默认 `GENARRATIVE_API_LISTEN_BACKLOG=1024`、`GENARRATIVE_API_WORKER_THREADS=4`;本地未设置 worker threads 时继续使用 Tokio 默认值。
|
||||||
|
- `GENARRATIVE_API_MAX_CONCURRENT_REQUESTS=512` 开启应用内 HTTP 并发背压,超过并发许可时直接返回 `429 Too Many Requests` 和 `Retry-After: 1`,`/healthz` 不受该限制。该值不是 RPS 限速;如果压测中 429 上升但内存和 p95 收敛,说明背压正在保护进程,需要结合真实容量调阈值或在 Nginx 前置限流。直连 `api-server` 的极高 RPS 压测若出现 `connection refused`,通常已经打到 TCP 监听 / accept 层,应同时检查 backlog、Nginx upstream keepalive 和前置限流。
|
||||||
- `genarrative-api.service` 设置 `LimitNOFILE=65535`、`TasksMax=2048`;上线后用 `systemctl show genarrative-api.service -p LimitNOFILE -p TasksMax` 和 `cat /proc/$(pidof api-server)/limits` 核对。
|
- `genarrative-api.service` 设置 `LimitNOFILE=65535`、`TasksMax=2048`;上线后用 `systemctl show genarrative-api.service -p LimitNOFILE -p TasksMax` 和 `cat /proc/$(pidof api-server)/limits` 核对。
|
||||||
- Nginx `/api/` 与 `/admin/api/` 通过 `genarrative_api` upstream 代理到 `127.0.0.1:8082`,upstream keepalive 为 64;压测时看 `/var/log/nginx/genarrative.access.log` 中的 `request_time`、`upstream_connect_time`、`upstream_header_time`、`upstream_response_time`、`upstream_status`、`request_id`。
|
- Nginx `/api/` 与 `/admin/api/` 通过 `genarrative_api` upstream 代理到 `127.0.0.1:8082`,upstream keepalive 为 64;压测时看 `/var/log/nginx/genarrative.access.log` 中的 `request_time`、`upstream_connect_time`、`upstream_header_time`、`upstream_response_time`、`upstream_status`、`request_id`。
|
||||||
- 作品列表 K6 脚本一次 iteration 默认请求两个公开接口,因此约 50 HTTP req/s 的目标命令使用 `SCENARIO=spike START_RPS=5 PEAK_RPS=25 HOLD=60s END_RPS=5 DETAIL_RATIO=0 npm run loadtest:k6:works`。
|
- 作品列表 K6 脚本一次 iteration 默认请求两个公开接口,因此约 50 HTTP req/s 的目标命令使用 `SCENARIO=spike START_RPS=5 PEAK_RPS=25 HOLD=60s END_RPS=5 DETAIL_RATIO=0 npm run loadtest:k6:works`。
|
||||||
|
- 作品列表短期继续由 `api-server` / BFF 订阅 SpacetimeDB 公开 read model 后读本地 cache,不让浏览器前端直接订阅完整列表;未来如新增 `public_work_gallery_entry` 等专用公开作品列表 read model,前端只可订阅稳定、低基数、公开的专用投影,禁止订阅 `puzzle_work_profile`、`custom_world_profile` 等玩法源表后自行 join、聚合或判断权限。前端直订阅落地前必须先补齐权限、字段契约、排序 / 分页、埋点和 BFF 回退策略。
|
||||||
- 50 HTTP req/s 验收目标为 `http_req_failed < 1%`、`p95 < 2s`、`dropped_iterations = 0`,同时压测窗口内 Nginx 无新增 502。
|
- 50 HTTP req/s 验收目标为 `http_req_failed < 1%`、`p95 < 2s`、`dropped_iterations = 0`,同时压测窗口内 Nginx 无新增 502。
|
||||||
|
|
||||||
|
容器化压测与隔离部署方案单独放在 `deploy/container/`,用于本机或预发模拟 Linux release + Nginx + OTLP Collector 拓扑,不替换当前生产 `systemd + Nginx + Jenkins` 发布路径:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run container:init
|
||||||
|
npm run container:config
|
||||||
|
npm run container:build
|
||||||
|
npm run container:up
|
||||||
|
npm run container:k6
|
||||||
|
npm run container:down
|
||||||
|
```
|
||||||
|
|
||||||
|
容器方案默认暴露 `http://127.0.0.1:18080`,`api-server` 在容器内监听 `0.0.0.0:8082`,Nginx 通过 `api-server:8082` upstream 反代 `/api/` 和 `/admin/api/`。SpacetimeDB 默认仍连接宿主机 `http://host.docker.internal:3101`,真实库名、token 和外部服务密钥只写本地 `deploy/container/api-server.env`,不提交 Git。完整拓扑、端口、k6 参数和 OTLP debug exporter 使用方法见 `deploy/container/README.md`。
|
||||||
|
`npm run container:config` 默认只做 quiet 校验,避免把本地 env 中的 token 展开到终端;确需排查完整 compose 时再传 `-- --print`。
|
||||||
|
|
||||||
OpenTelemetry 现阶段可选 OTLP traces / metrics / logs,但本地日志与 Nginx 文件日志仍保留:
|
OpenTelemetry 现阶段可选 OTLP traces / metrics / logs,但本地日志与 Nginx 文件日志仍保留:
|
||||||
|
|
||||||
- 默认 `GENARRATIVE_OTEL_ENABLED=false`,未开启时 api-server 不依赖 Collector。
|
- 默认 `GENARRATIVE_OTEL_ENABLED=false`,未开启时 api-server 不依赖 Collector。
|
||||||
@@ -167,6 +183,10 @@ OpenTelemetry 现阶段可选 OTLP traces / metrics / logs,但本地日志与
|
|||||||
- api-server 当前发 OTLP HTTP,`OTEL_EXPORTER_OTLP_ENDPOINT` 指向 Collector HTTP base endpoint;不要改到 gRPC `4317` 或 Rider 端口,Rider 由 Collector 通过 `RIDER_OTLP_GRPC_ENDPOINT` 转发。
|
- api-server 当前发 OTLP HTTP,`OTEL_EXPORTER_OTLP_ENDPOINT` 指向 Collector HTTP base endpoint;不要改到 gRPC `4317` 或 Rider 端口,Rider 由 Collector 通过 `RIDER_OTLP_GRPC_ENDPOINT` 转发。
|
||||||
- 应用日志仍通过 `journalctl -u genarrative-api.service` 查看,Nginx 日志仍写文件;日志等级继续用 `GENARRATIVE_API_LOG` / `RUST_LOG` 控制,例如 `info,tower_http=info,spacetime_client=info`。
|
- 应用日志仍通过 `journalctl -u genarrative-api.service` 查看,Nginx 日志仍写文件;日志等级继续用 `GENARRATIVE_API_LOG` / `RUST_LOG` 控制,例如 `info,tower_http=info,spacetime_client=info`。
|
||||||
- debug exporter / Rider 转发都会同时接收 traces、metrics 和 logs。
|
- debug exporter / Rider 转发都会同时接收 traces、metrics 和 logs。
|
||||||
|
- api-server 会随 metrics 发送进程级指标:`process.memory.usage`、`process.memory.virtual`、`process.thread.count`、`genarrative.process.memory.private`;Windows 额外发送 `process.windows.handle.count`,Linux 额外发送 `process.unix.file_descriptor.count`。这些指标只描述当前进程,不携带请求、用户或作品 label。
|
||||||
|
- HTTP 运行态补充发送 `genarrative.http.server.response_bodies.in_flight` 与 `genarrative.http.server.request_permits.available`,用于区分业务 handler / 背压 permit 是否仍被占用;拼图广场热点缓存补充发送 `genarrative.puzzle_gallery.cache.*` 指标,记录命中、未命中、重建耗时和预序列化 data JSON 字节数。
|
||||||
|
- SpacetimeDB 观测分为两类:procedure / reducer 调用继续用 `genarrative.spacetime.procedure.*`,订阅本地 cache 读使用 `genarrative.spacetime.read.*`。`read=list_puzzle_gallery` 表示拼图广场当前从 `puzzle_gallery_card_view` 本地 cache 读取,不再每个 HTTP 请求调用 `list_puzzle_gallery` procedure。
|
||||||
|
- 本地 Windows 直连压测的内存高水位要结合 K6 VU / 连接数解释。250 RPS 下过高 `PREALLOCATED_VUS` 可能让 300 个本地 Established 连接把 `api-server` private memory 瞬时推到 GB 级,且 `/healthz` 小响应也能复现;若压测结束后回落、`response_bodies.in_flight` 和背压 permit 未显示业务积压,应优先按连接 / 发送链路高水位处理,而不是判断为 SpacetimeDB 或 JSON 缓存泄漏。
|
||||||
- Rider 的 Logs 面板只展示 log event 自身字段,不会自动展开父 span 的全部 attributes;请求完成日志会直接带 `request_id`、`http.request.method`、`http.route`、`url.scheme`、`url.path`、`http.response.status_code`、`status_class`、`latency_ms` 和 `slow_request`,完整链路继续到 Traces 面板按 trace/span 查看。
|
- Rider 的 Logs 面板只展示 log event 自身字段,不会自动展开父 span 的全部 attributes;请求完成日志会直接带 `request_id`、`http.request.method`、`http.route`、`url.scheme`、`url.path`、`http.response.status_code`、`status_class`、`latency_ms` 和 `slow_request`,完整链路继续到 Traces 面板按 trace/span 查看。
|
||||||
- 指标 label 只允许低基数字段:HTTP 使用 `method`、`route`、`status_class`,SpacetimeDB 调用使用 `procedure`、`status_class`;`request_id` 只进入 trace/log attribute,不进入 metric label。
|
- 指标 label 只允许低基数字段:HTTP 使用 `method`、`route`、`status_class`,SpacetimeDB 调用使用 `procedure`、`status_class`;`request_id` 只进入 trace/log attribute,不进入 metric label。
|
||||||
|
|
||||||
|
|||||||
11
package.json
11
package.json
@@ -17,6 +17,7 @@
|
|||||||
"admin-web:preview": "npm --prefix apps/admin-web run preview --",
|
"admin-web:preview": "npm --prefix apps/admin-web run preview --",
|
||||||
"spacetime:generate": "node scripts/generate-spacetime-bindings.mjs",
|
"spacetime:generate": "node scripts/generate-spacetime-bindings.mjs",
|
||||||
"check:api-server-env": "node scripts/check-api-server-env.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",
|
"deploy:rust:remote": "node scripts/run-bash-script.mjs scripts/deploy-rust-remote.sh",
|
||||||
"build:production-release": "node scripts/run-bash-script.mjs scripts/build-production-release.sh",
|
"build:production-release": "node scripts/run-bash-script.mjs scripts/build-production-release.sh",
|
||||||
"build:rust:ubuntu": "node scripts/run-bash-script.mjs scripts/deploy-rust-remote.sh",
|
"build:rust:ubuntu": "node scripts/run-bash-script.mjs scripts/deploy-rust-remote.sh",
|
||||||
@@ -31,7 +32,7 @@
|
|||||||
"check:visual-novel-vn11": "node scripts/check-visual-novel-vn11-negative-scan.mjs",
|
"check:visual-novel-vn11": "node scripts/check-visual-novel-vn11-negative-scan.mjs",
|
||||||
"check:visual-novel-vn12": "node scripts/check-visual-novel-vn12-acceptance.mjs",
|
"check:visual-novel-vn12": "node scripts/check-visual-novel-vn12-acceptance.mjs",
|
||||||
"check:wechat-miniprogram-auth": "node scripts/check-wechat-miniprogram-auth-smoke.mjs",
|
"check:wechat-miniprogram-auth": "node scripts/check-wechat-miniprogram-auth-smoke.mjs",
|
||||||
"check:server-rs-ddd": "npm run check:spacetime-schema && node scripts/check-server-rs-ddd-boundaries.mjs",
|
"check:server-rs-ddd": "npm run check:spacetime-schema && npm run check:spacetime-runtime-access && node scripts/check-server-rs-ddd-boundaries.mjs",
|
||||||
"lint:eslint": "eslint . --ext .ts,.tsx,.js,.mjs,.cjs --max-warnings 0",
|
"lint:eslint": "eslint . --ext .ts,.tsx,.js,.mjs,.cjs --max-warnings 0",
|
||||||
"lint:guardrails": "npm run lint:eslint",
|
"lint:guardrails": "npm run lint:eslint",
|
||||||
"typecheck": "tsc -p tsconfig.typecheck-guardrails.json --noEmit",
|
"typecheck": "tsc -p tsconfig.typecheck-guardrails.json --noEmit",
|
||||||
@@ -44,6 +45,14 @@
|
|||||||
"test:watch": "vitest",
|
"test:watch": "vitest",
|
||||||
"loadtest:extract-works": "node scripts/loadtest/extract-works-list-data.mjs",
|
"loadtest:extract-works": "node scripts/loadtest/extract-works-list-data.mjs",
|
||||||
"loadtest:k6:works": "k6 run scripts/loadtest/k6-works-list.js",
|
"loadtest:k6:works": "k6 run scripts/loadtest/k6-works-list.js",
|
||||||
|
"container:init": "node scripts/container-compose.mjs init",
|
||||||
|
"container:build": "node scripts/container-compose.mjs build",
|
||||||
|
"container:up": "node scripts/container-compose.mjs up",
|
||||||
|
"container:down": "node scripts/container-compose.mjs down",
|
||||||
|
"container:logs": "node scripts/container-compose.mjs logs",
|
||||||
|
"container:ps": "node scripts/container-compose.mjs ps",
|
||||||
|
"container:config": "node scripts/container-compose.mjs config",
|
||||||
|
"container:k6": "node scripts/container-compose.mjs k6",
|
||||||
"check": "npm run lint && npm run test && npm run build && npm run check:content",
|
"check": "npm run lint && npm run test && npm run build && npm run check:content",
|
||||||
"check:data": "node scripts/run-tsx.cjs scripts/validate-content.ts",
|
"check:data": "node scripts/run-tsx.cjs scripts/validate-content.ts",
|
||||||
"check:overrides": "node scripts/run-tsx.cjs scripts/validate-overrides.ts",
|
"check:overrides": "node scripts/run-tsx.cjs scripts/validate-overrides.ts",
|
||||||
|
|||||||
@@ -42,6 +42,19 @@ export interface PuzzleWorksResponse {
|
|||||||
items: PuzzleWorkSummary[];
|
items: PuzzleWorkSummary[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PuzzleGalleryWorkRef {
|
||||||
|
workId: string;
|
||||||
|
profileId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PuzzleGalleryResponse {
|
||||||
|
items: PuzzleWorkSummary[];
|
||||||
|
previewRefs?: PuzzleGalleryWorkRef[];
|
||||||
|
hasMore?: boolean;
|
||||||
|
nextCursor?: string | null;
|
||||||
|
totalCount?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface PuzzleWorkDetailResponse {
|
export interface PuzzleWorkDetailResponse {
|
||||||
item: PuzzleWorkProfile;
|
item: PuzzleWorkProfile;
|
||||||
}
|
}
|
||||||
|
|||||||
221
scripts/check-spacetime-runtime-access.mjs
Normal file
221
scripts/check-spacetime-runtime-access.mjs
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
|
const repoRoot = process.cwd();
|
||||||
|
|
||||||
|
function readUtf8(relativePath) {
|
||||||
|
const absolute = path.join(repoRoot, relativePath);
|
||||||
|
if (!fs.existsSync(absolute)) {
|
||||||
|
failures.push(`${relativePath}: 文件不存在,无法执行 SpacetimeDB runtime access 检查`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return fs.readFileSync(absolute, 'utf8');
|
||||||
|
}
|
||||||
|
|
||||||
|
const forbiddenSnippets = [
|
||||||
|
{
|
||||||
|
file: 'server-rs/crates/spacetime-module/src/puzzle.rs',
|
||||||
|
snippet: '.puzzle_work_profile()\n .iter()\n .filter(|row| row.owner_user_id == input.owner_user_id)',
|
||||||
|
reason: 'puzzle_work_profile 已有 by_puzzle_work_owner_user_id 索引',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: 'server-rs/crates/spacetime-module/src/puzzle.rs',
|
||||||
|
snippet: '.puzzle_work_profile()\n .iter()\n .filter(|row| row.publication_status == PuzzlePublicationStatus::Published)',
|
||||||
|
reason: 'puzzle_work_profile 已有 by_puzzle_work_publication_status 索引',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: 'server-rs/crates/spacetime-module/src/puzzle.rs',
|
||||||
|
snippet: '.puzzle_leaderboard_entry()\n .iter()\n .filter(|row| row.profile_id == profile_id && row.grid_size == grid_size)',
|
||||||
|
reason: 'puzzle_leaderboard_entry 已有 by_puzzle_leaderboard_profile_grid 索引',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: 'server-rs/crates/spacetime-module/src/match3d/mod.rs',
|
||||||
|
snippet: '.match3d_work_profile()\n .iter()\n .filter(|row| {',
|
||||||
|
reason: 'match3d_work_profile 已有 owner/status 索引,列表不应整表过滤',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: 'server-rs/crates/spacetime-module/src/visual_novel.rs',
|
||||||
|
snippet: '.visual_novel_work_profile()\n .iter()\n .filter(|row| {',
|
||||||
|
reason: 'visual_novel_work_profile 已有 owner/status 索引,列表不应整表过滤',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: 'server-rs/crates/spacetime-module/src/asset_metadata/objects.rs',
|
||||||
|
snippet: '.asset_object()\n .iter()\n .find(|row| row.bucket == input.bucket && row.object_key == input.object_key)',
|
||||||
|
reason: 'asset_object 已有 by_bucket_object_key 索引',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: 'server-rs/crates/spacetime-module/src/asset_metadata/objects.rs',
|
||||||
|
snippet: '.asset_object()\n .iter()\n .filter(|row| row.asset_kind == asset_kind)',
|
||||||
|
reason: 'asset_object 已有 asset_kind 索引',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: 'server-rs/crates/spacetime-module/src/ai/stages.rs',
|
||||||
|
snippet: '.ai_task_stage()\n .iter()\n .filter(|row| row.task_id == task_id)',
|
||||||
|
reason: 'ai_task_stage 已有 by_ai_task_stage_task_id 索引',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: 'server-rs/crates/spacetime-module/src/ai/stages.rs',
|
||||||
|
snippet: '.ai_text_chunk()\n .iter()\n .filter(|row| row.task_id == task_id && row.stage_kind == stage_kind)',
|
||||||
|
reason: 'ai_text_chunk 已有 by_ai_text_chunk_task_id / by_ai_text_chunk_task_stage_sequence 索引',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: 'server-rs/crates/spacetime-module/src/ai/snapshots.rs',
|
||||||
|
snippet: '.ai_task_stage()\n .iter()\n .filter(|stage| stage.task_id == row.task_id)',
|
||||||
|
reason: 'ai_task_stage 快照组装应使用 by_ai_task_stage_task_id 索引',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: 'server-rs/crates/spacetime-module/src/ai/snapshots.rs',
|
||||||
|
snippet: '.ai_result_reference()\n .iter()\n .filter(|reference| reference.task_id == row.task_id)',
|
||||||
|
reason: 'ai_result_reference 快照组装应使用 by_ai_result_reference_task_id 索引',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: 'server-rs/crates/spacetime-module/src/runtime/profile.rs',
|
||||||
|
snippet: '.profile_save_archive()\n .iter()\n .filter(|row| row.user_id == validated_input.user_id)',
|
||||||
|
reason: 'profile_save_archive 已有 by_profile_save_archive_user_id 索引',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: 'server-rs/crates/spacetime-module/src/runtime/profile.rs',
|
||||||
|
snippet: '.profile_played_world()\n .iter()\n .filter(|row| row.user_id == validated_input.user_id)',
|
||||||
|
reason: 'profile_played_world 已有 by_profile_played_world_user_id 索引',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: 'server-rs/crates/spacetime-module/src/runtime/profile.rs',
|
||||||
|
snippet: '.profile_wallet_ledger()\n .iter()\n .filter(|row| row.user_id == validated_input.user_id)',
|
||||||
|
reason: 'profile_wallet_ledger 已有 by_profile_wallet_ledger_user_id 索引',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: 'server-rs/crates/spacetime-module/src/runtime/profile.rs',
|
||||||
|
snippet: '.profile_referral_relation()\n .iter()\n .filter(|row| row.inviter_user_id == user_id)',
|
||||||
|
reason: 'profile_referral_relation 已有 by_profile_referral_inviter_user_id 索引',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: 'server-rs/crates/spacetime-module/src/runtime/profile.rs',
|
||||||
|
snippet: '.profile_recharge_order()\n .iter()\n .filter(|row| row.user_id == user_id)',
|
||||||
|
reason: 'profile_recharge_order 已有 by_profile_recharge_order_user_id 索引',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: 'server-rs/crates/spacetime-module/src/runtime/profile.rs',
|
||||||
|
snippet: '.tracking_daily_stat()\n .iter()\n .filter(|row| {',
|
||||||
|
reason: 'tracking_daily_stat 已有 by_tracking_daily_stat_scope_day / event_day 索引,analytics 查询不应整表过滤',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: 'server-rs/crates/spacetime-module/src/custom_world/mod.rs',
|
||||||
|
snippet: '.custom_world_profile()\n .iter()\n .find(|row| {',
|
||||||
|
reason: 'custom_world_profile owner 维度已有 by_custom_world_profile_owner_user_id 索引',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: 'server-rs/crates/spacetime-module/src/custom_world/mod.rs',
|
||||||
|
snippet: '.custom_world_profile()\n .iter()\n .filter(|profile| {',
|
||||||
|
reason: 'custom_world_profile Published 同步已有 by_custom_world_profile_publication_status 索引',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const procedureResultFiles = [
|
||||||
|
'server-rs/crates/module-puzzle/src/application.rs',
|
||||||
|
'server-rs/crates/module-big-fish/src/domain.rs',
|
||||||
|
'server-rs/crates/spacetime-module/src/match3d/types.rs',
|
||||||
|
'server-rs/crates/spacetime-module/src/square_hole/types.rs',
|
||||||
|
'server-rs/crates/spacetime-module/src/visual_novel.rs',
|
||||||
|
'server-rs/crates/spacetime-module/src/bark_battle/types.rs',
|
||||||
|
];
|
||||||
|
|
||||||
|
const mapperCompatibilityFiles = [
|
||||||
|
'server-rs/crates/spacetime-client/src/mapper.rs',
|
||||||
|
'server-rs/crates/spacetime-client/src/lib.rs',
|
||||||
|
];
|
||||||
|
|
||||||
|
const bigFishRuntimeFiles = [
|
||||||
|
'server-rs/crates/module-big-fish/src/commands.rs',
|
||||||
|
'server-rs/crates/spacetime-module/src/big_fish/runtime.rs',
|
||||||
|
'server-rs/crates/spacetime-module/src/big_fish/session.rs',
|
||||||
|
];
|
||||||
|
|
||||||
|
const legacyMapperPatterns = [
|
||||||
|
{
|
||||||
|
pattern: /\b[A-Za-z0-9_]*JsonRecord\b/u,
|
||||||
|
reason: 'spacetime-client mapper 不应保留旧 ProcedureResult JSON 兼容 Record',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /\bCompatibleBigFish[A-Za-z0-9_]*\b/u,
|
||||||
|
reason: 'spacetime-client mapper 不应保留 BigFish 旧 JSON 兼容结构',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /\bmap_[A-Za-z0-9_]*_json\b/u,
|
||||||
|
reason: 'spacetime-client mapper 不应再通过 map_*_json 反序列化 procedure payload',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /serde_json::from_str::<[A-Za-z0-9_:]*JsonRecord/u,
|
||||||
|
reason: 'spacetime-client mapper 不应把 procedure result 再反序列化为 JsonRecord',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: /\b(?:items|run|work|session|event|feedback)_json:\s*Some\(/u,
|
||||||
|
reason: 'mapper 测试与兼容路径不应再构造旧 procedure JSON 字符串字段',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const typedProcedurePayloadFieldPattern =
|
||||||
|
/\b(?:row|session|work|item|items|run|event|feedback)_json:\s*Option<String>/gu;
|
||||||
|
|
||||||
|
const failures = [];
|
||||||
|
|
||||||
|
for (const rule of forbiddenSnippets) {
|
||||||
|
const content = readUtf8(rule.file);
|
||||||
|
if (content === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (content.includes(rule.snippet)) {
|
||||||
|
failures.push(`${rule.file}: ${rule.reason}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const file of procedureResultFiles) {
|
||||||
|
const content = readUtf8(file);
|
||||||
|
if (content === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const resultBlocks = content.match(/pub struct [A-Za-z0-9_]*ProcedureResult\s*\{[\s\S]*?\n\}/g) ?? [];
|
||||||
|
for (const block of resultBlocks) {
|
||||||
|
const jsonFields = block.match(typedProcedurePayloadFieldPattern);
|
||||||
|
if (jsonFields?.length) {
|
||||||
|
const name = block.match(/pub struct ([A-Za-z0-9_]+)/)?.[1] ?? 'ProcedureResult';
|
||||||
|
failures.push(`${file}: ${name} 仍通过 ${jsonFields.join(', ')} 跨层返回 JSON 字符串`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const file of mapperCompatibilityFiles) {
|
||||||
|
const content = readUtf8(file);
|
||||||
|
if (content === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const rule of legacyMapperPatterns) {
|
||||||
|
if (rule.pattern.test(content)) {
|
||||||
|
failures.push(`${file}: ${rule.reason}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const file of bigFishRuntimeFiles) {
|
||||||
|
const content = readUtf8(file);
|
||||||
|
if (content === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const resultBlocks = content.match(/pub struct [A-Za-z0-9_]*ProcedureResult\s*\{[\s\S]*?\n\}/g) ?? [];
|
||||||
|
for (const block of resultBlocks) {
|
||||||
|
const jsonFields = block.match(typedProcedurePayloadFieldPattern);
|
||||||
|
if (jsonFields?.length) {
|
||||||
|
const name = block.match(/pub struct ([A-Za-z0-9_]+)/)?.[1] ?? 'ProcedureResult';
|
||||||
|
failures.push(`${file}: ${name} 仍通过 ${jsonFields.join(', ')} 跨层返回 JSON 字符串`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failures.length > 0) {
|
||||||
|
console.error('SpacetimeDB runtime access 检查失败:');
|
||||||
|
for (const failure of failures) {
|
||||||
|
console.error(`- ${failure}`);
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('SpacetimeDB runtime access 检查通过。');
|
||||||
99
scripts/container-compose.mjs
Normal file
99
scripts/container-compose.mjs
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import {spawn} from 'node:child_process';
|
||||||
|
import {copyFileSync, existsSync} from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
|
const [, , rawCommand = 'help', ...args] = process.argv;
|
||||||
|
const command = rawCommand.trim();
|
||||||
|
const printComposeConfig = args.includes('--print');
|
||||||
|
const passthroughArgs = args.filter((arg) => arg !== '--print');
|
||||||
|
const projectRoot = process.cwd();
|
||||||
|
const composeFile = path.join('deploy', 'container', 'docker-compose.loadtest.yml');
|
||||||
|
const envExamplePath = path.join('deploy', 'container', 'api-server.env.example');
|
||||||
|
const envPath = path.join('deploy', 'container', 'api-server.env');
|
||||||
|
|
||||||
|
const supportedCommands = new Set(['init', 'build', 'up', 'down', 'logs', 'ps', 'config', 'k6']);
|
||||||
|
|
||||||
|
if (command === 'help' || !supportedCommands.has(command)) {
|
||||||
|
printHelp(command !== 'help');
|
||||||
|
process.exit(command === 'help' ? 0 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command === 'init') {
|
||||||
|
ensureEnvFile();
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!existsSync(envPath)) {
|
||||||
|
ensureEnvFile();
|
||||||
|
console.error('[container] 请先检查 deploy/container/api-server.env 中的 SpacetimeDB 地址、库名和 token。');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const composeArgs = buildComposeArgs(command, passthroughArgs);
|
||||||
|
const child = spawn('docker', composeArgs, {
|
||||||
|
cwd: projectRoot,
|
||||||
|
env: process.env,
|
||||||
|
stdio: 'inherit',
|
||||||
|
shell: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on('error', (error) => {
|
||||||
|
console.error(`[container] docker compose 启动失败: ${error.message}`);
|
||||||
|
console.error('[container] 请确认 Docker Desktop 或 Docker Engine 已安装,并且 docker 在 PATH 中。');
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
child.on('exit', (code, signal) => {
|
||||||
|
if (signal) {
|
||||||
|
console.error(`[container] docker compose 被信号终止: ${signal}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
process.exit(code ?? 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
function buildComposeArgs(selectedCommand, extraArgs) {
|
||||||
|
const baseArgs = ['compose', '-f', composeFile];
|
||||||
|
switch (selectedCommand) {
|
||||||
|
case 'build':
|
||||||
|
return [...baseArgs, 'build', ...extraArgs];
|
||||||
|
case 'up':
|
||||||
|
return [...baseArgs, 'up', '-d', ...extraArgs];
|
||||||
|
case 'down':
|
||||||
|
return [...baseArgs, 'down', ...extraArgs];
|
||||||
|
case 'logs':
|
||||||
|
return [...baseArgs, 'logs', ...extraArgs];
|
||||||
|
case 'ps':
|
||||||
|
return [...baseArgs, 'ps', ...extraArgs];
|
||||||
|
case 'config':
|
||||||
|
return [...baseArgs, 'config', ...(printComposeConfig ? [] : ['--quiet']), ...extraArgs];
|
||||||
|
case 'k6':
|
||||||
|
return [...baseArgs, '--profile', 'loadtest', 'run', '--rm', 'k6', ...extraArgs];
|
||||||
|
default:
|
||||||
|
throw new Error(`unsupported command: ${selectedCommand}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureEnvFile() {
|
||||||
|
if (existsSync(envPath)) {
|
||||||
|
console.log(`[container] 已存在 ${envPath}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
copyFileSync(envExamplePath, envPath);
|
||||||
|
console.log(`[container] 已从 ${envExamplePath} 生成 ${envPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function printHelp(isError) {
|
||||||
|
const output = isError ? console.error : console.log;
|
||||||
|
output(`Usage: npm run container:<command> -- [docker compose args]
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
container:init 生成 deploy/container/api-server.env
|
||||||
|
container:build 构建 api-server 容器镜像
|
||||||
|
container:up 后台启动 api-server + nginx + otelcol
|
||||||
|
container:down 停止并清理容器
|
||||||
|
container:logs 查看容器日志
|
||||||
|
container:ps 查看容器状态
|
||||||
|
container:config 校验 compose 配置,传 -- --print 可展开完整配置
|
||||||
|
container:k6 在 compose 网络内运行 k6
|
||||||
|
`);
|
||||||
|
}
|
||||||
@@ -226,6 +226,8 @@ npm run loadtest:k6:works
|
|||||||
## 排障
|
## 排障
|
||||||
|
|
||||||
- 如果公开 gallery 返回 `creation_entry_disabled` 或 503,检查本地 creation entry 配置是否禁用了对应入口。
|
- 如果公开 gallery 返回 `creation_entry_disabled` 或 503,检查本地 creation entry 配置是否禁用了对应入口。
|
||||||
|
- 如果高压下返回 429,优先确认目标环境是否设置了 `GENARRATIVE_API_MAX_CONCURRENT_REQUESTS`。429 表示 api-server 应用层背压已生效,不等同于业务错误;继续看内存、p95、`http_req_failed` 和 OTLP / Nginx timing 判断阈值是否偏低。
|
||||||
|
- 如果直连 `api-server` 压测出现 `connection refused` 或 status 0,说明压力已经打到 TCP 监听 / accept 层;此时同时检查 `GENARRATIVE_API_LISTEN_BACKLOG`、Nginx upstream keepalive 和是否需要在 Nginx 前置限流,不能只靠应用层背压解释。
|
||||||
- 如果个人作品列表返回 401,确认 `AUTH_TOKEN` 是当前 api-server 可识别的 access token。
|
- 如果个人作品列表返回 401,确认 `AUTH_TOKEN` 是当前 api-server 可识别的 access token。
|
||||||
- 如果详情全部 404,确认是否已向目标环境导入与 `WORKS_DATA` 一致的数据。
|
- 如果详情全部 404,确认是否已向目标环境导入与 `WORKS_DATA` 一致的数据。
|
||||||
|
|
||||||
@@ -310,11 +312,31 @@ OTLP logs 是远端观测增量,不替代本地日志;api-server 日志仍
|
|||||||
|
|
||||||
Rider 的 Logs 面板展示的是 OTLP log event 自身字段,不会自动把父 span 的全部 attributes 摊平到每一条日志。请求完成日志会直接携带 `request_id`、`http.request.method`、`http.route`、`url.scheme`、`url.path`、`http.response.status_code`、`status_class`、`latency_ms` 和 `slow_request`;更完整的请求链路仍在 Traces 面板中按同一个 trace/span 关联查看。
|
Rider 的 Logs 面板展示的是 OTLP log event 自身字段,不会自动把父 span 的全部 attributes 摊平到每一条日志。请求完成日志会直接携带 `request_id`、`http.request.method`、`http.route`、`url.scheme`、`url.path`、`http.response.status_code`、`status_class`、`latency_ms` 和 `slow_request`;更完整的请求链路仍在 Traces 面板中按同一个 trace/span 关联查看。
|
||||||
|
|
||||||
|
压测期间可在 Metrics 面板或 debug exporter 中观察进程内存指标:
|
||||||
|
|
||||||
|
- `process.memory.usage`:进程常驻内存 / RSS。
|
||||||
|
- `process.memory.virtual`:进程虚拟内存;Windows 当前按 `PrivateUsage` 上报,Linux 取 `VmSize`。
|
||||||
|
- `genarrative.process.memory.private`:进程私有内存,Windows 来自 `PrivateUsage`,Linux 近似取 `/proc/self/status` 的 `VmData`。
|
||||||
|
- `process.thread.count`:线程数。
|
||||||
|
- `process.windows.handle.count`:Windows 句柄数。
|
||||||
|
- `process.unix.file_descriptor.count`:Linux 文件描述符数。
|
||||||
|
- `genarrative.http.server.response_bodies.in_flight`:Axum / Hyper 仍持有的响应 body 数;如果内存高但该值很低,说明热点不在业务 handler 生命周期内。
|
||||||
|
- `genarrative.http.server.request_permits.available`:应用层 HTTP 背压剩余 permit 数;如果该值未接近 0,说明没有打满 `GENARRATIVE_API_MAX_CONCURRENT_REQUESTS`。
|
||||||
|
- `genarrative.puzzle_gallery.cache.hits` / `genarrative.puzzle_gallery.cache.misses` / `genarrative.puzzle_gallery.cache.rebuilds`:拼图广场响应缓存命中、未命中和重建次数。
|
||||||
|
- `genarrative.puzzle_gallery.cache.rebuild.duration`:拼图广场缓存重建耗时。
|
||||||
|
- `genarrative.puzzle_gallery.cache.data_json_bytes`:拼图广场缓存内预序列化 data JSON 大小。
|
||||||
|
- `genarrative.spacetime.read.calls` / `genarrative.spacetime.read.duration_ms`:SpacetimeDB 订阅本地 cache 读次数和耗时;`read=list_puzzle_gallery` 表示当前路径走 view / local cache,不是 procedure。
|
||||||
|
|
||||||
|
若 `/api/runtime/puzzle/gallery` 单接口压测出现 GB 级瞬时内存峰值,先区分“持续泄漏”和“请求期分配峰值”:关闭 OTEL 后若峰值仍复现且压测结束后回落,主因通常不是 Collector / exporter。当前拼图广场列表命中缓存时应复用 `PuzzleGalleryCache` 中的预序列化 data JSON,只按请求拼接 envelope meta,不应每个请求重新深拷贝 `PuzzleGalleryResponse` 或构造完整 `serde_json::Value`。
|
||||||
|
|
||||||
|
本地 Windows 直连 `api-server` 压测还要单独看 K6 的 VU / 连接模型。已验证在 250 RPS、`PREALLOCATED_VUS=300` 时,哪怕打 `/healthz` 这种小响应,也可能因为本地 300 个 Established 连接触发 `api-server` private memory 瞬时升到约 7GB,压测结束后回落到 100MB 级;同样 250 RPS 改成 `PREALLOCATED_VUS=20 MAX_VUS=40` 后,拼图广场 p95 约 9ms,峰值降到约 600MB。这个现象说明高水位主要来自本机直连连接 / 发送链路,不等价于 SpacetimeDB 或拼图 JSON 缓存泄漏。做本地容量判断时优先让 VU 接近真实并发,避免用过高预分配 VU 把测试变成 Windows 本机连接缓冲压力测试;生产仍以 Nginx upstream keepalive、系统内存和 OTLP 指标一起判断。
|
||||||
|
|
||||||
线上回归辅助命令:
|
线上回归辅助命令:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
systemctl show genarrative-api.service -p LimitNOFILE -p TasksMax
|
systemctl show genarrative-api.service -p LimitNOFILE -p TasksMax
|
||||||
cat /proc/$(pidof api-server)/limits
|
cat /proc/$(pidof api-server)/limits
|
||||||
|
tr '\0' '\n' < /proc/$(pidof api-server)/environ | grep GENARRATIVE_API_MAX_CONCURRENT_REQUESTS
|
||||||
ss -ltnp | grep 8082
|
ss -ltnp | grep 8082
|
||||||
curl -sS http://127.0.0.1:8082/healthz
|
curl -sS http://127.0.0.1:8082/healthz
|
||||||
```
|
```
|
||||||
|
|||||||
218
scripts/loadtest/data/works-list.sample.from-migration-1.json
Normal file
218
scripts/loadtest/data/works-list.sample.from-migration-1.json
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
{
|
||||||
|
"source": "spacetime-migration-1.json",
|
||||||
|
"generatedAt": "2026-05-16T13:35:40.282Z",
|
||||||
|
"counts": {
|
||||||
|
"puzzle_work_profile": 3,
|
||||||
|
"custom_world_profile": 1,
|
||||||
|
"match3d_work_profile": 0,
|
||||||
|
"square_hole_work_profile": 0,
|
||||||
|
"visual_novel_work_profile": 0
|
||||||
|
},
|
||||||
|
"tables": {
|
||||||
|
"puzzle_work_profile": [
|
||||||
|
{
|
||||||
|
"profile_id": "profile-001",
|
||||||
|
"work_id": "work-001",
|
||||||
|
"owner_user_id": "user-001",
|
||||||
|
"author_display_name": "author-001",
|
||||||
|
"cover_asset_id": "asset-001",
|
||||||
|
"cover_image_src": "/generated-puzzle-assets/puzzle-session-f38101d7277040fcb6fbc41fea8b714a/puzzle-session-f38101d7277040fcb6fbc41fea8b714a-candidate-2/asset-1777649330373133/image.png",
|
||||||
|
"work_title": "化学家",
|
||||||
|
"level_name": "文学家",
|
||||||
|
"summary": "几个文学家正站在山上面对着瀑布侃侃而谈",
|
||||||
|
"work_description": "一个穿着白大褂的化学家正在做酷炫的化学实验,背景是化学实验室",
|
||||||
|
"levels_json": "[{\"level_id\":\"puzzle-level-1777649242577-7\",\"level_name\":\"文学家\",\"picture_description\":\"几个文学家正站在山上面对着瀑布侃侃而谈\",\"candidates\":[{\"candidate_id\":\"puzzle-session-f38101d7277040fcb6fbc41fea8b714a-candidate-2\",\"image_src\":\"/generated-puzzle-assets/puzzle-session-f38101d7277040fcb6fbc41fea8b714a/puzzle-session-f38101d7277040fcb6fbc41fea8b714a-candidate-2/asset-1777649330373133/image.png\",\"asset_id\":\"asset-1777649330373133\",\"prompt\":\"几个文学家正站在山上面对着瀑布侃侃而谈\",\"actual_prompt\":\"请生成一张高清插画。画面主体:几个文学家正站在山上面对着瀑布侃侃而谈。画面…",
|
||||||
|
"anchor_pack_json": "{\"theme_promise\":{\"key\":\"themePromise\",\"label\":\"题材承诺\",\"value\":\"化学家\",\"status\":\"Locked\"},\"visual_subject\":{\"key\":\"visualSubject\",\"label\":\"画面主体\",\"value\":\"一个穿着白大褂的化学家正在做酷炫的化学实验,背景是化学实验室\",\"status\":\"Locked\"},\"visual_mood\":{\"key\":\"visualMood\",\"label\":\"视觉气质\",\"value\":\"清晰、适合拼图切块\",\"status\":\"Inferred\"},\"composition_hooks\":{\"key\":\"compositionHooks\",\"label\":\"拼图记忆点\",\"value\":\"主体轮廓、色块分区、局部细节\",\"status\":\"Inferred\"},\"tags_and_forbidden\":{\"key\":\"tagsAndForbidden\",\"label\":\"标签与禁忌\",\"value\":\"化学家、拼图、插画;禁止标题字\",\"status\":\"I…",
|
||||||
|
"theme_tags_json": "[\"化学家\",\"拼图\",\"插画\",\"禁止标题字\"]",
|
||||||
|
"publication_status": {
|
||||||
|
"Published": []
|
||||||
|
},
|
||||||
|
"play_count": 1,
|
||||||
|
"like_count": 0,
|
||||||
|
"remix_count": 1,
|
||||||
|
"updated_at": {
|
||||||
|
"__timestamp_micros_since_unix_epoch__": 1777703338322544
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"__timestamp_micros_since_unix_epoch__": 1777648804043558
|
||||||
|
},
|
||||||
|
"published_at": {
|
||||||
|
"__timestamp_micros_since_unix_epoch__": 1777649364112270
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"profile_id": "profile-002",
|
||||||
|
"work_id": "work-002",
|
||||||
|
"owner_user_id": "user-002",
|
||||||
|
"author_display_name": "author-002",
|
||||||
|
"work_title": "我不知道",
|
||||||
|
"level_name": "",
|
||||||
|
"summary": "你猜我是谁",
|
||||||
|
"work_description": "你猜我是谁",
|
||||||
|
"levels_json": "[{\"level_id\":\"puzzle-level-1\",\"level_name\":\"\",\"picture_description\":\"真不知道\",\"candidates\":[],\"selected_candidate_id\":null,\"cover_image_src\":null,\"cover_asset_id\":null,\"generation_status\":\"idle\"}]",
|
||||||
|
"anchor_pack_json": "{\"theme_promise\":{\"key\":\"themePromise\",\"label\":\"题材承诺\",\"value\":\"我不知道\",\"status\":\"Locked\"},\"visual_subject\":{\"key\":\"visualSubject\",\"label\":\"画面主体\",\"value\":\"真不知道\",\"status\":\"Locked\"},\"visual_mood\":{\"key\":\"visualMood\",\"label\":\"视觉气质\",\"value\":\"清晰、适合拼图切块\",\"status\":\"Inferred\"},\"composition_hooks\":{\"key\":\"compositionHooks\",\"label\":\"拼图记忆点\",\"value\":\"主体轮廓、色块分区、局部细节\",\"status\":\"Inferred\"},\"tags_and_forbidden\":{\"key\":\"tagsAndForbidden\",\"label\":\"标签与禁忌\",\"value\":\"我不知道、拼图、插画;禁止标题字\",\"status\":\"Inferred\"}}",
|
||||||
|
"theme_tags_json": "[\"我不知道\"]",
|
||||||
|
"publication_status": {
|
||||||
|
"Draft": []
|
||||||
|
},
|
||||||
|
"play_count": 0,
|
||||||
|
"like_count": 0,
|
||||||
|
"remix_count": 0,
|
||||||
|
"updated_at": {
|
||||||
|
"__timestamp_micros_since_unix_epoch__": 1777619351714201
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"__timestamp_micros_since_unix_epoch__": 1777619336673245
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"profile_id": "profile-003",
|
||||||
|
"work_id": "work-003",
|
||||||
|
"owner_user_id": "user-003",
|
||||||
|
"author_display_name": "author-002",
|
||||||
|
"work_title": "",
|
||||||
|
"level_name": "",
|
||||||
|
"summary": "",
|
||||||
|
"work_description": "",
|
||||||
|
"levels_json": "[{\"level_id\":\"puzzle-level-1\",\"level_name\":\"\",\"picture_description\":\"\",\"candidates\":[],\"selected_candidate_id\":null,\"cover_image_src\":null,\"cover_asset_id\":null,\"generation_status\":\"idle\"}]",
|
||||||
|
"anchor_pack_json": "{\"theme_promise\":{\"key\":\"themePromise\",\"label\":\"题材承诺\",\"value\":\"\",\"status\":\"Missing\"},\"visual_subject\":{\"key\":\"visualSubject\",\"label\":\"画面主体\",\"value\":\"\",\"status\":\"Missing\"},\"visual_mood\":{\"key\":\"visualMood\",\"label\":\"视觉气质\",\"value\":\"\",\"status\":\"Missing\"},\"composition_hooks\":{\"key\":\"compositionHooks\",\"label\":\"拼图记忆点\",\"value\":\"\",\"status\":\"Missing\"},\"tags_and_forbidden\":{\"key\":\"tagsAndForbidden\",\"label\":\"标签与禁忌\",\"value\":\"\",\"status\":\"Missing\"}}",
|
||||||
|
"theme_tags_json": "[\"拼图\",\"插画\",\"清晰构图\"]",
|
||||||
|
"publication_status": {
|
||||||
|
"Draft": []
|
||||||
|
},
|
||||||
|
"play_count": 0,
|
||||||
|
"like_count": 0,
|
||||||
|
"remix_count": 0,
|
||||||
|
"updated_at": {
|
||||||
|
"__timestamp_micros_since_unix_epoch__": 1777622285252380
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"__timestamp_micros_since_unix_epoch__": 1777622285252380
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"custom_world_profile": [
|
||||||
|
{
|
||||||
|
"profile_id": "profile-081",
|
||||||
|
"owner_user_id": "user-002",
|
||||||
|
"author_display_name": "author-012",
|
||||||
|
"author_public_user_code": "author-code-001",
|
||||||
|
"world_name": "青春飞扬校园",
|
||||||
|
"summary_text": "在现代校园中,玩家摆脱内卷,追求真实成长",
|
||||||
|
"subtitle": "反内卷的自由学习之旅",
|
||||||
|
"profile_payload_json": "{\"anchorContent\":null,\"anchorPack\":null,\"attributeSchema\":{\"generatedFrom\":{\"conflictCore\":\"与传统教育模式的冲突\",\"settingSummary\":\"在现代校园中,玩家摆脱内卷,追求真实成长\",\"tone\":\"积极向上,充满活力与创新\",\"worldName\":\"青春飞扬校园\",\"worldType\":\"CUSTOM\"},\"id\":\"schema:rpg-agent:1e15b44d:v1\",\"schemaVersion\":1,\"slots\":[{\"name\":\"知识储备\",\"slotId\":\"axis_a\"},{\"name\":\"创新思维\",\"slotId\":\"axis_b\"},{\"name\":\"社交能力\",\"slotId\":\"axis_c\"},{\"name\":\"抗压能力\",\"slotId\":\"axis_d\"},{\"name\":\"自我认知\",\"slotId\":\"axis_e\"},{\"name\":\"团队协作\",\"slotId\":\"axis_f\"}],\"worldId\":\"custom:青春飞扬校…",
|
||||||
|
"publication_status": {
|
||||||
|
"Draft": []
|
||||||
|
},
|
||||||
|
"play_count": 0,
|
||||||
|
"like_count": 0,
|
||||||
|
"remix_count": 0,
|
||||||
|
"updated_at": {
|
||||||
|
"__timestamp_micros_since_unix_epoch__": 1777532006629209
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"__timestamp_micros_since_unix_epoch__": 1777531745887256
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"match3d_work_profile": [],
|
||||||
|
"square_hole_work_profile": [],
|
||||||
|
"visual_novel_work_profile": []
|
||||||
|
},
|
||||||
|
"profileIds": {
|
||||||
|
"puzzle": [
|
||||||
|
"profile-001",
|
||||||
|
"profile-002",
|
||||||
|
"profile-003"
|
||||||
|
],
|
||||||
|
"customWorld": [
|
||||||
|
"profile-081"
|
||||||
|
],
|
||||||
|
"match3d": [],
|
||||||
|
"squareHole": [],
|
||||||
|
"bigFish": [],
|
||||||
|
"visualNovel": []
|
||||||
|
},
|
||||||
|
"workIds": {
|
||||||
|
"puzzle": [
|
||||||
|
"work-001",
|
||||||
|
"work-002",
|
||||||
|
"work-003"
|
||||||
|
],
|
||||||
|
"customWorld": [],
|
||||||
|
"match3d": [],
|
||||||
|
"squareHole": [],
|
||||||
|
"bigFish": [],
|
||||||
|
"visualNovel": []
|
||||||
|
},
|
||||||
|
"normalizedWorks": [
|
||||||
|
{
|
||||||
|
"type": "puzzle",
|
||||||
|
"workId": "work-001",
|
||||||
|
"profileId": "profile-001",
|
||||||
|
"ownerUserId": "user-001",
|
||||||
|
"title": "化学家",
|
||||||
|
"subtitle": "几个文学家正站在山上面对着瀑布侃侃而谈",
|
||||||
|
"publicationStatus": {
|
||||||
|
"Published": []
|
||||||
|
},
|
||||||
|
"playCount": 1,
|
||||||
|
"likeCount": 0,
|
||||||
|
"remixCount": 1,
|
||||||
|
"coverImageSrc": "/generated-puzzle-assets/puzzle-session-f38101d7277040fcb6fbc41fea8b714a/puzzle-session-f38101d7277040fcb6fbc41fea8b714a-candidate-2/asset-1777649330373133/image.png",
|
||||||
|
"updatedAt": {
|
||||||
|
"__timestamp_micros_since_unix_epoch__": 1777703338322544
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "puzzle",
|
||||||
|
"workId": "work-002",
|
||||||
|
"profileId": "profile-002",
|
||||||
|
"ownerUserId": "user-002",
|
||||||
|
"title": "我不知道",
|
||||||
|
"subtitle": "你猜我是谁",
|
||||||
|
"publicationStatus": {
|
||||||
|
"Draft": []
|
||||||
|
},
|
||||||
|
"playCount": 0,
|
||||||
|
"likeCount": 0,
|
||||||
|
"remixCount": 0,
|
||||||
|
"updatedAt": {
|
||||||
|
"__timestamp_micros_since_unix_epoch__": 1777619351714201
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "puzzle",
|
||||||
|
"workId": "work-003",
|
||||||
|
"profileId": "profile-003",
|
||||||
|
"ownerUserId": "user-003",
|
||||||
|
"title": "",
|
||||||
|
"subtitle": "",
|
||||||
|
"publicationStatus": {
|
||||||
|
"Draft": []
|
||||||
|
},
|
||||||
|
"playCount": 0,
|
||||||
|
"likeCount": 0,
|
||||||
|
"remixCount": 0,
|
||||||
|
"updatedAt": {
|
||||||
|
"__timestamp_micros_since_unix_epoch__": 1777622285252380
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "customWorld",
|
||||||
|
"profileId": "profile-081",
|
||||||
|
"ownerUserId": "user-002",
|
||||||
|
"title": "青春飞扬校园",
|
||||||
|
"subtitle": "反内卷的自由学习之旅",
|
||||||
|
"publicationStatus": {
|
||||||
|
"Draft": []
|
||||||
|
},
|
||||||
|
"playCount": 0,
|
||||||
|
"likeCount": 0,
|
||||||
|
"remixCount": 0,
|
||||||
|
"updatedAt": {
|
||||||
|
"__timestamp_micros_since_unix_epoch__": 1777532006629209
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
1
server-rs/Cargo.lock
generated
1
server-rs/Cargo.lock
generated
@@ -131,6 +131,7 @@ dependencies = [
|
|||||||
"urlencoding",
|
"urlencoding",
|
||||||
"uuid",
|
"uuid",
|
||||||
"webp",
|
"webp",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
"zip",
|
"zip",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ opentelemetry-otlp = { version = "0.31", default-features = false, features = ["
|
|||||||
opentelemetry_sdk = { version = "0.31", default-features = false, features = ["trace", "metrics", "logs"] }
|
opentelemetry_sdk = { version = "0.31", default-features = false, features = ["trace", "metrics", "logs"] }
|
||||||
tracing-opentelemetry = { version = "0.32", default-features = false }
|
tracing-opentelemetry = { version = "0.32", default-features = false }
|
||||||
tracing-subscriber = "0.3"
|
tracing-subscriber = "0.3"
|
||||||
|
windows-sys = "0.61"
|
||||||
url = "2"
|
url = "2"
|
||||||
urlencoding = "2"
|
urlencoding = "2"
|
||||||
uuid = "1"
|
uuid = "1"
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ base64 = { workspace = true }
|
|||||||
bytes = { workspace = true }
|
bytes = { workspace = true }
|
||||||
dotenvy = { workspace = true }
|
dotenvy = { workspace = true }
|
||||||
image = { workspace = true, features = ["jpeg", "png", "webp"] }
|
image = { workspace = true, features = ["jpeg", "png", "webp"] }
|
||||||
|
http-body-util = { workspace = true }
|
||||||
reqwest = { workspace = true, features = ["json", "multipart", "rustls-tls"] }
|
reqwest = { workspace = true, features = ["json", "multipart", "rustls-tls"] }
|
||||||
webp = { workspace = true }
|
webp = { workspace = true }
|
||||||
module-ai = { workspace = true }
|
module-ai = { workspace = true }
|
||||||
@@ -45,7 +46,7 @@ shared-kernel = { workspace = true }
|
|||||||
shared-logging = { workspace = true }
|
shared-logging = { workspace = true }
|
||||||
socket2 = { workspace = true }
|
socket2 = { workspace = true }
|
||||||
spacetime-client = { workspace = true }
|
spacetime-client = { workspace = true }
|
||||||
tokio = { workspace = true, features = ["macros", "rt-multi-thread", "net", "time"] }
|
tokio = { workspace = true, features = ["macros", "rt-multi-thread", "net", "time", "sync"] }
|
||||||
tokio-stream = { workspace = true }
|
tokio-stream = { workspace = true }
|
||||||
futures-util = { workspace = true }
|
futures-util = { workspace = true }
|
||||||
time = { workspace = true, features = ["formatting"] }
|
time = { workspace = true, features = ["formatting"] }
|
||||||
@@ -57,6 +58,9 @@ urlencoding = { workspace = true }
|
|||||||
uuid = { workspace = true, features = ["v4"] }
|
uuid = { workspace = true, features = ["v4"] }
|
||||||
zip = { workspace = true, features = ["deflate"] }
|
zip = { workspace = true, features = ["deflate"] }
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
windows-sys = { workspace = true, features = ["Win32_Foundation", "Win32_System_Diagnostics_ToolHelp", "Win32_System_ProcessStatus", "Win32_System_Threading"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
base64 = { workspace = true }
|
base64 = { workspace = true }
|
||||||
hmac = { workspace = true }
|
hmac = { workspace = true }
|
||||||
|
|||||||
@@ -1,4 +1,13 @@
|
|||||||
use axum::Json;
|
use std::convert::Infallible;
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
Json,
|
||||||
|
body::Body,
|
||||||
|
http::{HeaderValue, header},
|
||||||
|
response::{IntoResponse, Response},
|
||||||
|
};
|
||||||
|
use bytes::Bytes;
|
||||||
|
use futures_util::stream;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -32,6 +41,30 @@ where
|
|||||||
Json(serde_json::to_value(data).unwrap_or(Value::Null))
|
Json(serde_json::to_value(data).unwrap_or(Value::Null))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn json_success_data_bytes_response(
|
||||||
|
request_context: Option<&RequestContext>,
|
||||||
|
data_json: Bytes,
|
||||||
|
) -> Response {
|
||||||
|
if let Some(context) = request_context
|
||||||
|
&& context.wants_envelope()
|
||||||
|
{
|
||||||
|
let meta = serde_json::to_vec(&build_api_response_meta(Some(context)))
|
||||||
|
.map(Bytes::from)
|
||||||
|
.unwrap_or_else(|_| Bytes::from_static(b"null"));
|
||||||
|
let chunks = [
|
||||||
|
Bytes::from_static(b"{\"ok\":true,\"data\":"),
|
||||||
|
data_json,
|
||||||
|
Bytes::from_static(b",\"error\":null,\"meta\":"),
|
||||||
|
meta,
|
||||||
|
Bytes::from_static(b"}"),
|
||||||
|
];
|
||||||
|
let stream = stream::iter(chunks.into_iter().map(Ok::<Bytes, Infallible>));
|
||||||
|
return json_body_response(Body::from_stream(stream));
|
||||||
|
}
|
||||||
|
|
||||||
|
json_bytes_response(data_json)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn json_error_body(
|
pub fn json_error_body(
|
||||||
request_context: Option<&RequestContext>,
|
request_context: Option<&RequestContext>,
|
||||||
error: &ApiErrorPayload,
|
error: &ApiErrorPayload,
|
||||||
@@ -65,6 +98,19 @@ fn build_api_response_meta(request_context: Option<&RequestContext>) -> ApiRespo
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn json_bytes_response(bytes: Bytes) -> Response {
|
||||||
|
json_body_response(Body::from(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn json_body_response(body: Body) -> Response {
|
||||||
|
let mut response = body.into_response();
|
||||||
|
response.headers_mut().insert(
|
||||||
|
header::CONTENT_TYPE,
|
||||||
|
HeaderValue::from_static("application/json; charset=utf-8"),
|
||||||
|
);
|
||||||
|
response
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -106,6 +152,31 @@ mod tests {
|
|||||||
assert!(body.get("meta").is_none());
|
assert!(body.get("meta").is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn success_response_streams_cached_data_inside_standard_envelope() {
|
||||||
|
use http_body_util::BodyExt;
|
||||||
|
|
||||||
|
let request_context = build_request_context(true);
|
||||||
|
let response = json_success_data_bytes_response(
|
||||||
|
Some(&request_context),
|
||||||
|
Bytes::from_static(br#"{"items":[]}"#),
|
||||||
|
);
|
||||||
|
let body = response
|
||||||
|
.into_body()
|
||||||
|
.collect()
|
||||||
|
.await
|
||||||
|
.expect("response body should collect")
|
||||||
|
.to_bytes();
|
||||||
|
let payload: Value = serde_json::from_slice(&body).expect("body should be json");
|
||||||
|
|
||||||
|
assert_eq!(payload["ok"], Value::Bool(true));
|
||||||
|
assert_eq!(payload["data"]["items"], Value::Array(Vec::new()));
|
||||||
|
assert_eq!(
|
||||||
|
payload["meta"]["requestId"],
|
||||||
|
Value::String("req-test".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn error_body_returns_legacy_shape_without_envelope_header() {
|
fn error_body_returns_legacy_shape_without_envelope_header() {
|
||||||
let request_context = build_request_context(false);
|
let request_context = build_request_context(false);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ use tracing::{Level, Span, error, info_span};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
auth::{AuthenticatedAccessToken, require_bearer_auth},
|
auth::{AuthenticatedAccessToken, require_bearer_auth},
|
||||||
|
backpressure::limit_concurrent_requests,
|
||||||
creation_entry_config::require_creation_entry_route_enabled,
|
creation_entry_config::require_creation_entry_route_enabled,
|
||||||
error_middleware::normalize_error_response,
|
error_middleware::normalize_error_response,
|
||||||
modules,
|
modules,
|
||||||
@@ -76,6 +77,11 @@ pub fn build_router(state: AppState) -> Router {
|
|||||||
state.clone(),
|
state.clone(),
|
||||||
require_creation_entry_route_enabled,
|
require_creation_entry_route_enabled,
|
||||||
))
|
))
|
||||||
|
// HTTP 背压在业务路由外侧快拒绝,避免过载请求继续占用 SpacetimeDB facade 与业务执行资源。
|
||||||
|
.layer(middleware::from_fn_with_state(
|
||||||
|
state.clone(),
|
||||||
|
limit_concurrent_requests,
|
||||||
|
))
|
||||||
// 错误归一化层放在 tracing 里侧,让 tracing 记录到最终对外返回的状态与错误体形态。
|
// 错误归一化层放在 tracing 里侧,让 tracing 记录到最终对外返回的状态与错误体形态。
|
||||||
.layer(middleware::from_fn(normalize_error_response))
|
.layer(middleware::from_fn(normalize_error_response))
|
||||||
// 响应头回写放在错误归一化外侧,确保最终写回的是归一化后的最终响应。
|
// 响应头回写放在错误归一化外侧,确保最终写回的是归一化后的最终响应。
|
||||||
|
|||||||
245
server-rs/crates/api-server/src/backpressure.rs
Normal file
245
server-rs/crates/api-server/src/backpressure.rs
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
body::Body,
|
||||||
|
extract::{Request, State},
|
||||||
|
http::{HeaderValue, StatusCode, header::RETRY_AFTER},
|
||||||
|
middleware::Next,
|
||||||
|
response::Response,
|
||||||
|
};
|
||||||
|
use http_body_util::BodyExt;
|
||||||
|
use tokio::sync::{OwnedSemaphorePermit, TryAcquireError};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
http_error::AppError,
|
||||||
|
request_context::RequestContext,
|
||||||
|
state::{AppState, HttpRequestPermitPool},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub async fn limit_concurrent_requests(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
request: Request,
|
||||||
|
next: Next,
|
||||||
|
) -> Response {
|
||||||
|
if should_bypass_backpressure(&request) {
|
||||||
|
return next.run(request).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(permit_pool) = state.http_request_permit_pool() else {
|
||||||
|
return next.run(request).await;
|
||||||
|
};
|
||||||
|
|
||||||
|
match acquire_http_request_permit(permit_pool) {
|
||||||
|
Ok(permit) => hold_permit_until_response_body_dropped(next.run(request).await, permit),
|
||||||
|
Err(_) => reject_overloaded_request(&request),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn acquire_http_request_permit(
|
||||||
|
permit_pool: Arc<HttpRequestPermitPool>,
|
||||||
|
) -> Result<HttpRequestPermitGuard, TryAcquireError> {
|
||||||
|
match permit_pool.clone().try_acquire_owned() {
|
||||||
|
Ok(permit) => {
|
||||||
|
crate::telemetry::update_http_request_permits_available(permit_pool.available_permits());
|
||||||
|
Ok(HttpRequestPermitGuard {
|
||||||
|
permit: Some(permit),
|
||||||
|
permit_pool,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
crate::telemetry::update_http_request_permits_available(permit_pool.available_permits());
|
||||||
|
Err(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hold_permit_until_response_body_dropped(
|
||||||
|
response: Response,
|
||||||
|
permit: HttpRequestPermitGuard,
|
||||||
|
) -> Response {
|
||||||
|
response.map(|body| {
|
||||||
|
Body::new(body.map_frame(move |frame| {
|
||||||
|
let _permit_guard = &permit;
|
||||||
|
frame
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HttpRequestPermitGuard {
|
||||||
|
permit: Option<OwnedSemaphorePermit>,
|
||||||
|
permit_pool: Arc<HttpRequestPermitPool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for HttpRequestPermitGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
drop(self.permit.take());
|
||||||
|
crate::telemetry::update_http_request_permits_available(self.permit_pool.available_permits());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reject_overloaded_request(request: &Request<Body>) -> Response {
|
||||||
|
let request_context = request.extensions().get::<RequestContext>().cloned();
|
||||||
|
let mut response = AppError::from_status(StatusCode::TOO_MANY_REQUESTS)
|
||||||
|
.with_message("服务繁忙,请稍后重试")
|
||||||
|
.into_response_with_context(request_context.as_ref());
|
||||||
|
response
|
||||||
|
.headers_mut()
|
||||||
|
.insert(RETRY_AFTER, HeaderValue::from_static("1"));
|
||||||
|
response
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_bypass_backpressure(request: &Request<Body>) -> bool {
|
||||||
|
request.uri().path() == "/healthz"
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
Router,
|
||||||
|
body::Body,
|
||||||
|
extract::Extension,
|
||||||
|
http::{Request, StatusCode, header::RETRY_AFTER},
|
||||||
|
middleware,
|
||||||
|
routing::get,
|
||||||
|
};
|
||||||
|
use tokio::sync::Notify;
|
||||||
|
use tower::ServiceExt;
|
||||||
|
|
||||||
|
use crate::{config::AppConfig, state::AppState};
|
||||||
|
|
||||||
|
use super::limit_concurrent_requests;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct HeldRequestGate {
|
||||||
|
entered: Arc<Notify>,
|
||||||
|
release: Arc<Notify>,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn held_request(Extension(gate): Extension<HeldRequestGate>) -> &'static str {
|
||||||
|
gate.entered.notify_one();
|
||||||
|
gate.release.notified().await;
|
||||||
|
"ok"
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fast_request() -> &'static str {
|
||||||
|
"ok"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_request(path: &str) -> Request<Body> {
|
||||||
|
Request::builder()
|
||||||
|
.uri(path)
|
||||||
|
.body(Body::empty())
|
||||||
|
.expect("test request should build")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_test_app(max_concurrent_requests: usize, gate: HeldRequestGate) -> Router {
|
||||||
|
let mut config = AppConfig::default();
|
||||||
|
config.max_concurrent_requests = Some(max_concurrent_requests);
|
||||||
|
let state = AppState::new(config).expect("state should build");
|
||||||
|
|
||||||
|
Router::new()
|
||||||
|
.route("/held", get(held_request))
|
||||||
|
.route("/fast", get(fast_request))
|
||||||
|
.route("/healthz", get(fast_request))
|
||||||
|
.layer(middleware::from_fn_with_state(
|
||||||
|
state.clone(),
|
||||||
|
limit_concurrent_requests,
|
||||||
|
))
|
||||||
|
.layer(Extension(gate))
|
||||||
|
.with_state(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn returns_429_when_concurrency_permits_are_exhausted() {
|
||||||
|
let gate = HeldRequestGate {
|
||||||
|
entered: Arc::new(Notify::new()),
|
||||||
|
release: Arc::new(Notify::new()),
|
||||||
|
};
|
||||||
|
let app = build_test_app(1, gate.clone());
|
||||||
|
let entered = gate.entered.notified();
|
||||||
|
|
||||||
|
let held_response = tokio::spawn(app.clone().oneshot(test_request("/held")));
|
||||||
|
entered.await;
|
||||||
|
|
||||||
|
let rejected_response = app
|
||||||
|
.clone()
|
||||||
|
.oneshot(test_request("/fast"))
|
||||||
|
.await
|
||||||
|
.expect("rejected request should complete");
|
||||||
|
assert_eq!(rejected_response.status(), StatusCode::TOO_MANY_REQUESTS);
|
||||||
|
assert_eq!(
|
||||||
|
rejected_response
|
||||||
|
.headers()
|
||||||
|
.get(RETRY_AFTER)
|
||||||
|
.and_then(|value| value.to_str().ok()),
|
||||||
|
Some("1")
|
||||||
|
);
|
||||||
|
|
||||||
|
gate.release.notify_one();
|
||||||
|
let completed_response = held_response
|
||||||
|
.await
|
||||||
|
.expect("held request task should join")
|
||||||
|
.expect("held request should complete");
|
||||||
|
assert_eq!(completed_response.status(), StatusCode::OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn healthz_bypasses_concurrency_backpressure() {
|
||||||
|
let gate = HeldRequestGate {
|
||||||
|
entered: Arc::new(Notify::new()),
|
||||||
|
release: Arc::new(Notify::new()),
|
||||||
|
};
|
||||||
|
let app = build_test_app(1, gate.clone());
|
||||||
|
let entered = gate.entered.notified();
|
||||||
|
|
||||||
|
let held_response = tokio::spawn(app.clone().oneshot(test_request("/held")));
|
||||||
|
entered.await;
|
||||||
|
|
||||||
|
let health_response = app
|
||||||
|
.clone()
|
||||||
|
.oneshot(test_request("/healthz"))
|
||||||
|
.await
|
||||||
|
.expect("healthz request should complete");
|
||||||
|
assert_eq!(health_response.status(), StatusCode::OK);
|
||||||
|
|
||||||
|
gate.release.notify_one();
|
||||||
|
let completed_response = held_response
|
||||||
|
.await
|
||||||
|
.expect("held request task should join")
|
||||||
|
.expect("held request should complete");
|
||||||
|
assert_eq!(completed_response.status(), StatusCode::OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn permit_is_held_until_response_body_is_dropped() {
|
||||||
|
let gate = HeldRequestGate {
|
||||||
|
entered: Arc::new(Notify::new()),
|
||||||
|
release: Arc::new(Notify::new()),
|
||||||
|
};
|
||||||
|
let app = build_test_app(1, gate);
|
||||||
|
|
||||||
|
let first_response = app
|
||||||
|
.clone()
|
||||||
|
.oneshot(test_request("/fast"))
|
||||||
|
.await
|
||||||
|
.expect("first request should complete");
|
||||||
|
assert_eq!(first_response.status(), StatusCode::OK);
|
||||||
|
|
||||||
|
let rejected_response = app
|
||||||
|
.clone()
|
||||||
|
.oneshot(test_request("/fast"))
|
||||||
|
.await
|
||||||
|
.expect("second request should complete");
|
||||||
|
assert_eq!(rejected_response.status(), StatusCode::TOO_MANY_REQUESTS);
|
||||||
|
|
||||||
|
drop(first_response);
|
||||||
|
|
||||||
|
let accepted_response = app
|
||||||
|
.oneshot(test_request("/fast"))
|
||||||
|
.await
|
||||||
|
.expect("third request should complete");
|
||||||
|
assert_eq!(accepted_response.status(), StatusCode::OK);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ pub struct AppConfig {
|
|||||||
pub bind_port: u16,
|
pub bind_port: u16,
|
||||||
pub listen_backlog: i32,
|
pub listen_backlog: i32,
|
||||||
pub worker_threads: Option<usize>,
|
pub worker_threads: Option<usize>,
|
||||||
|
pub max_concurrent_requests: Option<usize>,
|
||||||
pub log_filter: String,
|
pub log_filter: String,
|
||||||
pub otel_enabled: bool,
|
pub otel_enabled: bool,
|
||||||
pub admin_username: Option<String>,
|
pub admin_username: Option<String>,
|
||||||
@@ -152,6 +153,7 @@ impl Default for AppConfig {
|
|||||||
bind_port: 3000,
|
bind_port: 3000,
|
||||||
listen_backlog: 1024,
|
listen_backlog: 1024,
|
||||||
worker_threads: None,
|
worker_threads: None,
|
||||||
|
max_concurrent_requests: None,
|
||||||
log_filter: "info,tower_http=info".to_string(),
|
log_filter: "info,tower_http=info".to_string(),
|
||||||
otel_enabled: false,
|
otel_enabled: false,
|
||||||
admin_username: None,
|
admin_username: None,
|
||||||
@@ -315,6 +317,11 @@ impl AppConfig {
|
|||||||
if let Some(worker_threads) = read_first_usize_env(&["GENARRATIVE_API_WORKER_THREADS"]) {
|
if let Some(worker_threads) = read_first_usize_env(&["GENARRATIVE_API_WORKER_THREADS"]) {
|
||||||
config.worker_threads = Some(worker_threads);
|
config.worker_threads = Some(worker_threads);
|
||||||
}
|
}
|
||||||
|
if let Some(max_concurrent_requests) =
|
||||||
|
read_first_usize_env(&["GENARRATIVE_API_MAX_CONCURRENT_REQUESTS"])
|
||||||
|
{
|
||||||
|
config.max_concurrent_requests = Some(max_concurrent_requests);
|
||||||
|
}
|
||||||
if let Some(otel_enabled) = read_first_bool_env(&["GENARRATIVE_OTEL_ENABLED"]) {
|
if let Some(otel_enabled) = read_first_bool_env(&["GENARRATIVE_OTEL_ENABLED"]) {
|
||||||
config.otel_enabled = otel_enabled;
|
config.otel_enabled = otel_enabled;
|
||||||
}
|
}
|
||||||
@@ -1198,20 +1205,24 @@ mod tests {
|
|||||||
unsafe {
|
unsafe {
|
||||||
std::env::remove_var("GENARRATIVE_API_LISTEN_BACKLOG");
|
std::env::remove_var("GENARRATIVE_API_LISTEN_BACKLOG");
|
||||||
std::env::remove_var("GENARRATIVE_API_WORKER_THREADS");
|
std::env::remove_var("GENARRATIVE_API_WORKER_THREADS");
|
||||||
|
std::env::remove_var("GENARRATIVE_API_MAX_CONCURRENT_REQUESTS");
|
||||||
std::env::remove_var("GENARRATIVE_OTEL_ENABLED");
|
std::env::remove_var("GENARRATIVE_OTEL_ENABLED");
|
||||||
std::env::set_var("GENARRATIVE_API_LISTEN_BACKLOG", "2048");
|
std::env::set_var("GENARRATIVE_API_LISTEN_BACKLOG", "2048");
|
||||||
std::env::set_var("GENARRATIVE_API_WORKER_THREADS", "6");
|
std::env::set_var("GENARRATIVE_API_WORKER_THREADS", "6");
|
||||||
|
std::env::set_var("GENARRATIVE_API_MAX_CONCURRENT_REQUESTS", "128");
|
||||||
std::env::set_var("GENARRATIVE_OTEL_ENABLED", "true");
|
std::env::set_var("GENARRATIVE_OTEL_ENABLED", "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
let config = AppConfig::from_env();
|
let config = AppConfig::from_env();
|
||||||
assert_eq!(config.listen_backlog, 2048);
|
assert_eq!(config.listen_backlog, 2048);
|
||||||
assert_eq!(config.worker_threads, Some(6));
|
assert_eq!(config.worker_threads, Some(6));
|
||||||
|
assert_eq!(config.max_concurrent_requests, Some(128));
|
||||||
assert!(config.otel_enabled);
|
assert!(config.otel_enabled);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
std::env::remove_var("GENARRATIVE_API_LISTEN_BACKLOG");
|
std::env::remove_var("GENARRATIVE_API_LISTEN_BACKLOG");
|
||||||
std::env::remove_var("GENARRATIVE_API_WORKER_THREADS");
|
std::env::remove_var("GENARRATIVE_API_WORKER_THREADS");
|
||||||
|
std::env::remove_var("GENARRATIVE_API_MAX_CONCURRENT_REQUESTS");
|
||||||
std::env::remove_var("GENARRATIVE_OTEL_ENABLED");
|
std::env::remove_var("GENARRATIVE_OTEL_ENABLED");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ mod auth_payload;
|
|||||||
mod auth_public_user;
|
mod auth_public_user;
|
||||||
mod auth_session;
|
mod auth_session;
|
||||||
mod auth_sessions;
|
mod auth_sessions;
|
||||||
|
mod backpressure;
|
||||||
mod bark_battle;
|
mod bark_battle;
|
||||||
mod big_fish;
|
mod big_fish;
|
||||||
mod big_fish_agent_turn;
|
mod big_fish_agent_turn;
|
||||||
@@ -55,9 +56,11 @@ mod password_management;
|
|||||||
mod phone_auth;
|
mod phone_auth;
|
||||||
mod platform_errors;
|
mod platform_errors;
|
||||||
mod profile_identity;
|
mod profile_identity;
|
||||||
|
mod process_metrics;
|
||||||
mod prompt;
|
mod prompt;
|
||||||
mod puzzle;
|
mod puzzle;
|
||||||
mod puzzle_agent_turn;
|
mod puzzle_agent_turn;
|
||||||
|
mod puzzle_gallery_cache;
|
||||||
mod refresh_session;
|
mod refresh_session;
|
||||||
mod registration_reward;
|
mod registration_reward;
|
||||||
mod request_context;
|
mod request_context;
|
||||||
@@ -138,6 +141,8 @@ async fn run_server(config: AppConfig) -> Result<(), io::Error> {
|
|||||||
enabled: config.otel_enabled,
|
enabled: config.otel_enabled,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
process_metrics::register_process_metrics();
|
||||||
|
telemetry::register_http_runtime_metrics();
|
||||||
|
|
||||||
let bind_address = config.bind_socket_addr();
|
let bind_address = config.bind_socket_addr();
|
||||||
let listen_backlog = config.listen_backlog;
|
let listen_backlog = config.listen_backlog;
|
||||||
@@ -148,6 +153,7 @@ async fn run_server(config: AppConfig) -> Result<(), io::Error> {
|
|||||||
let state = restore_app_state_for_startup(config)
|
let state = restore_app_state_for_startup(config)
|
||||||
.await
|
.await
|
||||||
.map_err(|error| std::io::Error::other(format!("初始化应用状态失败:{error}")))?;
|
.map_err(|error| std::io::Error::other(format!("初始化应用状态失败:{error}")))?;
|
||||||
|
state.puzzle_gallery_cache().spawn_cleanup_task();
|
||||||
let router = build_router(state);
|
let router = build_router(state);
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
|
|||||||
306
server-rs/crates/api-server/src/process_metrics.rs
Normal file
306
server-rs/crates/api-server/src/process_metrics.rs
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
use opentelemetry::global;
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
|
// 进程指标只描述 api-server 自身,不携带请求、用户或作品维度,避免 OTLP 指标高基数膨胀。
|
||||||
|
pub(crate) fn register_process_metrics() {
|
||||||
|
static REGISTERED: OnceLock<()> = OnceLock::new();
|
||||||
|
REGISTERED.get_or_init(register_process_metrics_once);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_process_metrics_once() {
|
||||||
|
let meter = global::meter("genarrative-api");
|
||||||
|
|
||||||
|
meter
|
||||||
|
.i64_observable_up_down_counter("process.memory.usage")
|
||||||
|
.with_unit("By")
|
||||||
|
.with_description("api-server process physical memory usage")
|
||||||
|
.with_callback(|observer| {
|
||||||
|
let Some(snapshot) = ProcessMetricsSnapshot::collect() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
observer.observe(to_i64(snapshot.rss_bytes), &[]);
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
|
||||||
|
meter
|
||||||
|
.i64_observable_up_down_counter("process.memory.virtual")
|
||||||
|
.with_unit("By")
|
||||||
|
.with_description("api-server committed virtual memory")
|
||||||
|
.with_callback(|observer| {
|
||||||
|
let Some(snapshot) = ProcessMetricsSnapshot::collect() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if let Some(virtual_bytes) = snapshot.virtual_bytes {
|
||||||
|
observer.observe(to_i64(virtual_bytes), &[]);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
|
||||||
|
meter
|
||||||
|
.i64_observable_up_down_counter("genarrative.process.memory.private")
|
||||||
|
.with_unit("By")
|
||||||
|
.with_description("api-server private memory for local diagnostics")
|
||||||
|
.with_callback(|observer| {
|
||||||
|
let Some(snapshot) = ProcessMetricsSnapshot::collect() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if let Some(private_bytes) = snapshot.private_bytes {
|
||||||
|
observer.observe(to_i64(private_bytes), &[]);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
|
||||||
|
meter
|
||||||
|
.i64_observable_up_down_counter("process.thread.count")
|
||||||
|
.with_unit("{thread}")
|
||||||
|
.with_description("api-server process thread count")
|
||||||
|
.with_callback(|observer| {
|
||||||
|
let Some(snapshot) = ProcessMetricsSnapshot::collect() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
observer.observe(to_i64(snapshot.thread_count), &[]);
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
|
||||||
|
meter
|
||||||
|
.i64_observable_up_down_counter("process.windows.handle.count")
|
||||||
|
.with_unit("{handle}")
|
||||||
|
.with_description("api-server process handle count on Windows")
|
||||||
|
.with_callback(|observer| {
|
||||||
|
let Some(snapshot) = ProcessMetricsSnapshot::collect() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if let Some(handle_count) = snapshot.windows_handle_count {
|
||||||
|
observer.observe(to_i64(handle_count), &[]);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
|
||||||
|
meter
|
||||||
|
.i64_observable_up_down_counter("process.unix.file_descriptor.count")
|
||||||
|
.with_unit("{file_descriptor}")
|
||||||
|
.with_description("api-server process file descriptor count on Unix")
|
||||||
|
.with_callback(|observer| {
|
||||||
|
let Some(snapshot) = ProcessMetricsSnapshot::collect() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if let Some(fd_count) = snapshot.unix_fd_count {
|
||||||
|
observer.observe(to_i64(fd_count), &[]);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_i64(value: u64) -> i64 {
|
||||||
|
value.min(i64::MAX as u64) as i64
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
struct ProcessMetricsSnapshot {
|
||||||
|
rss_bytes: u64,
|
||||||
|
private_bytes: Option<u64>,
|
||||||
|
virtual_bytes: Option<u64>,
|
||||||
|
thread_count: u64,
|
||||||
|
windows_handle_count: Option<u64>,
|
||||||
|
unix_fd_count: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProcessMetricsSnapshot {
|
||||||
|
fn collect() -> Option<Self> {
|
||||||
|
collect_process_metrics()
|
||||||
|
.inspect_err(|error| {
|
||||||
|
warn!(%error, "采集 api-server 进程内存指标失败");
|
||||||
|
})
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn collect_process_metrics() -> Result<ProcessMetricsSnapshot, String> {
|
||||||
|
use windows_sys::Win32::{
|
||||||
|
System::{
|
||||||
|
ProcessStatus::{GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS_EX},
|
||||||
|
Threading::{GetCurrentProcess, GetCurrentProcessId, GetProcessHandleCount},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let handle = unsafe { GetCurrentProcess() };
|
||||||
|
let mut counters = PROCESS_MEMORY_COUNTERS_EX {
|
||||||
|
cb: std::mem::size_of::<PROCESS_MEMORY_COUNTERS_EX>() as u32,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let ok = unsafe {
|
||||||
|
GetProcessMemoryInfo(
|
||||||
|
handle,
|
||||||
|
std::ptr::addr_of_mut!(counters).cast(),
|
||||||
|
counters.cb,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if ok == 0 {
|
||||||
|
return Err("GetProcessMemoryInfo returned false".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut handle_count = 0_u32;
|
||||||
|
let handle_count = if unsafe { GetProcessHandleCount(handle, &mut handle_count) } == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(u64::from(handle_count))
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(ProcessMetricsSnapshot {
|
||||||
|
rss_bytes: counters.WorkingSetSize as u64,
|
||||||
|
private_bytes: Some(counters.PrivateUsage as u64),
|
||||||
|
virtual_bytes: Some(counters.PrivateUsage as u64),
|
||||||
|
thread_count: u64::from(unsafe { GetCurrentProcessId() }.thread_count()?),
|
||||||
|
windows_handle_count: handle_count,
|
||||||
|
unix_fd_count: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
trait WindowsProcessThreadCount {
|
||||||
|
fn thread_count(self) -> Result<u32, String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
impl WindowsProcessThreadCount for u32 {
|
||||||
|
fn thread_count(self) -> Result<u32, String> {
|
||||||
|
use windows_sys::Win32::{
|
||||||
|
Foundation::{CloseHandle, INVALID_HANDLE_VALUE},
|
||||||
|
System::Diagnostics::ToolHelp::{
|
||||||
|
CreateToolhelp32Snapshot, PROCESSENTRY32, Process32First, Process32Next,
|
||||||
|
TH32CS_SNAPPROCESS,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) };
|
||||||
|
if snapshot == INVALID_HANDLE_VALUE {
|
||||||
|
return Err("CreateToolhelp32Snapshot returned INVALID_HANDLE_VALUE".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut entry = PROCESSENTRY32 {
|
||||||
|
dwSize: std::mem::size_of::<PROCESSENTRY32>() as u32,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mut found = None;
|
||||||
|
let mut ok = unsafe { Process32First(snapshot, &mut entry) };
|
||||||
|
while ok != 0 {
|
||||||
|
if entry.th32ProcessID == self {
|
||||||
|
found = Some(entry.cntThreads);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ok = unsafe { Process32Next(snapshot, &mut entry) };
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
CloseHandle(snapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
found.ok_or_else(|| format!("process {self} not found in ToolHelp snapshot"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
fn collect_process_metrics() -> Result<ProcessMetricsSnapshot, String> {
|
||||||
|
let status = std::fs::read_to_string("/proc/self/status")
|
||||||
|
.map_err(|error| format!("read /proc/self/status failed: {error}"))?;
|
||||||
|
let statm = std::fs::read_to_string("/proc/self/statm")
|
||||||
|
.map_err(|error| format!("read /proc/self/statm failed: {error}"))?;
|
||||||
|
let page_size = linux_page_size_bytes()?;
|
||||||
|
|
||||||
|
let rss_bytes = parse_status_kb(&status, "VmRSS:")
|
||||||
|
.map(|value| value * 1024)
|
||||||
|
.or_else(|| parse_statm_pages(&statm, 1).map(|value| value * page_size))
|
||||||
|
.ok_or_else(|| "missing VmRSS/statm resident field".to_string())?;
|
||||||
|
let virtual_bytes = parse_status_kb(&status, "VmSize:")
|
||||||
|
.map(|value| value * 1024)
|
||||||
|
.or_else(|| parse_statm_pages(&statm, 0).map(|value| value * page_size))
|
||||||
|
.ok_or_else(|| "missing VmSize/statm size field".to_string())?;
|
||||||
|
let private_bytes = parse_status_kb(&status, "VmData:").map(|value| value * 1024);
|
||||||
|
let thread_count = parse_status_u64(&status, "Threads:")
|
||||||
|
.ok_or_else(|| "missing Threads field".to_string())?;
|
||||||
|
|
||||||
|
Ok(ProcessMetricsSnapshot {
|
||||||
|
rss_bytes,
|
||||||
|
private_bytes,
|
||||||
|
virtual_bytes: Some(virtual_bytes),
|
||||||
|
thread_count,
|
||||||
|
windows_handle_count: None,
|
||||||
|
unix_fd_count: linux_fd_count(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
fn linux_page_size_bytes() -> Result<u64, String> {
|
||||||
|
let output = std::process::Command::new("getconf")
|
||||||
|
.arg("PAGESIZE")
|
||||||
|
.output()
|
||||||
|
.map_err(|error| format!("getconf PAGESIZE failed: {error}"))?;
|
||||||
|
if !output.status.success() {
|
||||||
|
return Err(format!("getconf PAGESIZE exited with {}", output.status));
|
||||||
|
}
|
||||||
|
let text = String::from_utf8(output.stdout)
|
||||||
|
.map_err(|error| format!("getconf PAGESIZE output is not utf8: {error}"))?;
|
||||||
|
text.trim()
|
||||||
|
.parse::<u64>()
|
||||||
|
.map_err(|error| format!("parse PAGESIZE failed: {error}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
fn linux_fd_count() -> Option<u64> {
|
||||||
|
let entries = std::fs::read_dir("/proc/self/fd").ok()?;
|
||||||
|
Some(entries.filter_map(Result::ok).count() as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
fn parse_status_kb(status: &str, key: &str) -> Option<u64> {
|
||||||
|
parse_status_u64(status, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
fn parse_status_u64(status: &str, key: &str) -> Option<u64> {
|
||||||
|
status.lines().find_map(|line| {
|
||||||
|
let rest = line.strip_prefix(key)?.trim();
|
||||||
|
rest.split_whitespace().next()?.parse::<u64>().ok()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
fn parse_statm_pages(statm: &str, index: usize) -> Option<u64> {
|
||||||
|
statm
|
||||||
|
.split_whitespace()
|
||||||
|
.nth(index)?
|
||||||
|
.parse::<u64>()
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(windows, target_os = "linux")))]
|
||||||
|
fn collect_process_metrics() -> Result<ProcessMetricsSnapshot, String> {
|
||||||
|
Err("process metrics are only implemented for Windows and Linux".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
use super::{parse_statm_pages, parse_status_kb, parse_status_u64};
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
#[test]
|
||||||
|
fn parses_linux_proc_status_memory_fields() {
|
||||||
|
let status = "Name:\tapi-server\nVmSize:\t 123456 kB\nVmRSS:\t 7890 kB\nVmData:\t 3456 kB\nThreads:\t37\n";
|
||||||
|
|
||||||
|
assert_eq!(parse_status_kb(status, "VmRSS:"), Some(7890));
|
||||||
|
assert_eq!(parse_status_kb(status, "VmSize:"), Some(123456));
|
||||||
|
assert_eq!(parse_status_kb(status, "VmData:"), Some(3456));
|
||||||
|
assert_eq!(parse_status_u64(status, "Threads:"), Some(37));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
#[test]
|
||||||
|
fn parses_linux_statm_pages() {
|
||||||
|
assert_eq!(parse_statm_pages("100 20 0 0 0 0 0", 0), Some(100));
|
||||||
|
assert_eq!(parse_statm_pages("100 20 0 0 0 0 0", 1), Some(20));
|
||||||
|
assert_eq!(parse_statm_pages("100 20", 7), None);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,7 +38,7 @@ use shared_contracts::{
|
|||||||
PuzzleResultPreviewBlockerResponse, PuzzleResultPreviewEnvelopeResponse,
|
PuzzleResultPreviewBlockerResponse, PuzzleResultPreviewEnvelopeResponse,
|
||||||
PuzzleResultPreviewFindingResponse, SendPuzzleAgentMessageRequest,
|
PuzzleResultPreviewFindingResponse, SendPuzzleAgentMessageRequest,
|
||||||
},
|
},
|
||||||
puzzle_gallery::{PuzzleGalleryDetailResponse, PuzzleGalleryResponse},
|
puzzle_gallery::PuzzleGalleryDetailResponse,
|
||||||
puzzle_runtime::{
|
puzzle_runtime::{
|
||||||
AdvancePuzzleNextLevelRequest, DragPuzzlePieceRequest, PuzzleBoardSnapshotResponse,
|
AdvancePuzzleNextLevelRequest, DragPuzzlePieceRequest, PuzzleBoardSnapshotResponse,
|
||||||
PuzzleCellPositionResponse, PuzzleLeaderboardEntryResponse, PuzzleMergedGroupStateResponse,
|
PuzzleCellPositionResponse, PuzzleLeaderboardEntryResponse, PuzzleMergedGroupStateResponse,
|
||||||
@@ -59,16 +59,16 @@ use spacetime_client::{
|
|||||||
PuzzleAgentSessionCreateRecordInput, PuzzleAgentSessionRecord,
|
PuzzleAgentSessionCreateRecordInput, PuzzleAgentSessionRecord,
|
||||||
PuzzleAgentSuggestedActionRecord, PuzzleAnchorItemRecord, PuzzleAnchorPackRecord,
|
PuzzleAgentSuggestedActionRecord, PuzzleAnchorItemRecord, PuzzleAnchorPackRecord,
|
||||||
PuzzleAudioAssetRecord, PuzzleCreatorIntentRecord, PuzzleDraftLevelRecord,
|
PuzzleAudioAssetRecord, PuzzleCreatorIntentRecord, PuzzleDraftLevelRecord,
|
||||||
PuzzleFormDraftRecord, PuzzleFormDraftSaveRecordInput, PuzzleGeneratedImageCandidateRecord,
|
PuzzleFormDraftRecord, PuzzleFormDraftSaveRecordInput, PuzzleGalleryCardRecord,
|
||||||
PuzzleGeneratedImagesSaveRecordInput, PuzzleLeaderboardEntryRecord,
|
PuzzleGeneratedImageCandidateRecord, PuzzleGeneratedImagesSaveRecordInput,
|
||||||
PuzzleLeaderboardSubmitRecordInput, PuzzlePublishRecordInput, PuzzleRecommendedNextWorkRecord,
|
PuzzleLeaderboardEntryRecord, PuzzleLeaderboardSubmitRecordInput, PuzzlePublishRecordInput,
|
||||||
PuzzleResultDraftRecord, PuzzleResultPreviewBlockerRecord, PuzzleResultPreviewFindingRecord,
|
PuzzleRecommendedNextWorkRecord, PuzzleResultDraftRecord, PuzzleResultPreviewBlockerRecord,
|
||||||
PuzzleResultPreviewRecord, PuzzleRunDragRecordInput, PuzzleRunPauseRecordInput,
|
PuzzleResultPreviewFindingRecord, PuzzleResultPreviewRecord, PuzzleRunDragRecordInput,
|
||||||
PuzzleRunPropRecordInput, PuzzleRunRecord, PuzzleRunStartRecordInput, PuzzleRunSwapRecordInput,
|
PuzzleRunPauseRecordInput, PuzzleRunPropRecordInput, PuzzleRunRecord,
|
||||||
PuzzleSelectCoverImageRecordInput, PuzzleUiBackgroundSaveRecordInput,
|
PuzzleRunStartRecordInput, PuzzleRunSwapRecordInput, PuzzleSelectCoverImageRecordInput,
|
||||||
PuzzleWorkLikeReportRecordInput, PuzzleWorkPointIncentiveClaimRecordInput,
|
PuzzleUiBackgroundSaveRecordInput, PuzzleWorkLikeReportRecordInput,
|
||||||
PuzzleWorkProfileRecord, PuzzleWorkRemixRecordInput, PuzzleWorkUpsertRecordInput,
|
PuzzleWorkPointIncentiveClaimRecordInput, PuzzleWorkProfileRecord, PuzzleWorkRemixRecordInput,
|
||||||
SpacetimeClientError,
|
PuzzleWorkUpsertRecordInput, SpacetimeClientError,
|
||||||
};
|
};
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
|
|
||||||
@@ -103,6 +103,7 @@ use crate::{
|
|||||||
PuzzleAgentTurnRequest, build_failed_finalize_record_input, build_finalize_record_input,
|
PuzzleAgentTurnRequest, build_failed_finalize_record_input, build_finalize_record_input,
|
||||||
run_puzzle_agent_turn,
|
run_puzzle_agent_turn,
|
||||||
},
|
},
|
||||||
|
puzzle_gallery_cache::{build_puzzle_gallery_window_response, puzzle_gallery_cached_json},
|
||||||
request_context::RequestContext,
|
request_context::RequestContext,
|
||||||
state::AppState,
|
state::AppState,
|
||||||
vector_engine_audio_generation::{
|
vector_engine_audio_generation::{
|
||||||
@@ -1528,7 +1529,19 @@ pub async fn claim_puzzle_work_point_incentive(
|
|||||||
pub async fn list_puzzle_gallery(
|
pub async fn list_puzzle_gallery(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Extension(request_context): Extension<RequestContext>,
|
Extension(request_context): Extension<RequestContext>,
|
||||||
) -> Result<Json<Value>, Response> {
|
) -> Result<Response, Response> {
|
||||||
|
if let Some(response) = state.puzzle_gallery_cache().read_fresh_response().await {
|
||||||
|
crate::telemetry::record_puzzle_gallery_cache_hit();
|
||||||
|
return Ok(puzzle_gallery_cached_json(&request_context, response));
|
||||||
|
}
|
||||||
|
crate::telemetry::record_puzzle_gallery_cache_miss();
|
||||||
|
let _rebuild_guard = state.puzzle_gallery_cache().acquire_rebuild_guard().await;
|
||||||
|
if let Some(response) = state.puzzle_gallery_cache().read_fresh_response().await {
|
||||||
|
crate::telemetry::record_puzzle_gallery_cache_hit();
|
||||||
|
return Ok(puzzle_gallery_cached_json(&request_context, response));
|
||||||
|
}
|
||||||
|
|
||||||
|
let rebuild_started_at = std::time::Instant::now();
|
||||||
let items = state
|
let items = state
|
||||||
.spacetime_client()
|
.spacetime_client()
|
||||||
.list_puzzle_gallery()
|
.list_puzzle_gallery()
|
||||||
@@ -1541,15 +1554,32 @@ pub async fn list_puzzle_gallery(
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(json_success_body(
|
let response = build_puzzle_gallery_window_response(
|
||||||
Some(&request_context),
|
items
|
||||||
PuzzleGalleryResponse {
|
|
||||||
items: items
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|item| map_puzzle_work_summary_response(&state, item))
|
.map(|item| map_puzzle_gallery_card_response(&state, item))
|
||||||
.collect(),
|
.collect(),
|
||||||
},
|
);
|
||||||
))
|
let cached_response = state
|
||||||
|
.puzzle_gallery_cache()
|
||||||
|
.store_response(response)
|
||||||
|
.await
|
||||||
|
.map_err(|error| {
|
||||||
|
puzzle_error_response(
|
||||||
|
&request_context,
|
||||||
|
PUZZLE_GALLERY_PROVIDER,
|
||||||
|
AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_details(json!({
|
||||||
|
"provider": PUZZLE_GALLERY_PROVIDER,
|
||||||
|
"message": format!("拼图广场缓存序列化失败:{error}"),
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
crate::telemetry::record_puzzle_gallery_cache_rebuild(
|
||||||
|
rebuild_started_at.elapsed(),
|
||||||
|
cached_response.data_json_len(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(puzzle_gallery_cached_json(&request_context, cached_response))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_puzzle_gallery_detail(
|
pub async fn get_puzzle_gallery_detail(
|
||||||
|
|||||||
@@ -342,6 +342,49 @@ pub(super) fn map_puzzle_work_summary_response(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn map_puzzle_gallery_card_response(
|
||||||
|
state: &AppState,
|
||||||
|
item: PuzzleGalleryCardRecord,
|
||||||
|
) -> PuzzleWorkSummaryResponse {
|
||||||
|
let author = resolve_work_author_by_user_id(
|
||||||
|
state,
|
||||||
|
&item.owner_user_id,
|
||||||
|
Some(&item.author_display_name),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
PuzzleWorkSummaryResponse {
|
||||||
|
work_id: item.work_id,
|
||||||
|
profile_id: item.profile_id,
|
||||||
|
owner_user_id: item.owner_user_id,
|
||||||
|
source_session_id: item.source_session_id,
|
||||||
|
author_display_name: author.display_name,
|
||||||
|
work_title: item.work_title,
|
||||||
|
work_description: item.work_description,
|
||||||
|
level_name: item.level_name,
|
||||||
|
summary: item.summary,
|
||||||
|
theme_tags: item.theme_tags,
|
||||||
|
cover_image_src: item.cover_image_src,
|
||||||
|
cover_asset_id: item.cover_asset_id,
|
||||||
|
publication_status: item.publication_status,
|
||||||
|
updated_at: item.updated_at,
|
||||||
|
published_at: item.published_at,
|
||||||
|
play_count: item.play_count,
|
||||||
|
remix_count: item.remix_count,
|
||||||
|
like_count: item.like_count,
|
||||||
|
recent_play_count_7d: item.recent_play_count_7d,
|
||||||
|
point_incentive_total_half_points: item.point_incentive_total_half_points,
|
||||||
|
point_incentive_claimed_points: item.point_incentive_claimed_points,
|
||||||
|
point_incentive_total_points: item.point_incentive_total_half_points as f64 / 2.0,
|
||||||
|
point_incentive_claimable_points: item
|
||||||
|
.point_incentive_total_half_points
|
||||||
|
.saturating_div(2)
|
||||||
|
.saturating_sub(item.point_incentive_claimed_points),
|
||||||
|
publish_ready: item.publish_ready,
|
||||||
|
generation_status: item.generation_status,
|
||||||
|
levels: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn map_puzzle_work_profile_response(
|
pub(super) fn map_puzzle_work_profile_response(
|
||||||
state: &AppState,
|
state: &AppState,
|
||||||
item: PuzzleWorkProfileRecord,
|
item: PuzzleWorkProfileRecord,
|
||||||
|
|||||||
208
server-rs/crates/api-server/src/puzzle_gallery_cache.rs
Normal file
208
server-rs/crates/api-server/src/puzzle_gallery_cache.rs
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
use std::{
|
||||||
|
sync::Arc,
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
|
use axum::response::Response;
|
||||||
|
use bytes::Bytes;
|
||||||
|
use shared_contracts::{
|
||||||
|
puzzle_gallery::{PuzzleGalleryResponse, PuzzleGalleryWorkRefResponse},
|
||||||
|
puzzle_works::PuzzleWorkSummaryResponse,
|
||||||
|
};
|
||||||
|
use tokio::{
|
||||||
|
sync::{Mutex, MutexGuard, RwLock},
|
||||||
|
time,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{api_response::json_success_data_bytes_response, request_context::RequestContext};
|
||||||
|
|
||||||
|
const PUZZLE_GALLERY_PRIMARY_ITEM_COUNT: usize = 10;
|
||||||
|
const PUZZLE_GALLERY_PREVIEW_REF_COUNT: usize = 10;
|
||||||
|
const PUZZLE_GALLERY_CACHE_TTL: Duration = Duration::from_secs(5);
|
||||||
|
const PUZZLE_GALLERY_CACHE_MAX_IDLE: Duration = Duration::from_secs(300);
|
||||||
|
const PUZZLE_GALLERY_CACHE_CLEANUP_INTERVAL: Duration = Duration::from_secs(60);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct PuzzleGalleryCache {
|
||||||
|
inner: Arc<RwLock<Option<PuzzleGalleryCacheEntry>>>,
|
||||||
|
rebuild_lock: Arc<Mutex<()>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct PuzzleGalleryCacheEntry {
|
||||||
|
data_json: Bytes,
|
||||||
|
built_at: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct PuzzleGalleryCachedResponse {
|
||||||
|
data_json: Bytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PuzzleGalleryCachedResponse {
|
||||||
|
pub fn data_json_len(&self) -> usize {
|
||||||
|
self.data_json.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PuzzleGalleryCache {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Arc::new(RwLock::new(None)),
|
||||||
|
rebuild_lock: Arc::new(Mutex::new(())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn acquire_rebuild_guard(&self) -> MutexGuard<'_, ()> {
|
||||||
|
self.rebuild_lock.lock().await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn read_fresh_response(&self) -> Option<PuzzleGalleryCachedResponse> {
|
||||||
|
let guard = self.inner.read().await;
|
||||||
|
let entry = guard.as_ref()?;
|
||||||
|
let now = Instant::now();
|
||||||
|
if now.duration_since(entry.built_at) > PUZZLE_GALLERY_CACHE_TTL {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(PuzzleGalleryCachedResponse {
|
||||||
|
data_json: entry.data_json.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn store_response(
|
||||||
|
&self,
|
||||||
|
response: PuzzleGalleryResponse,
|
||||||
|
) -> Result<PuzzleGalleryCachedResponse, serde_json::Error> {
|
||||||
|
let now = Instant::now();
|
||||||
|
let cached = PuzzleGalleryCachedResponse {
|
||||||
|
data_json: Bytes::from(serde_json::to_vec(&response)?),
|
||||||
|
};
|
||||||
|
*self.inner.write().await = Some(PuzzleGalleryCacheEntry {
|
||||||
|
data_json: cached.data_json.clone(),
|
||||||
|
built_at: now,
|
||||||
|
});
|
||||||
|
Ok(cached)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn_cleanup_task(&self) {
|
||||||
|
let cache = self.clone();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let mut interval = time::interval(PUZZLE_GALLERY_CACHE_CLEANUP_INTERVAL);
|
||||||
|
loop {
|
||||||
|
interval.tick().await;
|
||||||
|
cache.cleanup_idle_entry().await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn cleanup_idle_entry(&self) {
|
||||||
|
let mut guard = self.inner.write().await;
|
||||||
|
if let Some(entry) = guard.as_ref()
|
||||||
|
&& Instant::now().duration_since(entry.built_at) > PUZZLE_GALLERY_CACHE_MAX_IDLE
|
||||||
|
{
|
||||||
|
*guard = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_puzzle_gallery_window_response(
|
||||||
|
items: Vec<PuzzleWorkSummaryResponse>,
|
||||||
|
) -> PuzzleGalleryResponse {
|
||||||
|
let total_count = items.len().min(u32::MAX as usize) as u32;
|
||||||
|
let preview_refs = items
|
||||||
|
.iter()
|
||||||
|
.skip(PUZZLE_GALLERY_PRIMARY_ITEM_COUNT)
|
||||||
|
.take(PUZZLE_GALLERY_PREVIEW_REF_COUNT)
|
||||||
|
.map(|item| PuzzleGalleryWorkRefResponse {
|
||||||
|
work_id: item.work_id.clone(),
|
||||||
|
profile_id: item.profile_id.clone(),
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let next_cursor = items
|
||||||
|
.get(PUZZLE_GALLERY_PRIMARY_ITEM_COUNT + PUZZLE_GALLERY_PREVIEW_REF_COUNT)
|
||||||
|
.map(|item| item.profile_id.clone());
|
||||||
|
let has_more =
|
||||||
|
items.len() > PUZZLE_GALLERY_PRIMARY_ITEM_COUNT + PUZZLE_GALLERY_PREVIEW_REF_COUNT;
|
||||||
|
|
||||||
|
PuzzleGalleryResponse {
|
||||||
|
items: items
|
||||||
|
.into_iter()
|
||||||
|
.take(PUZZLE_GALLERY_PRIMARY_ITEM_COUNT)
|
||||||
|
.collect(),
|
||||||
|
preview_refs,
|
||||||
|
has_more,
|
||||||
|
next_cursor,
|
||||||
|
total_count,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn puzzle_gallery_cached_json(
|
||||||
|
request_context: &RequestContext,
|
||||||
|
response: PuzzleGalleryCachedResponse,
|
||||||
|
) -> Response {
|
||||||
|
json_success_data_bytes_response(Some(request_context), response.data_json)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn build_summary(index: usize) -> PuzzleWorkSummaryResponse {
|
||||||
|
PuzzleWorkSummaryResponse {
|
||||||
|
work_id: format!("work-{index}"),
|
||||||
|
profile_id: format!("profile-{index}"),
|
||||||
|
owner_user_id: "user-1".to_string(),
|
||||||
|
source_session_id: None,
|
||||||
|
author_display_name: "作者".to_string(),
|
||||||
|
work_title: format!("作品 {index}"),
|
||||||
|
work_description: "描述".to_string(),
|
||||||
|
level_name: "第一关".to_string(),
|
||||||
|
summary: "摘要".to_string(),
|
||||||
|
theme_tags: Vec::new(),
|
||||||
|
cover_image_src: None,
|
||||||
|
cover_asset_id: None,
|
||||||
|
publication_status: "published".to_string(),
|
||||||
|
updated_at: "2026-05-01T00:00:00Z".to_string(),
|
||||||
|
published_at: None,
|
||||||
|
play_count: 0,
|
||||||
|
remix_count: 0,
|
||||||
|
like_count: 0,
|
||||||
|
recent_play_count_7d: 0,
|
||||||
|
point_incentive_total_half_points: 0,
|
||||||
|
point_incentive_claimed_points: 0,
|
||||||
|
point_incentive_total_points: 0.0,
|
||||||
|
point_incentive_claimable_points: 0,
|
||||||
|
publish_ready: true,
|
||||||
|
generation_status: Some("ready".to_string()),
|
||||||
|
levels: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn build_window_returns_primary_cards_preview_refs_and_cursor() {
|
||||||
|
let response =
|
||||||
|
build_puzzle_gallery_window_response((0..25).map(build_summary).collect::<Vec<_>>());
|
||||||
|
|
||||||
|
assert_eq!(response.total_count, 25);
|
||||||
|
assert_eq!(response.items.len(), 10);
|
||||||
|
assert_eq!(response.preview_refs.len(), 10);
|
||||||
|
assert_eq!(response.items[0].profile_id, "profile-0");
|
||||||
|
assert_eq!(response.items[9].profile_id, "profile-9");
|
||||||
|
assert_eq!(response.preview_refs[0].profile_id, "profile-10");
|
||||||
|
assert_eq!(response.preview_refs[9].profile_id, "profile-19");
|
||||||
|
assert!(response.has_more);
|
||||||
|
assert_eq!(response.next_cursor.as_deref(), Some("profile-20"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn build_window_handles_short_gallery_without_more_cursor() {
|
||||||
|
let response =
|
||||||
|
build_puzzle_gallery_window_response((0..8).map(build_summary).collect::<Vec<_>>());
|
||||||
|
|
||||||
|
assert_eq!(response.total_count, 8);
|
||||||
|
assert_eq!(response.items.len(), 8);
|
||||||
|
assert!(response.preview_refs.is_empty());
|
||||||
|
assert!(!response.has_more);
|
||||||
|
assert_eq!(response.next_cursor, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,20 +27,25 @@ use shared_contracts::creation_entry_config::CreationEntryConfigResponse;
|
|||||||
use shared_contracts::creative_agent::CreativeAgentSessionSnapshot;
|
use shared_contracts::creative_agent::CreativeAgentSessionSnapshot;
|
||||||
use spacetime_client::{SpacetimeClient, SpacetimeClientConfig, SpacetimeClientError};
|
use spacetime_client::{SpacetimeClient, SpacetimeClientConfig, SpacetimeClientError};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
use tokio::sync::Semaphore;
|
||||||
use tracing::{info, warn};
|
use tracing::{info, warn};
|
||||||
|
|
||||||
use crate::config::AppConfig;
|
use crate::config::AppConfig;
|
||||||
|
use crate::puzzle_gallery_cache::PuzzleGalleryCache;
|
||||||
use crate::wechat_pay::{WechatPayClient, map_wechat_pay_init_error};
|
use crate::wechat_pay::{WechatPayClient, map_wechat_pay_init_error};
|
||||||
use crate::wechat_provider::build_wechat_provider;
|
use crate::wechat_provider::build_wechat_provider;
|
||||||
|
|
||||||
const ADMIN_ROLE: &str = "admin";
|
const ADMIN_ROLE: &str = "admin";
|
||||||
|
|
||||||
|
pub type HttpRequestPermitPool = Semaphore;
|
||||||
|
|
||||||
// 当前阶段先保留最小共享状态壳,后续逐步接入配置、客户端与平台适配。
|
// 当前阶段先保留最小共享状态壳,后续逐步接入配置、客户端与平台适配。
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
// 配置会在后续中间件、路由和平台适配接入时逐步消费。
|
// 配置会在后续中间件、路由和平台适配接入时逐步消费。
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub config: AppConfig,
|
pub config: AppConfig,
|
||||||
|
http_request_permit_pool: Option<Arc<HttpRequestPermitPool>>,
|
||||||
auth_jwt_config: JwtConfig,
|
auth_jwt_config: JwtConfig,
|
||||||
admin_runtime: Option<AdminRuntime>,
|
admin_runtime: Option<AdminRuntime>,
|
||||||
refresh_cookie_config: RefreshCookieConfig,
|
refresh_cookie_config: RefreshCookieConfig,
|
||||||
@@ -60,6 +65,7 @@ pub struct AppState {
|
|||||||
#[cfg_attr(not(test), allow(dead_code))]
|
#[cfg_attr(not(test), allow(dead_code))]
|
||||||
ai_task_service: AiTaskService,
|
ai_task_service: AiTaskService,
|
||||||
spacetime_client: SpacetimeClient,
|
spacetime_client: SpacetimeClient,
|
||||||
|
puzzle_gallery_cache: PuzzleGalleryCache,
|
||||||
llm_client: Option<LlmClient>,
|
llm_client: Option<LlmClient>,
|
||||||
creative_agent_gpt5_client: Option<LlmClient>,
|
creative_agent_gpt5_client: Option<LlmClient>,
|
||||||
creative_agent_executor: Arc<MockLangChainRustAgentExecutor>,
|
creative_agent_executor: Arc<MockLangChainRustAgentExecutor>,
|
||||||
@@ -192,9 +198,14 @@ impl AppState {
|
|||||||
});
|
});
|
||||||
let llm_client = build_llm_client(&config)?;
|
let llm_client = build_llm_client(&config)?;
|
||||||
let creative_agent_gpt5_client = build_creative_agent_gpt5_client(&config)?;
|
let creative_agent_gpt5_client = build_creative_agent_gpt5_client(&config)?;
|
||||||
|
let http_request_permit_pool = config
|
||||||
|
.max_concurrent_requests
|
||||||
|
.map(HttpRequestPermitPool::new)
|
||||||
|
.map(Arc::new);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
config,
|
config,
|
||||||
|
http_request_permit_pool,
|
||||||
auth_jwt_config,
|
auth_jwt_config,
|
||||||
admin_runtime,
|
admin_runtime,
|
||||||
refresh_cookie_config,
|
refresh_cookie_config,
|
||||||
@@ -214,6 +225,7 @@ impl AppState {
|
|||||||
wechat_pay_client,
|
wechat_pay_client,
|
||||||
ai_task_service,
|
ai_task_service,
|
||||||
spacetime_client,
|
spacetime_client,
|
||||||
|
puzzle_gallery_cache: PuzzleGalleryCache::new(),
|
||||||
llm_client,
|
llm_client,
|
||||||
creative_agent_gpt5_client,
|
creative_agent_gpt5_client,
|
||||||
creative_agent_executor: Arc::new(MockLangChainRustAgentExecutor),
|
creative_agent_executor: Arc::new(MockLangChainRustAgentExecutor),
|
||||||
@@ -235,6 +247,10 @@ impl AppState {
|
|||||||
&self.refresh_cookie_config
|
&self.refresh_cookie_config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn http_request_permit_pool(&self) -> Option<Arc<HttpRequestPermitPool>> {
|
||||||
|
self.http_request_permit_pool.clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn upsert_creation_entry_type_config(
|
pub async fn upsert_creation_entry_type_config(
|
||||||
&self,
|
&self,
|
||||||
input: module_runtime::CreationEntryTypeAdminUpsertInput,
|
input: module_runtime::CreationEntryTypeAdminUpsertInput,
|
||||||
@@ -464,6 +480,10 @@ impl AppState {
|
|||||||
&self.spacetime_client
|
&self.spacetime_client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn puzzle_gallery_cache(&self) -> &PuzzleGalleryCache {
|
||||||
|
&self.puzzle_gallery_cache
|
||||||
|
}
|
||||||
|
|
||||||
pub fn llm_client(&self) -> Option<&LlmClient> {
|
pub fn llm_client(&self) -> Option<&LlmClient> {
|
||||||
self.llm_client.as_ref()
|
self.llm_client.as_ref()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,19 @@ use axum::{
|
|||||||
http::{HeaderMap, Request, Response},
|
http::{HeaderMap, Request, Response},
|
||||||
middleware::Next,
|
middleware::Next,
|
||||||
};
|
};
|
||||||
|
use http_body_util::BodyExt;
|
||||||
use opentelemetry::{KeyValue, global, metrics::Counter};
|
use opentelemetry::{KeyValue, global, metrics::Counter};
|
||||||
|
use std::sync::{
|
||||||
|
Arc, OnceLock,
|
||||||
|
atomic::{AtomicI64, Ordering},
|
||||||
|
};
|
||||||
use tracing::{info, warn};
|
use tracing::{info, warn};
|
||||||
|
|
||||||
use crate::{request_context::resolve_request_id, state::AppState};
|
use crate::{request_context::resolve_request_id, state::AppState};
|
||||||
|
|
||||||
|
static HTTP_RESPONSE_BODY_IN_FLIGHT: AtomicI64 = AtomicI64::new(0);
|
||||||
|
static HTTP_REQUEST_PERMITS_AVAILABLE: OnceLock<Arc<AtomicI64>> = OnceLock::new();
|
||||||
|
|
||||||
// 集中维护 api-server HTTP 观测,避免在 handler 中散落高基数字段或重复创建 instrument。
|
// 集中维护 api-server HTTP 观测,避免在 handler 中散落高基数字段或重复创建 instrument。
|
||||||
pub async fn record_http_observability(
|
pub async fn record_http_observability(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
@@ -67,7 +75,46 @@ pub async fn record_http_observability(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
response
|
track_response_body_in_flight(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn update_http_request_permits_available(available: usize) {
|
||||||
|
let gauge = HTTP_REQUEST_PERMITS_AVAILABLE.get_or_init(|| {
|
||||||
|
let gauge = Arc::new(AtomicI64::new(0));
|
||||||
|
register_http_request_permits_available_metric(gauge.clone());
|
||||||
|
gauge
|
||||||
|
});
|
||||||
|
gauge.store(available.min(i64::MAX as usize) as i64, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn record_puzzle_gallery_cache_hit() {
|
||||||
|
puzzle_gallery_cache_metrics().hits.add(1, &[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn record_puzzle_gallery_cache_miss() {
|
||||||
|
puzzle_gallery_cache_metrics().misses.add(1, &[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn record_puzzle_gallery_cache_rebuild(duration: std::time::Duration, data_bytes: usize) {
|
||||||
|
let metrics = puzzle_gallery_cache_metrics();
|
||||||
|
metrics.rebuilds.add(1, &[]);
|
||||||
|
metrics
|
||||||
|
.rebuild_duration
|
||||||
|
.record(duration.as_secs_f64(), &[]);
|
||||||
|
metrics
|
||||||
|
.data_json_bytes
|
||||||
|
.record(data_bytes.min(u64::MAX as usize) as u64, &[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn track_response_body_in_flight(response: Response<Body>) -> Response<Body> {
|
||||||
|
response.map(|body| {
|
||||||
|
HTTP_RESPONSE_BODY_IN_FLIGHT.fetch_add(1, Ordering::Relaxed);
|
||||||
|
let guard = ResponseBodyInFlightGuard;
|
||||||
|
Body::new(body.map_frame(move |frame| {
|
||||||
|
let _guard = &guard;
|
||||||
|
frame
|
||||||
|
}))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HttpMetrics {
|
struct HttpMetrics {
|
||||||
@@ -76,6 +123,22 @@ struct HttpMetrics {
|
|||||||
duration: opentelemetry::metrics::Histogram<f64>,
|
duration: opentelemetry::metrics::Histogram<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PuzzleGalleryCacheMetrics {
|
||||||
|
hits: Counter<u64>,
|
||||||
|
misses: Counter<u64>,
|
||||||
|
rebuilds: Counter<u64>,
|
||||||
|
rebuild_duration: opentelemetry::metrics::Histogram<f64>,
|
||||||
|
data_json_bytes: opentelemetry::metrics::Histogram<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ResponseBodyInFlightGuard;
|
||||||
|
|
||||||
|
impl Drop for ResponseBodyInFlightGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
HTTP_RESPONSE_BODY_IN_FLIGHT.fetch_sub(1, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn http_metrics() -> &'static HttpMetrics {
|
fn http_metrics() -> &'static HttpMetrics {
|
||||||
static METRICS: std::sync::OnceLock<HttpMetrics> = std::sync::OnceLock::new();
|
static METRICS: std::sync::OnceLock<HttpMetrics> = std::sync::OnceLock::new();
|
||||||
METRICS.get_or_init(|| {
|
METRICS.get_or_init(|| {
|
||||||
@@ -99,6 +162,64 @@ fn http_metrics() -> &'static HttpMetrics {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn puzzle_gallery_cache_metrics() -> &'static PuzzleGalleryCacheMetrics {
|
||||||
|
static METRICS: std::sync::OnceLock<PuzzleGalleryCacheMetrics> = std::sync::OnceLock::new();
|
||||||
|
METRICS.get_or_init(|| {
|
||||||
|
let meter = global::meter("genarrative-api");
|
||||||
|
PuzzleGalleryCacheMetrics {
|
||||||
|
hits: meter
|
||||||
|
.u64_counter("genarrative.puzzle_gallery.cache.hits")
|
||||||
|
.with_description("Puzzle gallery response cache hits")
|
||||||
|
.build(),
|
||||||
|
misses: meter
|
||||||
|
.u64_counter("genarrative.puzzle_gallery.cache.misses")
|
||||||
|
.with_description("Puzzle gallery response cache misses")
|
||||||
|
.build(),
|
||||||
|
rebuilds: meter
|
||||||
|
.u64_counter("genarrative.puzzle_gallery.cache.rebuilds")
|
||||||
|
.with_description("Puzzle gallery response cache rebuild count")
|
||||||
|
.build(),
|
||||||
|
rebuild_duration: meter
|
||||||
|
.f64_histogram("genarrative.puzzle_gallery.cache.rebuild.duration")
|
||||||
|
.with_unit("s")
|
||||||
|
.with_description("Puzzle gallery response cache rebuild duration")
|
||||||
|
.build(),
|
||||||
|
data_json_bytes: meter
|
||||||
|
.u64_histogram("genarrative.puzzle_gallery.cache.data_json_bytes")
|
||||||
|
.with_unit("By")
|
||||||
|
.with_description("Serialized puzzle gallery data JSON size")
|
||||||
|
.build(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_http_request_permits_available_metric(gauge: Arc<AtomicI64>) {
|
||||||
|
let meter = global::meter("genarrative-api");
|
||||||
|
meter
|
||||||
|
.i64_observable_up_down_counter("genarrative.http.server.request_permits.available")
|
||||||
|
.with_unit("{permit}")
|
||||||
|
.with_description("Available api-server HTTP backpressure permits")
|
||||||
|
.with_callback(move |observer| {
|
||||||
|
observer.observe(gauge.load(Ordering::Relaxed), &[]);
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn register_http_runtime_metrics() {
|
||||||
|
static REGISTERED: OnceLock<()> = OnceLock::new();
|
||||||
|
REGISTERED.get_or_init(|| {
|
||||||
|
let meter = global::meter("genarrative-api");
|
||||||
|
meter
|
||||||
|
.i64_observable_up_down_counter("genarrative.http.server.response_bodies.in_flight")
|
||||||
|
.with_unit("{response}")
|
||||||
|
.with_description("HTTP response bodies still owned by Axum/Hyper")
|
||||||
|
.with_callback(|observer| {
|
||||||
|
observer.observe(HTTP_RESPONSE_BODY_IN_FLIGHT.load(Ordering::Relaxed), &[]);
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn http_base_labels(method: String, route: String) -> Vec<KeyValue> {
|
fn http_base_labels(method: String, route: String) -> Vec<KeyValue> {
|
||||||
vec![
|
vec![
|
||||||
KeyValue::new("http.request.method", method),
|
KeyValue::new("http.request.method", method),
|
||||||
|
|||||||
1
server-rs/crates/module-bark-battle/src/application.rs
Normal file
1
server-rs/crates/module-bark-battle/src/application.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
//! 中文注释:汪汪声浪领域应用服务预留落位,当前规则仍集中在 domain/scoring。
|
||||||
1
server-rs/crates/module-bark-battle/src/commands.rs
Normal file
1
server-rs/crates/module-bark-battle/src/commands.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
//! 中文注释:汪汪声浪命令归一化预留落位,当前无独立命令构造。
|
||||||
1
server-rs/crates/module-bark-battle/src/errors.rs
Normal file
1
server-rs/crates/module-bark-battle/src/errors.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
//! 中文注释:汪汪声浪领域错误预留落位,当前复用调用方错误文本。
|
||||||
1
server-rs/crates/module-bark-battle/src/events.rs
Normal file
1
server-rs/crates/module-bark-battle/src/events.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
//! 中文注释:汪汪声浪领域事件预留落位,当前不导出独立事件类型。
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
|
mod application;
|
||||||
|
mod commands;
|
||||||
pub mod domain;
|
pub mod domain;
|
||||||
|
mod errors;
|
||||||
|
mod events;
|
||||||
pub mod scoring;
|
pub mod scoring;
|
||||||
|
|
||||||
pub use domain::*;
|
pub use domain::*;
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ pub struct BigFishWorkRemixInput {
|
|||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct BigFishWorksProcedureResult {
|
pub struct BigFishWorksProcedureResult {
|
||||||
pub ok: bool,
|
pub ok: bool,
|
||||||
pub items_json: Option<String>,
|
pub items: Vec<BigFishWorkSummarySnapshot>,
|
||||||
pub error_message: Option<String>,
|
pub error_message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,9 +188,9 @@ pub struct BigFishInputSubmitInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct BigFishRunProcedureResult {
|
pub struct BigFishRunProcedureResult {
|
||||||
pub ok: bool,
|
pub ok: bool,
|
||||||
pub run_json: Option<String>,
|
pub run: Option<BigFishRuntimeSnapshot>,
|
||||||
pub error_message: Option<String>,
|
pub error_message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|||||||
1
server-rs/crates/module-creative-agent/src/events.rs
Normal file
1
server-rs/crates/module-creative-agent/src/events.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
//! 中文注释:创意 Agent 领域事件预留落位,当前流程不导出独立事件类型。
|
||||||
@@ -2,6 +2,7 @@ mod application;
|
|||||||
mod commands;
|
mod commands;
|
||||||
mod domain;
|
mod domain;
|
||||||
mod errors;
|
mod errors;
|
||||||
|
mod events;
|
||||||
|
|
||||||
pub use application::*;
|
pub use application::*;
|
||||||
pub use commands::*;
|
pub use commands::*;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use crate::{domain::*, errors::PuzzleFieldError};
|
|||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct PuzzleAgentSessionProcedureResult {
|
pub struct PuzzleAgentSessionProcedureResult {
|
||||||
pub ok: bool,
|
pub ok: bool,
|
||||||
pub session_json: Option<String>,
|
pub session: Option<PuzzleAgentSessionSnapshot>,
|
||||||
pub error_message: Option<String>,
|
pub error_message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ pub struct PuzzleAgentSessionProcedureResult {
|
|||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct PuzzleWorksProcedureResult {
|
pub struct PuzzleWorksProcedureResult {
|
||||||
pub ok: bool,
|
pub ok: bool,
|
||||||
pub items_json: Option<String>,
|
pub items: Vec<PuzzleWorkProfile>,
|
||||||
pub error_message: Option<String>,
|
pub error_message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,15 +32,15 @@ pub struct PuzzleWorksProcedureResult {
|
|||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct PuzzleWorkProcedureResult {
|
pub struct PuzzleWorkProcedureResult {
|
||||||
pub ok: bool,
|
pub ok: bool,
|
||||||
pub item_json: Option<String>,
|
pub item: Option<PuzzleWorkProfile>,
|
||||||
pub error_message: Option<String>,
|
pub error_message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct PuzzleRunProcedureResult {
|
pub struct PuzzleRunProcedureResult {
|
||||||
pub ok: bool,
|
pub ok: bool,
|
||||||
pub run_json: Option<String>,
|
pub run: Option<PuzzleRunSnapshot>,
|
||||||
pub error_message: Option<String>,
|
pub error_message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1
server-rs/crates/module-visual-novel/src/commands.rs
Normal file
1
server-rs/crates/module-visual-novel/src/commands.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
//! 中文注释:视觉小说命令归一化预留落位,当前命令校验仍由 application 承接。
|
||||||
1
server-rs/crates/module-visual-novel/src/events.rs
Normal file
1
server-rs/crates/module-visual-novel/src/events.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
//! 中文注释:视觉小说领域事件预留落位,当前不导出独立事件类型。
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
mod application;
|
mod application;
|
||||||
|
mod commands;
|
||||||
mod domain;
|
mod domain;
|
||||||
mod errors;
|
mod errors;
|
||||||
|
mod events;
|
||||||
|
|
||||||
pub use application::*;
|
pub use application::*;
|
||||||
pub use domain::*;
|
pub use domain::*;
|
||||||
|
|||||||
@@ -6,6 +6,21 @@ use crate::puzzle_works::{PuzzleWorkProfileResponse, PuzzleWorkSummaryResponse};
|
|||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct PuzzleGalleryResponse {
|
pub struct PuzzleGalleryResponse {
|
||||||
pub items: Vec<PuzzleWorkSummaryResponse>,
|
pub items: Vec<PuzzleWorkSummaryResponse>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub preview_refs: Vec<PuzzleGalleryWorkRefResponse>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub has_more: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub next_cursor: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub total_count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct PuzzleGalleryWorkRefResponse {
|
||||||
|
pub work_id: String,
|
||||||
|
pub profile_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ use crate::module_bindings::record_big_fish_play_procedure::record_big_fish_play
|
|||||||
use crate::module_bindings::remix_big_fish_work_procedure::remix_big_fish_work;
|
use crate::module_bindings::remix_big_fish_work_procedure::remix_big_fish_work;
|
||||||
use crate::module_bindings::start_big_fish_run_procedure::start_big_fish_run;
|
use crate::module_bindings::start_big_fish_run_procedure::start_big_fish_run;
|
||||||
use crate::module_bindings::submit_big_fish_input_procedure::submit_big_fish_input;
|
use crate::module_bindings::submit_big_fish_input_procedure::submit_big_fish_input;
|
||||||
use module_big_fish::PUBLIC_BIG_FISH_GALLERY_OWNER_USER_ID;
|
|
||||||
|
|
||||||
impl SpacetimeClient {
|
impl SpacetimeClient {
|
||||||
pub async fn create_big_fish_session(
|
pub async fn create_big_fish_session(
|
||||||
@@ -75,10 +74,29 @@ impl SpacetimeClient {
|
|||||||
pub async fn list_big_fish_gallery(
|
pub async fn list_big_fish_gallery(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<Vec<BigFishWorkSummaryRecord>, SpacetimeClientError> {
|
) -> Result<Vec<BigFishWorkSummaryRecord>, SpacetimeClientError> {
|
||||||
self.list_big_fish_works_with_input(BigFishWorksListInput {
|
self.read_after_connect("list_big_fish_gallery", move |connection| {
|
||||||
// 中文注释:公开广场读取只依赖 published_only,但旧部署模块会先校验 owner_user_id 非空。
|
let recent_play_counts = public_work_recent_play_counts(connection, "big-fish");
|
||||||
owner_user_id: PUBLIC_BIG_FISH_GALLERY_OWNER_USER_ID.to_string(),
|
let mut items = connection
|
||||||
published_only: true,
|
.db()
|
||||||
|
.big_fish_gallery_view()
|
||||||
|
.iter()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
items.sort_by(|left, right| {
|
||||||
|
right
|
||||||
|
.updated_at_micros
|
||||||
|
.cmp(&left.updated_at_micros)
|
||||||
|
.then_with(|| left.source_session_id.cmp(&right.source_session_id))
|
||||||
|
});
|
||||||
|
Ok(items
|
||||||
|
.into_iter()
|
||||||
|
.map(|item| {
|
||||||
|
let recent_play_count_7d = recent_play_counts
|
||||||
|
.get(&item.source_session_id)
|
||||||
|
.copied()
|
||||||
|
.unwrap_or(0);
|
||||||
|
map_big_fish_gallery_view_row(item, recent_play_count_7d)
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,6 +181,55 @@ impl SpacetimeClient {
|
|||||||
pub async fn list_custom_world_gallery_entries(
|
pub async fn list_custom_world_gallery_entries(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<Vec<CustomWorldGalleryEntryRecord>, SpacetimeClientError> {
|
) -> Result<Vec<CustomWorldGalleryEntryRecord>, SpacetimeClientError> {
|
||||||
|
let records = self.read_custom_world_gallery_entries_from_cache().await?;
|
||||||
|
if !records.is_empty()
|
||||||
|
|| self
|
||||||
|
.custom_world_gallery_legacy_sync_attempted
|
||||||
|
.swap(true, std::sync::atomic::Ordering::SeqCst)
|
||||||
|
{
|
||||||
|
return Ok(records);
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = self
|
||||||
|
.sync_custom_world_gallery_entries_via_legacy_procedure()
|
||||||
|
.await;
|
||||||
|
self.read_custom_world_gallery_entries_from_cache().await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_custom_world_gallery_entries_from_cache(
|
||||||
|
&self,
|
||||||
|
) -> Result<Vec<CustomWorldGalleryEntryRecord>, SpacetimeClientError> {
|
||||||
|
self.read_after_connect("list_custom_world_gallery", move |connection| {
|
||||||
|
let recent_play_counts = public_work_recent_play_counts(connection, "custom-world");
|
||||||
|
let mut entries = connection
|
||||||
|
.db()
|
||||||
|
.custom_world_gallery_entry()
|
||||||
|
.iter()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
entries.sort_by(|left, right| {
|
||||||
|
right
|
||||||
|
.published_at
|
||||||
|
.cmp(&left.published_at)
|
||||||
|
.then(right.updated_at.cmp(&left.updated_at))
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(entries
|
||||||
|
.into_iter()
|
||||||
|
.map(|entry| {
|
||||||
|
let recent_play_count_7d = recent_play_counts
|
||||||
|
.get(&entry.profile_id)
|
||||||
|
.copied()
|
||||||
|
.unwrap_or(0);
|
||||||
|
map_custom_world_gallery_entry_row(entry, recent_play_count_7d)
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn sync_custom_world_gallery_entries_via_legacy_procedure(
|
||||||
|
&self,
|
||||||
|
) -> Result<(), SpacetimeClientError> {
|
||||||
self.call_after_connect(
|
self.call_after_connect(
|
||||||
"list_custom_world_gallery_entries",
|
"list_custom_world_gallery_entries",
|
||||||
move |connection, sender| {
|
move |connection, sender| {
|
||||||
@@ -188,8 +237,8 @@ impl SpacetimeClient {
|
|||||||
.procedures()
|
.procedures()
|
||||||
.list_custom_world_gallery_entries_then(move |_, result| {
|
.list_custom_world_gallery_entries_then(move |_, result| {
|
||||||
let mapped = result
|
let mapped = result
|
||||||
.map_err(SpacetimeClientError::from_sdk_error)
|
.map(|_| ())
|
||||||
.and_then(map_custom_world_gallery_list_result);
|
.map_err(SpacetimeClientError::from_sdk_error);
|
||||||
send_once(&sender, mapped);
|
send_once(&sender, mapped);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ pub use mapper::{
|
|||||||
PuzzleAgentSessionRecord, PuzzleAgentSuggestedActionRecord, PuzzleAnchorItemRecord,
|
PuzzleAgentSessionRecord, PuzzleAgentSuggestedActionRecord, PuzzleAnchorItemRecord,
|
||||||
PuzzleAnchorPackRecord, PuzzleAudioAssetRecord, PuzzleBoardRecord, PuzzleCellPositionRecord,
|
PuzzleAnchorPackRecord, PuzzleAudioAssetRecord, PuzzleBoardRecord, PuzzleCellPositionRecord,
|
||||||
PuzzleCreatorIntentRecord, PuzzleDraftLevelRecord, PuzzleFormDraftRecord,
|
PuzzleCreatorIntentRecord, PuzzleDraftLevelRecord, PuzzleFormDraftRecord,
|
||||||
PuzzleFormDraftSaveRecordInput, PuzzleGeneratedImageCandidateRecord,
|
PuzzleFormDraftSaveRecordInput, PuzzleGalleryCardRecord, PuzzleGeneratedImageCandidateRecord,
|
||||||
PuzzleGeneratedImagesSaveRecordInput, PuzzleLeaderboardEntryRecord,
|
PuzzleGeneratedImagesSaveRecordInput, PuzzleLeaderboardEntryRecord,
|
||||||
PuzzleLeaderboardSubmitRecordInput, PuzzleMergedGroupRecord, PuzzlePieceStateRecord,
|
PuzzleLeaderboardSubmitRecordInput, PuzzleMergedGroupRecord, PuzzlePieceStateRecord,
|
||||||
PuzzlePublishRecordInput, PuzzleRecommendedNextWorkRecord, PuzzleResultDraftRecord,
|
PuzzlePublishRecordInput, PuzzleRecommendedNextWorkRecord, PuzzleResultDraftRecord,
|
||||||
@@ -96,6 +96,7 @@ pub mod story_runtime;
|
|||||||
pub mod visual_novel;
|
pub mod visual_novel;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
error::Error,
|
error::Error,
|
||||||
fmt,
|
fmt,
|
||||||
sync::atomic::{AtomicBool, Ordering},
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
@@ -225,7 +226,7 @@ use module_story::{
|
|||||||
use shared_kernel::format_timestamp_micros;
|
use shared_kernel::format_timestamp_micros;
|
||||||
use spacetimedb_sdk::{DbContext, Table};
|
use spacetimedb_sdk::{DbContext, Table};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
sync::{OwnedSemaphorePermit, Semaphore, oneshot},
|
sync::{OwnedSemaphorePermit, RwLock, Semaphore, oneshot},
|
||||||
time::timeout,
|
time::timeout,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -257,6 +258,8 @@ pub struct AuthStoreSnapshotImportRecord {
|
|||||||
pub struct SpacetimeClient {
|
pub struct SpacetimeClient {
|
||||||
config: SpacetimeClientConfig,
|
config: SpacetimeClientConfig,
|
||||||
pool: Arc<SpacetimeConnectionPool>,
|
pool: Arc<SpacetimeConnectionPool>,
|
||||||
|
creation_entry_config_cache: Arc<RwLock<Option<CreationEntryConfigRecord>>>,
|
||||||
|
custom_world_gallery_legacy_sync_attempted: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -269,6 +272,8 @@ pub enum SpacetimeClientError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_PROCEDURE_TIMEOUT: Duration = Duration::from_secs(30);
|
const DEFAULT_PROCEDURE_TIMEOUT: Duration = Duration::from_secs(30);
|
||||||
|
const PUBLIC_WORK_PLAY_DAY_MICROS: i64 = 86_400_000_000;
|
||||||
|
const PUBLIC_WORK_RECENT_PLAY_WINDOW_DAYS: i64 = 7;
|
||||||
|
|
||||||
type ProcedureResultSender<T> =
|
type ProcedureResultSender<T> =
|
||||||
Arc<Mutex<Option<oneshot::Sender<Result<T, SpacetimeClientError>>>>>;
|
Arc<Mutex<Option<oneshot::Sender<Result<T, SpacetimeClientError>>>>>;
|
||||||
@@ -286,7 +291,7 @@ struct PooledConnectionSlot {
|
|||||||
|
|
||||||
struct PooledConnection {
|
struct PooledConnection {
|
||||||
connection: DbConnection,
|
connection: DbConnection,
|
||||||
_gallery_subscription: Vec<SubscriptionHandle>,
|
_read_model_subscriptions: Vec<SubscriptionHandle>,
|
||||||
runner: Option<JoinHandle<()>>,
|
runner: Option<JoinHandle<()>>,
|
||||||
broken: Arc<AtomicBool>,
|
broken: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
@@ -321,7 +326,12 @@ impl SpacetimeClient {
|
|||||||
permits: Arc::new(Semaphore::new(pool_size)),
|
permits: Arc::new(Semaphore::new(pool_size)),
|
||||||
});
|
});
|
||||||
|
|
||||||
Self { config, pool }
|
Self {
|
||||||
|
config,
|
||||||
|
pool,
|
||||||
|
creation_entry_config_cache: Arc::new(RwLock::new(None)),
|
||||||
|
custom_world_gallery_legacy_sync_attempted: Arc::new(AtomicBool::new(false)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn call_after_connect<T>(
|
async fn call_after_connect<T>(
|
||||||
@@ -397,12 +407,21 @@ impl SpacetimeClient {
|
|||||||
|
|
||||||
async fn read_after_connect<T>(
|
async fn read_after_connect<T>(
|
||||||
&self,
|
&self,
|
||||||
|
read_name: &'static str,
|
||||||
read: impl FnOnce(&DbConnection) -> Result<T, SpacetimeClientError> + Send + 'static,
|
read: impl FnOnce(&DbConnection) -> Result<T, SpacetimeClientError> + Send + 'static,
|
||||||
) -> Result<T, SpacetimeClientError>
|
) -> Result<T, SpacetimeClientError>
|
||||||
where
|
where
|
||||||
T: Send + 'static,
|
T: Send + 'static,
|
||||||
{
|
{
|
||||||
let lease = self.acquire_connection().await?;
|
let metrics_guard = telemetry::begin_read(read_name);
|
||||||
|
let lease = match self.acquire_connection().await {
|
||||||
|
Ok(lease) => lease,
|
||||||
|
Err(error) => {
|
||||||
|
let final_result = Err(error);
|
||||||
|
metrics_guard.finish(&final_result);
|
||||||
|
return final_result;
|
||||||
|
}
|
||||||
|
};
|
||||||
let final_result = if let Some(connection) = lease.connection.as_ref() {
|
let final_result = if let Some(connection) = lease.connection.as_ref() {
|
||||||
read(&connection.connection)
|
read(&connection.connection)
|
||||||
} else {
|
} else {
|
||||||
@@ -412,9 +431,18 @@ impl SpacetimeClient {
|
|||||||
};
|
};
|
||||||
self.release_connection(lease).await;
|
self.release_connection(lease).await;
|
||||||
|
|
||||||
|
metrics_guard.finish(&final_result);
|
||||||
final_result
|
final_result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn cache_creation_entry_config(&self, config: CreationEntryConfigRecord) {
|
||||||
|
*self.creation_entry_config_cache.write().await = Some(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_cached_creation_entry_config(&self) -> Option<CreationEntryConfigRecord> {
|
||||||
|
self.creation_entry_config_cache.read().await.clone()
|
||||||
|
}
|
||||||
|
|
||||||
async fn acquire_connection(&self) -> Result<PooledConnectionLease, SpacetimeClientError> {
|
async fn acquire_connection(&self) -> Result<PooledConnectionLease, SpacetimeClientError> {
|
||||||
let permit = timeout(
|
let permit = timeout(
|
||||||
self.config.procedure_timeout,
|
self.config.procedure_timeout,
|
||||||
@@ -503,27 +531,66 @@ impl SpacetimeClient {
|
|||||||
.map_err(|_| SpacetimeClientError::Timeout)?
|
.map_err(|_| SpacetimeClientError::Timeout)?
|
||||||
.map_err(|_| SpacetimeClientError::ConnectDropped)??;
|
.map_err(|_| SpacetimeClientError::ConnectDropped)??;
|
||||||
|
|
||||||
let gallery_subscription = self
|
let read_model_subscriptions = self
|
||||||
.subscribe_puzzle_gallery_views(&connection, broken.clone())
|
.subscribe_cached_read_models(&connection, broken.clone())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(PooledConnection {
|
Ok(PooledConnection {
|
||||||
connection,
|
connection,
|
||||||
_gallery_subscription: gallery_subscription,
|
_read_model_subscriptions: read_model_subscriptions,
|
||||||
runner: Some(runner),
|
runner: Some(runner),
|
||||||
broken,
|
broken,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn subscribe_puzzle_gallery_views(
|
async fn subscribe_cached_read_models(
|
||||||
&self,
|
&self,
|
||||||
connection: &DbConnection,
|
connection: &DbConnection,
|
||||||
broken: Arc<AtomicBool>,
|
broken: Arc<AtomicBool>,
|
||||||
) -> Result<Vec<SubscriptionHandle>, SpacetimeClientError> {
|
) -> Result<Vec<SubscriptionHandle>, SpacetimeClientError> {
|
||||||
let mut subscriptions = Vec::new();
|
let mut subscriptions = Vec::new();
|
||||||
for query in [
|
for query in [
|
||||||
"SELECT * FROM puzzle_gallery_view",
|
"SELECT * FROM puzzle_gallery_card_view",
|
||||||
|
"SELECT * FROM custom_world_gallery_entry",
|
||||||
|
"SELECT * FROM match_3_d_gallery_view",
|
||||||
|
"SELECT * FROM square_hole_gallery_view",
|
||||||
|
"SELECT * FROM visual_novel_gallery_view",
|
||||||
|
"SELECT * FROM big_fish_gallery_view",
|
||||||
] {
|
] {
|
||||||
|
let subscription = self
|
||||||
|
.subscribe_cached_read_model_query(connection, broken.clone(), query, true)
|
||||||
|
.await?;
|
||||||
|
subscriptions.push(subscription);
|
||||||
|
}
|
||||||
|
|
||||||
|
for query in [
|
||||||
|
"SELECT * FROM public_work_play_daily_stat WHERE source_type = 'puzzle'",
|
||||||
|
"SELECT * FROM public_work_play_daily_stat WHERE source_type = 'custom-world'",
|
||||||
|
"SELECT * FROM public_work_play_daily_stat WHERE source_type = 'match3d'",
|
||||||
|
"SELECT * FROM public_work_play_daily_stat WHERE source_type = 'square-hole'",
|
||||||
|
"SELECT * FROM public_work_play_daily_stat WHERE source_type = 'visual-novel'",
|
||||||
|
"SELECT * FROM public_work_play_daily_stat WHERE source_type = 'big-fish'",
|
||||||
|
"SELECT * FROM creation_entry_config",
|
||||||
|
"SELECT * FROM creation_entry_type_config",
|
||||||
|
] {
|
||||||
|
if let Ok(subscription) = self
|
||||||
|
.subscribe_cached_read_model_query(connection, broken.clone(), query, false)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
subscriptions.push(subscription);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(subscriptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn subscribe_cached_read_model_query(
|
||||||
|
&self,
|
||||||
|
connection: &DbConnection,
|
||||||
|
broken: Arc<AtomicBool>,
|
||||||
|
query: &'static str,
|
||||||
|
mark_broken_on_error: bool,
|
||||||
|
) -> Result<SubscriptionHandle, SpacetimeClientError> {
|
||||||
let (sender, receiver) = oneshot::channel::<Result<(), SpacetimeClientError>>();
|
let (sender, receiver) = oneshot::channel::<Result<(), SpacetimeClientError>>();
|
||||||
let applied_sender = Arc::new(Mutex::new(Some(sender)));
|
let applied_sender = Arc::new(Mutex::new(Some(sender)));
|
||||||
let on_applied_sender = applied_sender.clone();
|
let on_applied_sender = applied_sender.clone();
|
||||||
@@ -535,7 +602,9 @@ impl SpacetimeClient {
|
|||||||
send_connect_once(&on_applied_sender, Ok(()));
|
send_connect_once(&on_applied_sender, Ok(()));
|
||||||
})
|
})
|
||||||
.on_error(move |_, error| {
|
.on_error(move |_, error| {
|
||||||
|
if mark_broken_on_error {
|
||||||
broken_flag.store(true, Ordering::SeqCst);
|
broken_flag.store(true, Ordering::SeqCst);
|
||||||
|
}
|
||||||
send_connect_once(
|
send_connect_once(
|
||||||
&on_error_sender,
|
&on_error_sender,
|
||||||
Err(SpacetimeClientError::Procedure(error.to_string())),
|
Err(SpacetimeClientError::Procedure(error.to_string())),
|
||||||
@@ -548,10 +617,7 @@ impl SpacetimeClient {
|
|||||||
.map_err(|_| SpacetimeClientError::Timeout)?
|
.map_err(|_| SpacetimeClientError::Timeout)?
|
||||||
.map_err(|_| SpacetimeClientError::ConnectDropped)??;
|
.map_err(|_| SpacetimeClientError::ConnectDropped)??;
|
||||||
|
|
||||||
subscriptions.push(subscription);
|
Ok(subscription)
|
||||||
}
|
|
||||||
|
|
||||||
Ok(subscriptions)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn release_connection(&self, mut lease: PooledConnectionLease) {
|
async fn release_connection(&self, mut lease: PooledConnectionLease) {
|
||||||
@@ -581,6 +647,39 @@ impl SpacetimeClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn current_unix_micros() -> i64 {
|
||||||
|
std::time::SystemTime::now()
|
||||||
|
.duration_since(std::time::UNIX_EPOCH)
|
||||||
|
.map(|duration| duration.as_micros() as i64)
|
||||||
|
.unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_public_work_day() -> i64 {
|
||||||
|
current_unix_micros().div_euclid(PUBLIC_WORK_PLAY_DAY_MICROS)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn public_work_recent_play_counts(
|
||||||
|
connection: &DbConnection,
|
||||||
|
source_type: &str,
|
||||||
|
) -> HashMap<String, u32> {
|
||||||
|
let current_day = current_public_work_day();
|
||||||
|
let first_day = current_day - (PUBLIC_WORK_RECENT_PLAY_WINDOW_DAYS - 1);
|
||||||
|
let mut counts = HashMap::new();
|
||||||
|
|
||||||
|
for row in connection.db().public_work_play_daily_stat().iter() {
|
||||||
|
if row.source_type != source_type
|
||||||
|
|| row.played_day < first_day
|
||||||
|
|| row.played_day > current_day
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let entry: &mut u32 = counts.entry(row.profile_id).or_insert(0);
|
||||||
|
*entry = (*entry).saturating_add(row.play_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
counts
|
||||||
|
}
|
||||||
|
|
||||||
impl SpacetimeClientError {
|
impl SpacetimeClientError {
|
||||||
pub(crate) fn from_sdk_error(error: impl fmt::Display) -> Self {
|
pub(crate) fn from_sdk_error(error: impl fmt::Display) -> Self {
|
||||||
Self::Procedure(error.to_string())
|
Self::Procedure(error.to_string())
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -225,10 +225,22 @@ impl SpacetimeClient {
|
|||||||
pub async fn list_match3d_gallery(
|
pub async fn list_match3d_gallery(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<Vec<Match3DWorkProfileRecord>, SpacetimeClientError> {
|
) -> Result<Vec<Match3DWorkProfileRecord>, SpacetimeClientError> {
|
||||||
self.list_match3d_works_with_input(Match3DWorksListInput {
|
self.read_after_connect("list_match3d_gallery", move |connection| {
|
||||||
// 中文注释:公开广场读取只依赖 published_only,owner_user_id 保持非空便于兼容校验。
|
let mut items = connection
|
||||||
owner_user_id: "match3d-public-gallery".to_string(),
|
.db()
|
||||||
published_only: true,
|
.match_3_d_gallery_view()
|
||||||
|
.iter()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
items.sort_by(|left, right| {
|
||||||
|
right
|
||||||
|
.updated_at_micros
|
||||||
|
.cmp(&left.updated_at_micros)
|
||||||
|
.then_with(|| left.profile_id.cmp(&right.profile_id))
|
||||||
|
});
|
||||||
|
Ok(items
|
||||||
|
.into_iter()
|
||||||
|
.map(map_match3d_gallery_view_row)
|
||||||
|
.collect())
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// 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 BarkBattleDraftConfigSnapshot {
|
||||||
|
pub draft_id: String,
|
||||||
|
pub owner_user_id: String,
|
||||||
|
pub work_id: String,
|
||||||
|
pub config_version: u64,
|
||||||
|
pub ruleset_version: String,
|
||||||
|
pub difficulty_preset: String,
|
||||||
|
pub leaderboard_enabled: bool,
|
||||||
|
pub config_json: String,
|
||||||
|
pub editor_state_json: String,
|
||||||
|
pub created_at_micros: i64,
|
||||||
|
pub updated_at_micros: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for BarkBattleDraftConfigSnapshot {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -4,11 +4,17 @@
|
|||||||
#![allow(unused, clippy::all)]
|
#![allow(unused, clippy::all)]
|
||||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||||
|
|
||||||
|
use super::bark_battle_draft_config_snapshot_type::BarkBattleDraftConfigSnapshot;
|
||||||
|
use super::bark_battle_run_snapshot_type::BarkBattleRunSnapshot;
|
||||||
|
use super::bark_battle_runtime_config_snapshot_type::BarkBattleRuntimeConfigSnapshot;
|
||||||
|
|
||||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||||
#[sats(crate = __lib)]
|
#[sats(crate = __lib)]
|
||||||
pub struct BarkBattleProcedureResult {
|
pub struct BarkBattleProcedureResult {
|
||||||
pub ok: bool,
|
pub ok: bool,
|
||||||
pub row_json: Option<String>,
|
pub draft_config: Option<BarkBattleDraftConfigSnapshot>,
|
||||||
|
pub runtime_config: Option<BarkBattleRuntimeConfigSnapshot>,
|
||||||
|
pub run: Option<BarkBattleRunSnapshot>,
|
||||||
pub error_message: Option<String>,
|
pub error_message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
// 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 BarkBattleRunSnapshot {
|
||||||
|
pub run_id: String,
|
||||||
|
pub owner_user_id: String,
|
||||||
|
pub work_id: String,
|
||||||
|
pub config_version: u64,
|
||||||
|
pub ruleset_version: String,
|
||||||
|
pub difficulty_preset: String,
|
||||||
|
pub leaderboard_enabled: bool,
|
||||||
|
pub status: String,
|
||||||
|
pub client_started_at_micros: i64,
|
||||||
|
pub server_started_at_micros: i64,
|
||||||
|
pub client_finished_at_micros: Option<i64>,
|
||||||
|
pub server_finished_at_micros: Option<i64>,
|
||||||
|
pub metrics_json: String,
|
||||||
|
pub server_result: Option<String>,
|
||||||
|
pub validation_status: String,
|
||||||
|
pub anti_cheat_flags_json: String,
|
||||||
|
pub leaderboard_score: Option<u64>,
|
||||||
|
pub score_id: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for BarkBattleRunSnapshot {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// 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 BarkBattleRuntimeConfigSnapshot {
|
||||||
|
pub work_id: String,
|
||||||
|
pub owner_user_id: String,
|
||||||
|
pub source_draft_id: Option<String>,
|
||||||
|
pub config_version: u64,
|
||||||
|
pub ruleset_version: String,
|
||||||
|
pub difficulty_preset: String,
|
||||||
|
pub leaderboard_enabled: bool,
|
||||||
|
pub config_json: String,
|
||||||
|
pub published_snapshot_json: String,
|
||||||
|
pub published_at_micros: i64,
|
||||||
|
pub updated_at_micros: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for BarkBattleRuntimeConfigSnapshot {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -92,6 +92,7 @@ impl __sdk::__query_builder::HasCols for BigFishCreationSession {
|
|||||||
pub struct BigFishCreationSessionIxCols {
|
pub struct BigFishCreationSessionIxCols {
|
||||||
pub owner_user_id: __sdk::__query_builder::IxCol<BigFishCreationSession, String>,
|
pub owner_user_id: __sdk::__query_builder::IxCol<BigFishCreationSession, String>,
|
||||||
pub session_id: __sdk::__query_builder::IxCol<BigFishCreationSession, String>,
|
pub session_id: __sdk::__query_builder::IxCol<BigFishCreationSession, String>,
|
||||||
|
pub stage: __sdk::__query_builder::IxCol<BigFishCreationSession, BigFishCreationStage>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl __sdk::__query_builder::HasIxCols for BigFishCreationSession {
|
impl __sdk::__query_builder::HasIxCols for BigFishCreationSession {
|
||||||
@@ -100,6 +101,7 @@ impl __sdk::__query_builder::HasIxCols for BigFishCreationSession {
|
|||||||
BigFishCreationSessionIxCols {
|
BigFishCreationSessionIxCols {
|
||||||
owner_user_id: __sdk::__query_builder::IxCol::new(table_name, "owner_user_id"),
|
owner_user_id: __sdk::__query_builder::IxCol::new(table_name, "owner_user_id"),
|
||||||
session_id: __sdk::__query_builder::IxCol::new(table_name, "session_id"),
|
session_id: __sdk::__query_builder::IxCol::new(table_name, "session_id"),
|
||||||
|
stage: __sdk::__query_builder::IxCol::new(table_name, "stage"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
// 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::big_fish_work_summary_snapshot_type::BigFishWorkSummarySnapshot;
|
||||||
|
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||||
|
|
||||||
|
/// Table handle for the table `big_fish_gallery_view`.
|
||||||
|
///
|
||||||
|
/// Obtain a handle from the [`BigFishGalleryViewTableAccess::big_fish_gallery_view`] method on [`super::RemoteTables`],
|
||||||
|
/// like `ctx.db.big_fish_gallery_view()`.
|
||||||
|
///
|
||||||
|
/// Users are encouraged not to explicitly reference this type,
|
||||||
|
/// but to directly chain method calls,
|
||||||
|
/// like `ctx.db.big_fish_gallery_view().on_insert(...)`.
|
||||||
|
pub struct BigFishGalleryViewTableHandle<'ctx> {
|
||||||
|
imp: __sdk::TableHandle<BigFishWorkSummarySnapshot>,
|
||||||
|
ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
/// Extension trait for access to the table `big_fish_gallery_view`.
|
||||||
|
///
|
||||||
|
/// Implemented for [`super::RemoteTables`].
|
||||||
|
pub trait BigFishGalleryViewTableAccess {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
/// Obtain a [`BigFishGalleryViewTableHandle`], which mediates access to the table `big_fish_gallery_view`.
|
||||||
|
fn big_fish_gallery_view(&self) -> BigFishGalleryViewTableHandle<'_>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BigFishGalleryViewTableAccess for super::RemoteTables {
|
||||||
|
fn big_fish_gallery_view(&self) -> BigFishGalleryViewTableHandle<'_> {
|
||||||
|
BigFishGalleryViewTableHandle {
|
||||||
|
imp: self
|
||||||
|
.imp
|
||||||
|
.get_table::<BigFishWorkSummarySnapshot>("big_fish_gallery_view"),
|
||||||
|
ctx: std::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BigFishGalleryViewInsertCallbackId(__sdk::CallbackId);
|
||||||
|
pub struct BigFishGalleryViewDeleteCallbackId(__sdk::CallbackId);
|
||||||
|
|
||||||
|
impl<'ctx> __sdk::Table for BigFishGalleryViewTableHandle<'ctx> {
|
||||||
|
type Row = BigFishWorkSummarySnapshot;
|
||||||
|
type EventContext = super::EventContext;
|
||||||
|
|
||||||
|
fn count(&self) -> u64 {
|
||||||
|
self.imp.count()
|
||||||
|
}
|
||||||
|
fn iter(&self) -> impl Iterator<Item = BigFishWorkSummarySnapshot> + '_ {
|
||||||
|
self.imp.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
type InsertCallbackId = BigFishGalleryViewInsertCallbackId;
|
||||||
|
|
||||||
|
fn on_insert(
|
||||||
|
&self,
|
||||||
|
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
|
||||||
|
) -> BigFishGalleryViewInsertCallbackId {
|
||||||
|
BigFishGalleryViewInsertCallbackId(self.imp.on_insert(Box::new(callback)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_on_insert(&self, callback: BigFishGalleryViewInsertCallbackId) {
|
||||||
|
self.imp.remove_on_insert(callback.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteCallbackId = BigFishGalleryViewDeleteCallbackId;
|
||||||
|
|
||||||
|
fn on_delete(
|
||||||
|
&self,
|
||||||
|
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
|
||||||
|
) -> BigFishGalleryViewDeleteCallbackId {
|
||||||
|
BigFishGalleryViewDeleteCallbackId(self.imp.on_delete(Box::new(callback)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_on_delete(&self, callback: BigFishGalleryViewDeleteCallbackId) {
|
||||||
|
self.imp.remove_on_delete(callback.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
|
||||||
|
let _table =
|
||||||
|
client_cache.get_or_make_table::<BigFishWorkSummarySnapshot>("big_fish_gallery_view");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub(super) fn parse_table_update(
|
||||||
|
raw_updates: __ws::v2::TableUpdate,
|
||||||
|
) -> __sdk::Result<__sdk::TableUpdate<BigFishWorkSummarySnapshot>> {
|
||||||
|
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
|
||||||
|
__sdk::InternalError::failed_parse("TableUpdate<BigFishWorkSummarySnapshot>", "TableUpdate")
|
||||||
|
.with_cause(e)
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
/// Extension trait for query builder access to the table `BigFishWorkSummarySnapshot`.
|
||||||
|
///
|
||||||
|
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||||
|
pub trait big_fish_gallery_viewQueryTableAccess {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
/// Get a query builder for the table `BigFishWorkSummarySnapshot`.
|
||||||
|
fn big_fish_gallery_view(&self) -> __sdk::__query_builder::Table<BigFishWorkSummarySnapshot>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl big_fish_gallery_viewQueryTableAccess for __sdk::QueryTableAccessor {
|
||||||
|
fn big_fish_gallery_view(&self) -> __sdk::__query_builder::Table<BigFishWorkSummarySnapshot> {
|
||||||
|
__sdk::__query_builder::Table::new("big_fish_gallery_view")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,11 +4,13 @@
|
|||||||
#![allow(unused, clippy::all)]
|
#![allow(unused, clippy::all)]
|
||||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||||
|
|
||||||
|
use super::big_fish_runtime_snapshot_type::BigFishRuntimeSnapshot;
|
||||||
|
|
||||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||||
#[sats(crate = __lib)]
|
#[sats(crate = __lib)]
|
||||||
pub struct BigFishRunProcedureResult {
|
pub struct BigFishRunProcedureResult {
|
||||||
pub ok: bool,
|
pub ok: bool,
|
||||||
pub run_json: Option<String>,
|
pub run: Option<BigFishRuntimeSnapshot>,
|
||||||
pub error_message: Option<String>,
|
pub error_message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// 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::big_fish_vector_2_type::BigFishVector2;
|
||||||
|
|
||||||
|
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||||
|
#[sats(crate = __lib)]
|
||||||
|
pub struct BigFishRuntimeEntitySnapshot {
|
||||||
|
pub entity_id: String,
|
||||||
|
pub level: u32,
|
||||||
|
pub position: BigFishVector2,
|
||||||
|
pub radius: f32,
|
||||||
|
pub offscreen_seconds: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for BigFishRuntimeEntitySnapshot {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// 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::big_fish_run_status_type::BigFishRunStatus;
|
||||||
|
use super::big_fish_runtime_entity_snapshot_type::BigFishRuntimeEntitySnapshot;
|
||||||
|
use super::big_fish_vector_2_type::BigFishVector2;
|
||||||
|
|
||||||
|
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||||
|
#[sats(crate = __lib)]
|
||||||
|
pub struct BigFishRuntimeSnapshot {
|
||||||
|
pub run_id: String,
|
||||||
|
pub session_id: String,
|
||||||
|
pub status: BigFishRunStatus,
|
||||||
|
pub tick: u64,
|
||||||
|
pub player_level: u32,
|
||||||
|
pub win_level: u32,
|
||||||
|
pub leader_entity_id: Option<String>,
|
||||||
|
pub owned_entities: Vec<BigFishRuntimeEntitySnapshot>,
|
||||||
|
pub wild_entities: Vec<BigFishRuntimeEntitySnapshot>,
|
||||||
|
pub camera_center: BigFishVector2,
|
||||||
|
pub last_input: BigFishVector2,
|
||||||
|
pub event_log: Vec<String>,
|
||||||
|
pub updated_at_micros: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for BigFishRuntimeSnapshot {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// 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 BigFishVector2 {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for BigFishVector2 {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
// 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 BigFishWorkSummarySnapshot {
|
||||||
|
pub work_id: String,
|
||||||
|
pub source_session_id: String,
|
||||||
|
pub owner_user_id: String,
|
||||||
|
pub title: String,
|
||||||
|
pub subtitle: String,
|
||||||
|
pub summary: String,
|
||||||
|
pub cover_image_src: Option<String>,
|
||||||
|
pub status: String,
|
||||||
|
pub updated_at_micros: i64,
|
||||||
|
pub publish_ready: bool,
|
||||||
|
pub level_count: u32,
|
||||||
|
pub level_main_image_ready_count: u32,
|
||||||
|
pub level_motion_ready_count: u32,
|
||||||
|
pub background_ready: bool,
|
||||||
|
pub play_count: u32,
|
||||||
|
pub remix_count: u32,
|
||||||
|
pub like_count: u32,
|
||||||
|
pub recent_play_count_7_d: u32,
|
||||||
|
pub published_at_micros: Option<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for BigFishWorkSummarySnapshot {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Column accessor struct for the table `BigFishWorkSummarySnapshot`.
|
||||||
|
///
|
||||||
|
/// Provides typed access to columns for query building.
|
||||||
|
pub struct BigFishWorkSummarySnapshotCols {
|
||||||
|
pub work_id: __sdk::__query_builder::Col<BigFishWorkSummarySnapshot, String>,
|
||||||
|
pub source_session_id: __sdk::__query_builder::Col<BigFishWorkSummarySnapshot, String>,
|
||||||
|
pub owner_user_id: __sdk::__query_builder::Col<BigFishWorkSummarySnapshot, String>,
|
||||||
|
pub title: __sdk::__query_builder::Col<BigFishWorkSummarySnapshot, String>,
|
||||||
|
pub subtitle: __sdk::__query_builder::Col<BigFishWorkSummarySnapshot, String>,
|
||||||
|
pub summary: __sdk::__query_builder::Col<BigFishWorkSummarySnapshot, String>,
|
||||||
|
pub cover_image_src: __sdk::__query_builder::Col<BigFishWorkSummarySnapshot, Option<String>>,
|
||||||
|
pub status: __sdk::__query_builder::Col<BigFishWorkSummarySnapshot, String>,
|
||||||
|
pub updated_at_micros: __sdk::__query_builder::Col<BigFishWorkSummarySnapshot, i64>,
|
||||||
|
pub publish_ready: __sdk::__query_builder::Col<BigFishWorkSummarySnapshot, bool>,
|
||||||
|
pub level_count: __sdk::__query_builder::Col<BigFishWorkSummarySnapshot, u32>,
|
||||||
|
pub level_main_image_ready_count: __sdk::__query_builder::Col<BigFishWorkSummarySnapshot, u32>,
|
||||||
|
pub level_motion_ready_count: __sdk::__query_builder::Col<BigFishWorkSummarySnapshot, u32>,
|
||||||
|
pub background_ready: __sdk::__query_builder::Col<BigFishWorkSummarySnapshot, bool>,
|
||||||
|
pub play_count: __sdk::__query_builder::Col<BigFishWorkSummarySnapshot, u32>,
|
||||||
|
pub remix_count: __sdk::__query_builder::Col<BigFishWorkSummarySnapshot, u32>,
|
||||||
|
pub like_count: __sdk::__query_builder::Col<BigFishWorkSummarySnapshot, u32>,
|
||||||
|
pub recent_play_count_7_d: __sdk::__query_builder::Col<BigFishWorkSummarySnapshot, u32>,
|
||||||
|
pub published_at_micros: __sdk::__query_builder::Col<BigFishWorkSummarySnapshot, Option<i64>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::__query_builder::HasCols for BigFishWorkSummarySnapshot {
|
||||||
|
type Cols = BigFishWorkSummarySnapshotCols;
|
||||||
|
fn cols(table_name: &'static str) -> Self::Cols {
|
||||||
|
BigFishWorkSummarySnapshotCols {
|
||||||
|
work_id: __sdk::__query_builder::Col::new(table_name, "work_id"),
|
||||||
|
source_session_id: __sdk::__query_builder::Col::new(table_name, "source_session_id"),
|
||||||
|
owner_user_id: __sdk::__query_builder::Col::new(table_name, "owner_user_id"),
|
||||||
|
title: __sdk::__query_builder::Col::new(table_name, "title"),
|
||||||
|
subtitle: __sdk::__query_builder::Col::new(table_name, "subtitle"),
|
||||||
|
summary: __sdk::__query_builder::Col::new(table_name, "summary"),
|
||||||
|
cover_image_src: __sdk::__query_builder::Col::new(table_name, "cover_image_src"),
|
||||||
|
status: __sdk::__query_builder::Col::new(table_name, "status"),
|
||||||
|
updated_at_micros: __sdk::__query_builder::Col::new(table_name, "updated_at_micros"),
|
||||||
|
publish_ready: __sdk::__query_builder::Col::new(table_name, "publish_ready"),
|
||||||
|
level_count: __sdk::__query_builder::Col::new(table_name, "level_count"),
|
||||||
|
level_main_image_ready_count: __sdk::__query_builder::Col::new(
|
||||||
|
table_name,
|
||||||
|
"level_main_image_ready_count",
|
||||||
|
),
|
||||||
|
level_motion_ready_count: __sdk::__query_builder::Col::new(
|
||||||
|
table_name,
|
||||||
|
"level_motion_ready_count",
|
||||||
|
),
|
||||||
|
background_ready: __sdk::__query_builder::Col::new(table_name, "background_ready"),
|
||||||
|
play_count: __sdk::__query_builder::Col::new(table_name, "play_count"),
|
||||||
|
remix_count: __sdk::__query_builder::Col::new(table_name, "remix_count"),
|
||||||
|
like_count: __sdk::__query_builder::Col::new(table_name, "like_count"),
|
||||||
|
recent_play_count_7_d: __sdk::__query_builder::Col::new(
|
||||||
|
table_name,
|
||||||
|
"recent_play_count_7_d",
|
||||||
|
),
|
||||||
|
published_at_micros: __sdk::__query_builder::Col::new(
|
||||||
|
table_name,
|
||||||
|
"published_at_micros",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,11 +4,13 @@
|
|||||||
#![allow(unused, clippy::all)]
|
#![allow(unused, clippy::all)]
|
||||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||||
|
|
||||||
|
use super::big_fish_work_summary_snapshot_type::BigFishWorkSummarySnapshot;
|
||||||
|
|
||||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||||
#[sats(crate = __lib)]
|
#[sats(crate = __lib)]
|
||||||
pub struct BigFishWorksProcedureResult {
|
pub struct BigFishWorksProcedureResult {
|
||||||
pub ok: bool,
|
pub ok: bool,
|
||||||
pub items_json: Option<String>,
|
pub items: Vec<BigFishWorkSummarySnapshot>,
|
||||||
pub error_message: Option<String>,
|
pub error_message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// 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 Match3DAgentMessageSnapshot {
|
||||||
|
pub message_id: String,
|
||||||
|
pub session_id: String,
|
||||||
|
pub role: String,
|
||||||
|
pub kind: String,
|
||||||
|
pub text: String,
|
||||||
|
pub created_at_micros: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for Match3DAgentMessageSnapshot {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -4,11 +4,13 @@
|
|||||||
#![allow(unused, clippy::all)]
|
#![allow(unused, clippy::all)]
|
||||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||||
|
|
||||||
|
use super::match_3_d_agent_session_snapshot_type::Match3DAgentSessionSnapshot;
|
||||||
|
|
||||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||||
#[sats(crate = __lib)]
|
#[sats(crate = __lib)]
|
||||||
pub struct Match3DAgentSessionProcedureResult {
|
pub struct Match3DAgentSessionProcedureResult {
|
||||||
pub ok: bool,
|
pub ok: bool,
|
||||||
pub session_json: Option<String>,
|
pub session: Option<Match3DAgentSessionSnapshot>,
|
||||||
pub error_message: Option<String>,
|
pub error_message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// 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::match_3_d_agent_message_snapshot_type::Match3DAgentMessageSnapshot;
|
||||||
|
use super::match_3_d_creator_config_snapshot_type::Match3DCreatorConfigSnapshot;
|
||||||
|
use super::match_3_d_draft_snapshot_type::Match3DDraftSnapshot;
|
||||||
|
|
||||||
|
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||||
|
#[sats(crate = __lib)]
|
||||||
|
pub struct Match3DAgentSessionSnapshot {
|
||||||
|
pub session_id: String,
|
||||||
|
pub owner_user_id: String,
|
||||||
|
pub seed_text: String,
|
||||||
|
pub current_turn: u32,
|
||||||
|
pub progress_percent: u32,
|
||||||
|
pub stage: String,
|
||||||
|
pub config: Match3DCreatorConfigSnapshot,
|
||||||
|
pub draft: Option<Match3DDraftSnapshot>,
|
||||||
|
pub messages: Vec<Match3DAgentMessageSnapshot>,
|
||||||
|
pub last_assistant_reply: String,
|
||||||
|
pub published_profile_id: Option<String>,
|
||||||
|
pub created_at_micros: i64,
|
||||||
|
pub updated_at_micros: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for Match3DAgentSessionSnapshot {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -4,12 +4,14 @@
|
|||||||
#![allow(unused, clippy::all)]
|
#![allow(unused, clippy::all)]
|
||||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||||
|
|
||||||
|
use super::match_3_d_run_snapshot_type::Match3DRunSnapshot;
|
||||||
|
|
||||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||||
#[sats(crate = __lib)]
|
#[sats(crate = __lib)]
|
||||||
pub struct Match3DClickItemProcedureResult {
|
pub struct Match3DClickItemProcedureResult {
|
||||||
pub ok: bool,
|
pub ok: bool,
|
||||||
pub status: String,
|
pub status: String,
|
||||||
pub run_json: Option<String>,
|
pub run: Option<Match3DRunSnapshot>,
|
||||||
pub accepted_item_instance_id: Option<String>,
|
pub accepted_item_instance_id: Option<String>,
|
||||||
pub cleared_item_instance_ids: Vec<String>,
|
pub cleared_item_instance_ids: Vec<String>,
|
||||||
pub failure_reason: Option<String>,
|
pub failure_reason: Option<String>,
|
||||||
|
|||||||
@@ -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 Match3DCreatorConfigSnapshot {
|
||||||
|
pub theme_text: String,
|
||||||
|
pub reference_image_src: Option<String>,
|
||||||
|
pub clear_count: u32,
|
||||||
|
pub difficulty: u32,
|
||||||
|
pub asset_style_id: Option<String>,
|
||||||
|
pub asset_style_label: Option<String>,
|
||||||
|
pub asset_style_prompt: Option<String>,
|
||||||
|
pub generate_click_sound: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for Match3DCreatorConfigSnapshot {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -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 Match3DDraftSnapshot {
|
||||||
|
pub profile_id: String,
|
||||||
|
pub game_name: String,
|
||||||
|
pub theme_text: String,
|
||||||
|
pub summary_text: String,
|
||||||
|
pub tags: Vec<String>,
|
||||||
|
pub clear_count: u32,
|
||||||
|
pub difficulty: u32,
|
||||||
|
pub generated_item_assets_json: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for Match3DDraftSnapshot {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
// 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 Match3DGalleryViewRow {
|
||||||
|
pub profile_id: String,
|
||||||
|
pub owner_user_id: String,
|
||||||
|
pub source_session_id: String,
|
||||||
|
pub author_display_name: String,
|
||||||
|
pub game_name: String,
|
||||||
|
pub theme_text: String,
|
||||||
|
pub summary_text: String,
|
||||||
|
pub tags: Vec<String>,
|
||||||
|
pub cover_image_src: String,
|
||||||
|
pub cover_asset_id: String,
|
||||||
|
pub reference_image_src: Option<String>,
|
||||||
|
pub clear_count: u32,
|
||||||
|
pub difficulty: u32,
|
||||||
|
pub publication_status: String,
|
||||||
|
pub publish_ready: bool,
|
||||||
|
pub play_count: u32,
|
||||||
|
pub updated_at_micros: i64,
|
||||||
|
pub published_at_micros: Option<i64>,
|
||||||
|
pub generated_item_assets_json: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for Match3DGalleryViewRow {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Column accessor struct for the table `Match3DGalleryViewRow`.
|
||||||
|
///
|
||||||
|
/// Provides typed access to columns for query building.
|
||||||
|
pub struct Match3DGalleryViewRowCols {
|
||||||
|
pub profile_id: __sdk::__query_builder::Col<Match3DGalleryViewRow, String>,
|
||||||
|
pub owner_user_id: __sdk::__query_builder::Col<Match3DGalleryViewRow, String>,
|
||||||
|
pub source_session_id: __sdk::__query_builder::Col<Match3DGalleryViewRow, String>,
|
||||||
|
pub author_display_name: __sdk::__query_builder::Col<Match3DGalleryViewRow, String>,
|
||||||
|
pub game_name: __sdk::__query_builder::Col<Match3DGalleryViewRow, String>,
|
||||||
|
pub theme_text: __sdk::__query_builder::Col<Match3DGalleryViewRow, String>,
|
||||||
|
pub summary_text: __sdk::__query_builder::Col<Match3DGalleryViewRow, String>,
|
||||||
|
pub tags: __sdk::__query_builder::Col<Match3DGalleryViewRow, Vec<String>>,
|
||||||
|
pub cover_image_src: __sdk::__query_builder::Col<Match3DGalleryViewRow, String>,
|
||||||
|
pub cover_asset_id: __sdk::__query_builder::Col<Match3DGalleryViewRow, String>,
|
||||||
|
pub reference_image_src: __sdk::__query_builder::Col<Match3DGalleryViewRow, Option<String>>,
|
||||||
|
pub clear_count: __sdk::__query_builder::Col<Match3DGalleryViewRow, u32>,
|
||||||
|
pub difficulty: __sdk::__query_builder::Col<Match3DGalleryViewRow, u32>,
|
||||||
|
pub publication_status: __sdk::__query_builder::Col<Match3DGalleryViewRow, String>,
|
||||||
|
pub publish_ready: __sdk::__query_builder::Col<Match3DGalleryViewRow, bool>,
|
||||||
|
pub play_count: __sdk::__query_builder::Col<Match3DGalleryViewRow, u32>,
|
||||||
|
pub updated_at_micros: __sdk::__query_builder::Col<Match3DGalleryViewRow, i64>,
|
||||||
|
pub published_at_micros: __sdk::__query_builder::Col<Match3DGalleryViewRow, Option<i64>>,
|
||||||
|
pub generated_item_assets_json:
|
||||||
|
__sdk::__query_builder::Col<Match3DGalleryViewRow, Option<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::__query_builder::HasCols for Match3DGalleryViewRow {
|
||||||
|
type Cols = Match3DGalleryViewRowCols;
|
||||||
|
fn cols(table_name: &'static str) -> Self::Cols {
|
||||||
|
Match3DGalleryViewRowCols {
|
||||||
|
profile_id: __sdk::__query_builder::Col::new(table_name, "profile_id"),
|
||||||
|
owner_user_id: __sdk::__query_builder::Col::new(table_name, "owner_user_id"),
|
||||||
|
source_session_id: __sdk::__query_builder::Col::new(table_name, "source_session_id"),
|
||||||
|
author_display_name: __sdk::__query_builder::Col::new(
|
||||||
|
table_name,
|
||||||
|
"author_display_name",
|
||||||
|
),
|
||||||
|
game_name: __sdk::__query_builder::Col::new(table_name, "game_name"),
|
||||||
|
theme_text: __sdk::__query_builder::Col::new(table_name, "theme_text"),
|
||||||
|
summary_text: __sdk::__query_builder::Col::new(table_name, "summary_text"),
|
||||||
|
tags: __sdk::__query_builder::Col::new(table_name, "tags"),
|
||||||
|
cover_image_src: __sdk::__query_builder::Col::new(table_name, "cover_image_src"),
|
||||||
|
cover_asset_id: __sdk::__query_builder::Col::new(table_name, "cover_asset_id"),
|
||||||
|
reference_image_src: __sdk::__query_builder::Col::new(
|
||||||
|
table_name,
|
||||||
|
"reference_image_src",
|
||||||
|
),
|
||||||
|
clear_count: __sdk::__query_builder::Col::new(table_name, "clear_count"),
|
||||||
|
difficulty: __sdk::__query_builder::Col::new(table_name, "difficulty"),
|
||||||
|
publication_status: __sdk::__query_builder::Col::new(table_name, "publication_status"),
|
||||||
|
publish_ready: __sdk::__query_builder::Col::new(table_name, "publish_ready"),
|
||||||
|
play_count: __sdk::__query_builder::Col::new(table_name, "play_count"),
|
||||||
|
updated_at_micros: __sdk::__query_builder::Col::new(table_name, "updated_at_micros"),
|
||||||
|
published_at_micros: __sdk::__query_builder::Col::new(
|
||||||
|
table_name,
|
||||||
|
"published_at_micros",
|
||||||
|
),
|
||||||
|
generated_item_assets_json: __sdk::__query_builder::Col::new(
|
||||||
|
table_name,
|
||||||
|
"generated_item_assets_json",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
// 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::match_3_d_gallery_view_row_type::Match3DGalleryViewRow;
|
||||||
|
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||||
|
|
||||||
|
/// Table handle for the table `match_3_d_gallery_view`.
|
||||||
|
///
|
||||||
|
/// Obtain a handle from the [`Match3DGalleryViewTableAccess::match_3_d_gallery_view`] method on [`super::RemoteTables`],
|
||||||
|
/// like `ctx.db.match_3_d_gallery_view()`.
|
||||||
|
///
|
||||||
|
/// Users are encouraged not to explicitly reference this type,
|
||||||
|
/// but to directly chain method calls,
|
||||||
|
/// like `ctx.db.match_3_d_gallery_view().on_insert(...)`.
|
||||||
|
pub struct Match3DGalleryViewTableHandle<'ctx> {
|
||||||
|
imp: __sdk::TableHandle<Match3DGalleryViewRow>,
|
||||||
|
ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
/// Extension trait for access to the table `match_3_d_gallery_view`.
|
||||||
|
///
|
||||||
|
/// Implemented for [`super::RemoteTables`].
|
||||||
|
pub trait Match3DGalleryViewTableAccess {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
/// Obtain a [`Match3DGalleryViewTableHandle`], which mediates access to the table `match_3_d_gallery_view`.
|
||||||
|
fn match_3_d_gallery_view(&self) -> Match3DGalleryViewTableHandle<'_>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Match3DGalleryViewTableAccess for super::RemoteTables {
|
||||||
|
fn match_3_d_gallery_view(&self) -> Match3DGalleryViewTableHandle<'_> {
|
||||||
|
Match3DGalleryViewTableHandle {
|
||||||
|
imp: self
|
||||||
|
.imp
|
||||||
|
.get_table::<Match3DGalleryViewRow>("match_3_d_gallery_view"),
|
||||||
|
ctx: std::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Match3DGalleryViewInsertCallbackId(__sdk::CallbackId);
|
||||||
|
pub struct Match3DGalleryViewDeleteCallbackId(__sdk::CallbackId);
|
||||||
|
|
||||||
|
impl<'ctx> __sdk::Table for Match3DGalleryViewTableHandle<'ctx> {
|
||||||
|
type Row = Match3DGalleryViewRow;
|
||||||
|
type EventContext = super::EventContext;
|
||||||
|
|
||||||
|
fn count(&self) -> u64 {
|
||||||
|
self.imp.count()
|
||||||
|
}
|
||||||
|
fn iter(&self) -> impl Iterator<Item = Match3DGalleryViewRow> + '_ {
|
||||||
|
self.imp.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
type InsertCallbackId = Match3DGalleryViewInsertCallbackId;
|
||||||
|
|
||||||
|
fn on_insert(
|
||||||
|
&self,
|
||||||
|
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
|
||||||
|
) -> Match3DGalleryViewInsertCallbackId {
|
||||||
|
Match3DGalleryViewInsertCallbackId(self.imp.on_insert(Box::new(callback)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_on_insert(&self, callback: Match3DGalleryViewInsertCallbackId) {
|
||||||
|
self.imp.remove_on_insert(callback.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteCallbackId = Match3DGalleryViewDeleteCallbackId;
|
||||||
|
|
||||||
|
fn on_delete(
|
||||||
|
&self,
|
||||||
|
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
|
||||||
|
) -> Match3DGalleryViewDeleteCallbackId {
|
||||||
|
Match3DGalleryViewDeleteCallbackId(self.imp.on_delete(Box::new(callback)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_on_delete(&self, callback: Match3DGalleryViewDeleteCallbackId) {
|
||||||
|
self.imp.remove_on_delete(callback.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
|
||||||
|
let _table = client_cache.get_or_make_table::<Match3DGalleryViewRow>("match_3_d_gallery_view");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub(super) fn parse_table_update(
|
||||||
|
raw_updates: __ws::v2::TableUpdate,
|
||||||
|
) -> __sdk::Result<__sdk::TableUpdate<Match3DGalleryViewRow>> {
|
||||||
|
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
|
||||||
|
__sdk::InternalError::failed_parse("TableUpdate<Match3DGalleryViewRow>", "TableUpdate")
|
||||||
|
.with_cause(e)
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
/// Extension trait for query builder access to the table `Match3DGalleryViewRow`.
|
||||||
|
///
|
||||||
|
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||||
|
pub trait match_3_d_gallery_viewQueryTableAccess {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
/// Get a query builder for the table `Match3DGalleryViewRow`.
|
||||||
|
fn match_3_d_gallery_view(&self) -> __sdk::__query_builder::Table<Match3DGalleryViewRow>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl match_3_d_gallery_viewQueryTableAccess for __sdk::QueryTableAccessor {
|
||||||
|
fn match_3_d_gallery_view(&self) -> __sdk::__query_builder::Table<Match3DGalleryViewRow> {
|
||||||
|
__sdk::__query_builder::Table::new("match_3_d_gallery_view")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// 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 Match3DItemSnapshot {
|
||||||
|
pub item_instance_id: String,
|
||||||
|
pub item_type_id: String,
|
||||||
|
pub visual_key: String,
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
pub radius: f32,
|
||||||
|
pub layer: u32,
|
||||||
|
pub state: String,
|
||||||
|
pub clickable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for Match3DItemSnapshot {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -4,11 +4,13 @@
|
|||||||
#![allow(unused, clippy::all)]
|
#![allow(unused, clippy::all)]
|
||||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||||
|
|
||||||
|
use super::match_3_d_run_snapshot_type::Match3DRunSnapshot;
|
||||||
|
|
||||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||||
#[sats(crate = __lib)]
|
#[sats(crate = __lib)]
|
||||||
pub struct Match3DRunProcedureResult {
|
pub struct Match3DRunProcedureResult {
|
||||||
pub ok: bool,
|
pub ok: bool,
|
||||||
pub run_json: Option<String>,
|
pub run: Option<Match3DRunSnapshot>,
|
||||||
pub error_message: Option<String>,
|
pub error_message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// 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::match_3_d_item_snapshot_type::Match3DItemSnapshot;
|
||||||
|
use super::match_3_d_tray_slot_snapshot_type::Match3DTraySlotSnapshot;
|
||||||
|
|
||||||
|
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||||
|
#[sats(crate = __lib)]
|
||||||
|
pub struct Match3DRunSnapshot {
|
||||||
|
pub run_id: String,
|
||||||
|
pub profile_id: String,
|
||||||
|
pub status: String,
|
||||||
|
pub snapshot_version: u32,
|
||||||
|
pub started_at_ms: i64,
|
||||||
|
pub duration_limit_ms: i64,
|
||||||
|
pub server_now_ms: i64,
|
||||||
|
pub remaining_ms: i64,
|
||||||
|
pub clear_count: u32,
|
||||||
|
pub total_item_count: u32,
|
||||||
|
pub cleared_item_count: u32,
|
||||||
|
pub tray_slots: Vec<Match3DTraySlotSnapshot>,
|
||||||
|
pub items: Vec<Match3DItemSnapshot>,
|
||||||
|
pub failure_reason: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for Match3DRunSnapshot {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -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 Match3DTraySlotSnapshot {
|
||||||
|
pub slot_index: u32,
|
||||||
|
pub item_instance_id: Option<String>,
|
||||||
|
pub item_type_id: Option<String>,
|
||||||
|
pub visual_key: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for Match3DTraySlotSnapshot {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -4,11 +4,13 @@
|
|||||||
#![allow(unused, clippy::all)]
|
#![allow(unused, clippy::all)]
|
||||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||||
|
|
||||||
|
use super::match_3_d_work_snapshot_type::Match3DWorkSnapshot;
|
||||||
|
|
||||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||||
#[sats(crate = __lib)]
|
#[sats(crate = __lib)]
|
||||||
pub struct Match3DWorkProcedureResult {
|
pub struct Match3DWorkProcedureResult {
|
||||||
pub ok: bool,
|
pub ok: bool,
|
||||||
pub work_json: Option<String>,
|
pub work: Option<Match3DWorkSnapshot>,
|
||||||
pub error_message: Option<String>,
|
pub error_message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// 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::match_3_d_creator_config_snapshot_type::Match3DCreatorConfigSnapshot;
|
||||||
|
|
||||||
|
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||||
|
#[sats(crate = __lib)]
|
||||||
|
pub struct Match3DWorkSnapshot {
|
||||||
|
pub profile_id: String,
|
||||||
|
pub owner_user_id: String,
|
||||||
|
pub source_session_id: String,
|
||||||
|
pub author_display_name: String,
|
||||||
|
pub game_name: String,
|
||||||
|
pub theme_text: String,
|
||||||
|
pub summary_text: String,
|
||||||
|
pub tags: Vec<String>,
|
||||||
|
pub cover_image_src: String,
|
||||||
|
pub cover_asset_id: String,
|
||||||
|
pub clear_count: u32,
|
||||||
|
pub difficulty: u32,
|
||||||
|
pub config: Match3DCreatorConfigSnapshot,
|
||||||
|
pub publication_status: String,
|
||||||
|
pub publish_ready: bool,
|
||||||
|
pub play_count: u32,
|
||||||
|
pub updated_at_micros: i64,
|
||||||
|
pub published_at_micros: Option<i64>,
|
||||||
|
pub generated_item_assets_json: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for Match3DWorkSnapshot {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -4,11 +4,13 @@
|
|||||||
#![allow(unused, clippy::all)]
|
#![allow(unused, clippy::all)]
|
||||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||||
|
|
||||||
|
use super::match_3_d_work_snapshot_type::Match3DWorkSnapshot;
|
||||||
|
|
||||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||||
#[sats(crate = __lib)]
|
#[sats(crate = __lib)]
|
||||||
pub struct Match3DWorksProcedureResult {
|
pub struct Match3DWorksProcedureResult {
|
||||||
pub ok: bool,
|
pub ok: bool,
|
||||||
pub items_json: Option<String>,
|
pub items: Vec<Match3DWorkSnapshot>,
|
||||||
pub error_message: Option<String>,
|
pub error_message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ pub mod auth_store_snapshot_type;
|
|||||||
pub mod auth_store_snapshot_upsert_input_type;
|
pub mod auth_store_snapshot_upsert_input_type;
|
||||||
pub mod authorize_database_migration_operator_procedure;
|
pub mod authorize_database_migration_operator_procedure;
|
||||||
pub mod bark_battle_draft_config_row_type;
|
pub mod bark_battle_draft_config_row_type;
|
||||||
|
pub mod bark_battle_draft_config_snapshot_type;
|
||||||
pub mod bark_battle_draft_config_table;
|
pub mod bark_battle_draft_config_table;
|
||||||
pub mod bark_battle_draft_config_upsert_input_type;
|
pub mod bark_battle_draft_config_upsert_input_type;
|
||||||
pub mod bark_battle_draft_create_input_type;
|
pub mod bark_battle_draft_create_input_type;
|
||||||
@@ -107,8 +108,10 @@ pub mod bark_battle_published_config_row_type;
|
|||||||
pub mod bark_battle_published_config_table;
|
pub mod bark_battle_published_config_table;
|
||||||
pub mod bark_battle_run_finish_input_type;
|
pub mod bark_battle_run_finish_input_type;
|
||||||
pub mod bark_battle_run_get_input_type;
|
pub mod bark_battle_run_get_input_type;
|
||||||
|
pub mod bark_battle_run_snapshot_type;
|
||||||
pub mod bark_battle_run_start_input_type;
|
pub mod bark_battle_run_start_input_type;
|
||||||
pub mod bark_battle_runtime_config_get_input_type;
|
pub mod bark_battle_runtime_config_get_input_type;
|
||||||
|
pub mod bark_battle_runtime_config_snapshot_type;
|
||||||
pub mod bark_battle_runtime_run_row_type;
|
pub mod bark_battle_runtime_run_row_type;
|
||||||
pub mod bark_battle_runtime_run_table;
|
pub mod bark_battle_runtime_run_table;
|
||||||
pub mod bark_battle_score_record_row_type;
|
pub mod bark_battle_score_record_row_type;
|
||||||
@@ -149,6 +152,7 @@ pub mod big_fish_draft_compile_input_type;
|
|||||||
pub mod big_fish_event_kind_type;
|
pub mod big_fish_event_kind_type;
|
||||||
pub mod big_fish_event_table;
|
pub mod big_fish_event_table;
|
||||||
pub mod big_fish_event_type;
|
pub mod big_fish_event_type;
|
||||||
|
pub mod big_fish_gallery_view_table;
|
||||||
pub mod big_fish_game_draft_type;
|
pub mod big_fish_game_draft_type;
|
||||||
pub mod big_fish_input_submit_input_type;
|
pub mod big_fish_input_submit_input_type;
|
||||||
pub mod big_fish_level_blueprint_type;
|
pub mod big_fish_level_blueprint_type;
|
||||||
@@ -160,16 +164,20 @@ pub mod big_fish_run_get_input_type;
|
|||||||
pub mod big_fish_run_procedure_result_type;
|
pub mod big_fish_run_procedure_result_type;
|
||||||
pub mod big_fish_run_start_input_type;
|
pub mod big_fish_run_start_input_type;
|
||||||
pub mod big_fish_run_status_type;
|
pub mod big_fish_run_status_type;
|
||||||
|
pub mod big_fish_runtime_entity_snapshot_type;
|
||||||
pub mod big_fish_runtime_params_type;
|
pub mod big_fish_runtime_params_type;
|
||||||
pub mod big_fish_runtime_run_table;
|
pub mod big_fish_runtime_run_table;
|
||||||
pub mod big_fish_runtime_run_type;
|
pub mod big_fish_runtime_run_type;
|
||||||
|
pub mod big_fish_runtime_snapshot_type;
|
||||||
pub mod big_fish_session_create_input_type;
|
pub mod big_fish_session_create_input_type;
|
||||||
pub mod big_fish_session_get_input_type;
|
pub mod big_fish_session_get_input_type;
|
||||||
pub mod big_fish_session_procedure_result_type;
|
pub mod big_fish_session_procedure_result_type;
|
||||||
pub mod big_fish_session_snapshot_type;
|
pub mod big_fish_session_snapshot_type;
|
||||||
|
pub mod big_fish_vector_2_type;
|
||||||
pub mod big_fish_work_delete_input_type;
|
pub mod big_fish_work_delete_input_type;
|
||||||
pub mod big_fish_work_like_record_input_type;
|
pub mod big_fish_work_like_record_input_type;
|
||||||
pub mod big_fish_work_remix_input_type;
|
pub mod big_fish_work_remix_input_type;
|
||||||
|
pub mod big_fish_work_summary_snapshot_type;
|
||||||
pub mod big_fish_works_list_input_type;
|
pub mod big_fish_works_list_input_type;
|
||||||
pub mod big_fish_works_procedure_result_type;
|
pub mod big_fish_works_procedure_result_type;
|
||||||
pub mod bind_asset_object_to_entity_and_return_procedure;
|
pub mod bind_asset_object_to_entity_and_return_procedure;
|
||||||
@@ -402,30 +410,40 @@ pub mod list_visual_novel_works_procedure;
|
|||||||
pub mod mark_profile_recharge_order_paid_and_return_procedure;
|
pub mod mark_profile_recharge_order_paid_and_return_procedure;
|
||||||
pub mod match_3_d_agent_message_finalize_input_type;
|
pub mod match_3_d_agent_message_finalize_input_type;
|
||||||
pub mod match_3_d_agent_message_row_type;
|
pub mod match_3_d_agent_message_row_type;
|
||||||
|
pub mod match_3_d_agent_message_snapshot_type;
|
||||||
pub mod match_3_d_agent_message_submit_input_type;
|
pub mod match_3_d_agent_message_submit_input_type;
|
||||||
pub mod match_3_d_agent_message_table;
|
pub mod match_3_d_agent_message_table;
|
||||||
pub mod match_3_d_agent_session_create_input_type;
|
pub mod match_3_d_agent_session_create_input_type;
|
||||||
pub mod match_3_d_agent_session_get_input_type;
|
pub mod match_3_d_agent_session_get_input_type;
|
||||||
pub mod match_3_d_agent_session_procedure_result_type;
|
pub mod match_3_d_agent_session_procedure_result_type;
|
||||||
pub mod match_3_d_agent_session_row_type;
|
pub mod match_3_d_agent_session_row_type;
|
||||||
|
pub mod match_3_d_agent_session_snapshot_type;
|
||||||
pub mod match_3_d_agent_session_table;
|
pub mod match_3_d_agent_session_table;
|
||||||
pub mod match_3_d_click_item_procedure_result_type;
|
pub mod match_3_d_click_item_procedure_result_type;
|
||||||
|
pub mod match_3_d_creator_config_snapshot_type;
|
||||||
pub mod match_3_d_draft_compile_input_type;
|
pub mod match_3_d_draft_compile_input_type;
|
||||||
|
pub mod match_3_d_draft_snapshot_type;
|
||||||
|
pub mod match_3_d_gallery_view_row_type;
|
||||||
|
pub mod match_3_d_gallery_view_table;
|
||||||
|
pub mod match_3_d_item_snapshot_type;
|
||||||
pub mod match_3_d_run_click_input_type;
|
pub mod match_3_d_run_click_input_type;
|
||||||
pub mod match_3_d_run_get_input_type;
|
pub mod match_3_d_run_get_input_type;
|
||||||
pub mod match_3_d_run_procedure_result_type;
|
pub mod match_3_d_run_procedure_result_type;
|
||||||
pub mod match_3_d_run_restart_input_type;
|
pub mod match_3_d_run_restart_input_type;
|
||||||
|
pub mod match_3_d_run_snapshot_type;
|
||||||
pub mod match_3_d_run_start_input_type;
|
pub mod match_3_d_run_start_input_type;
|
||||||
pub mod match_3_d_run_stop_input_type;
|
pub mod match_3_d_run_stop_input_type;
|
||||||
pub mod match_3_d_run_time_up_input_type;
|
pub mod match_3_d_run_time_up_input_type;
|
||||||
pub mod match_3_d_runtime_run_row_type;
|
pub mod match_3_d_runtime_run_row_type;
|
||||||
pub mod match_3_d_runtime_run_table;
|
pub mod match_3_d_runtime_run_table;
|
||||||
|
pub mod match_3_d_tray_slot_snapshot_type;
|
||||||
pub mod match_3_d_work_delete_input_type;
|
pub mod match_3_d_work_delete_input_type;
|
||||||
pub mod match_3_d_work_get_input_type;
|
pub mod match_3_d_work_get_input_type;
|
||||||
pub mod match_3_d_work_procedure_result_type;
|
pub mod match_3_d_work_procedure_result_type;
|
||||||
pub mod match_3_d_work_profile_row_type;
|
pub mod match_3_d_work_profile_row_type;
|
||||||
pub mod match_3_d_work_profile_table;
|
pub mod match_3_d_work_profile_table;
|
||||||
pub mod match_3_d_work_publish_input_type;
|
pub mod match_3_d_work_publish_input_type;
|
||||||
|
pub mod match_3_d_work_snapshot_type;
|
||||||
pub mod match_3_d_work_update_input_type;
|
pub mod match_3_d_work_update_input_type;
|
||||||
pub mod match_3_d_works_list_input_type;
|
pub mod match_3_d_works_list_input_type;
|
||||||
pub mod match_3_d_works_procedure_result_type;
|
pub mod match_3_d_works_procedure_result_type;
|
||||||
@@ -499,34 +517,60 @@ pub mod puzzle_agent_message_finalize_input_type;
|
|||||||
pub mod puzzle_agent_message_kind_type;
|
pub mod puzzle_agent_message_kind_type;
|
||||||
pub mod puzzle_agent_message_role_type;
|
pub mod puzzle_agent_message_role_type;
|
||||||
pub mod puzzle_agent_message_row_type;
|
pub mod puzzle_agent_message_row_type;
|
||||||
|
pub mod puzzle_agent_message_snapshot_type;
|
||||||
pub mod puzzle_agent_message_submit_input_type;
|
pub mod puzzle_agent_message_submit_input_type;
|
||||||
pub mod puzzle_agent_message_table;
|
pub mod puzzle_agent_message_table;
|
||||||
pub mod puzzle_agent_session_create_input_type;
|
pub mod puzzle_agent_session_create_input_type;
|
||||||
pub mod puzzle_agent_session_get_input_type;
|
pub mod puzzle_agent_session_get_input_type;
|
||||||
pub mod puzzle_agent_session_procedure_result_type;
|
pub mod puzzle_agent_session_procedure_result_type;
|
||||||
pub mod puzzle_agent_session_row_type;
|
pub mod puzzle_agent_session_row_type;
|
||||||
|
pub mod puzzle_agent_session_snapshot_type;
|
||||||
pub mod puzzle_agent_session_table;
|
pub mod puzzle_agent_session_table;
|
||||||
pub mod puzzle_agent_stage_type;
|
pub mod puzzle_agent_stage_type;
|
||||||
|
pub mod puzzle_agent_suggested_action_type;
|
||||||
|
pub mod puzzle_anchor_item_type;
|
||||||
|
pub mod puzzle_anchor_pack_type;
|
||||||
|
pub mod puzzle_anchor_status_type;
|
||||||
|
pub mod puzzle_audio_asset_type;
|
||||||
|
pub mod puzzle_board_snapshot_type;
|
||||||
|
pub mod puzzle_cell_position_type;
|
||||||
|
pub mod puzzle_creator_intent_type;
|
||||||
pub mod puzzle_draft_compile_input_type;
|
pub mod puzzle_draft_compile_input_type;
|
||||||
|
pub mod puzzle_draft_level_type;
|
||||||
pub mod puzzle_event_kind_type;
|
pub mod puzzle_event_kind_type;
|
||||||
pub mod puzzle_event_table;
|
pub mod puzzle_event_table;
|
||||||
pub mod puzzle_event_type;
|
pub mod puzzle_event_type;
|
||||||
pub mod puzzle_form_draft_save_input_type;
|
pub mod puzzle_form_draft_save_input_type;
|
||||||
|
pub mod puzzle_form_draft_type;
|
||||||
|
pub mod puzzle_gallery_card_view_row_type;
|
||||||
|
pub mod puzzle_gallery_card_view_table;
|
||||||
pub mod puzzle_gallery_view_table;
|
pub mod puzzle_gallery_view_table;
|
||||||
|
pub mod puzzle_generated_image_candidate_type;
|
||||||
pub mod puzzle_generated_images_save_input_type;
|
pub mod puzzle_generated_images_save_input_type;
|
||||||
pub mod puzzle_leaderboard_entry_row_type;
|
pub mod puzzle_leaderboard_entry_row_type;
|
||||||
pub mod puzzle_leaderboard_entry_table;
|
pub mod puzzle_leaderboard_entry_table;
|
||||||
|
pub mod puzzle_leaderboard_entry_type;
|
||||||
pub mod puzzle_leaderboard_submit_input_type;
|
pub mod puzzle_leaderboard_submit_input_type;
|
||||||
|
pub mod puzzle_merged_group_state_type;
|
||||||
|
pub mod puzzle_piece_state_type;
|
||||||
pub mod puzzle_publication_status_type;
|
pub mod puzzle_publication_status_type;
|
||||||
pub mod puzzle_publish_input_type;
|
pub mod puzzle_publish_input_type;
|
||||||
|
pub mod puzzle_recommended_next_work_type;
|
||||||
|
pub mod puzzle_result_draft_type;
|
||||||
|
pub mod puzzle_result_preview_blocker_type;
|
||||||
|
pub mod puzzle_result_preview_envelope_type;
|
||||||
|
pub mod puzzle_result_preview_finding_type;
|
||||||
pub mod puzzle_run_drag_input_type;
|
pub mod puzzle_run_drag_input_type;
|
||||||
pub mod puzzle_run_get_input_type;
|
pub mod puzzle_run_get_input_type;
|
||||||
pub mod puzzle_run_next_level_input_type;
|
pub mod puzzle_run_next_level_input_type;
|
||||||
pub mod puzzle_run_pause_input_type;
|
pub mod puzzle_run_pause_input_type;
|
||||||
pub mod puzzle_run_procedure_result_type;
|
pub mod puzzle_run_procedure_result_type;
|
||||||
pub mod puzzle_run_prop_input_type;
|
pub mod puzzle_run_prop_input_type;
|
||||||
|
pub mod puzzle_run_snapshot_type;
|
||||||
pub mod puzzle_run_start_input_type;
|
pub mod puzzle_run_start_input_type;
|
||||||
pub mod puzzle_run_swap_input_type;
|
pub mod puzzle_run_swap_input_type;
|
||||||
|
pub mod puzzle_runtime_level_snapshot_type;
|
||||||
|
pub mod puzzle_runtime_level_status_type;
|
||||||
pub mod puzzle_runtime_run_row_type;
|
pub mod puzzle_runtime_run_row_type;
|
||||||
pub mod puzzle_runtime_run_table;
|
pub mod puzzle_runtime_run_table;
|
||||||
pub mod puzzle_select_cover_image_input_type;
|
pub mod puzzle_select_cover_image_input_type;
|
||||||
@@ -538,6 +582,7 @@ pub mod puzzle_work_point_incentive_claim_input_type;
|
|||||||
pub mod puzzle_work_procedure_result_type;
|
pub mod puzzle_work_procedure_result_type;
|
||||||
pub mod puzzle_work_profile_row_type;
|
pub mod puzzle_work_profile_row_type;
|
||||||
pub mod puzzle_work_profile_table;
|
pub mod puzzle_work_profile_table;
|
||||||
|
pub mod puzzle_work_profile_type;
|
||||||
pub mod puzzle_work_remix_input_type;
|
pub mod puzzle_work_remix_input_type;
|
||||||
pub mod puzzle_work_upsert_input_type;
|
pub mod puzzle_work_upsert_input_type;
|
||||||
pub mod puzzle_works_list_input_type;
|
pub mod puzzle_works_list_input_type;
|
||||||
@@ -729,30 +774,43 @@ pub mod seed_analytics_date_dimensions_reducer;
|
|||||||
pub mod select_puzzle_cover_image_procedure;
|
pub mod select_puzzle_cover_image_procedure;
|
||||||
pub mod square_hole_agent_message_finalize_input_type;
|
pub mod square_hole_agent_message_finalize_input_type;
|
||||||
pub mod square_hole_agent_message_row_type;
|
pub mod square_hole_agent_message_row_type;
|
||||||
|
pub mod square_hole_agent_message_snapshot_type;
|
||||||
pub mod square_hole_agent_message_submit_input_type;
|
pub mod square_hole_agent_message_submit_input_type;
|
||||||
pub mod square_hole_agent_message_table;
|
pub mod square_hole_agent_message_table;
|
||||||
pub mod square_hole_agent_session_create_input_type;
|
pub mod square_hole_agent_session_create_input_type;
|
||||||
pub mod square_hole_agent_session_get_input_type;
|
pub mod square_hole_agent_session_get_input_type;
|
||||||
pub mod square_hole_agent_session_procedure_result_type;
|
pub mod square_hole_agent_session_procedure_result_type;
|
||||||
pub mod square_hole_agent_session_row_type;
|
pub mod square_hole_agent_session_row_type;
|
||||||
|
pub mod square_hole_agent_session_snapshot_type;
|
||||||
pub mod square_hole_agent_session_table;
|
pub mod square_hole_agent_session_table;
|
||||||
|
pub mod square_hole_creator_config_snapshot_type;
|
||||||
pub mod square_hole_draft_compile_input_type;
|
pub mod square_hole_draft_compile_input_type;
|
||||||
|
pub mod square_hole_draft_snapshot_type;
|
||||||
|
pub mod square_hole_drop_feedback_snapshot_type;
|
||||||
pub mod square_hole_drop_shape_procedure_result_type;
|
pub mod square_hole_drop_shape_procedure_result_type;
|
||||||
|
pub mod square_hole_gallery_view_row_type;
|
||||||
|
pub mod square_hole_gallery_view_table;
|
||||||
|
pub mod square_hole_hole_option_snapshot_type;
|
||||||
|
pub mod square_hole_hole_snapshot_type;
|
||||||
pub mod square_hole_run_drop_input_type;
|
pub mod square_hole_run_drop_input_type;
|
||||||
pub mod square_hole_run_get_input_type;
|
pub mod square_hole_run_get_input_type;
|
||||||
pub mod square_hole_run_procedure_result_type;
|
pub mod square_hole_run_procedure_result_type;
|
||||||
pub mod square_hole_run_restart_input_type;
|
pub mod square_hole_run_restart_input_type;
|
||||||
|
pub mod square_hole_run_snapshot_type;
|
||||||
pub mod square_hole_run_start_input_type;
|
pub mod square_hole_run_start_input_type;
|
||||||
pub mod square_hole_run_stop_input_type;
|
pub mod square_hole_run_stop_input_type;
|
||||||
pub mod square_hole_run_time_up_input_type;
|
pub mod square_hole_run_time_up_input_type;
|
||||||
pub mod square_hole_runtime_run_row_type;
|
pub mod square_hole_runtime_run_row_type;
|
||||||
pub mod square_hole_runtime_run_table;
|
pub mod square_hole_runtime_run_table;
|
||||||
|
pub mod square_hole_shape_option_snapshot_type;
|
||||||
|
pub mod square_hole_shape_snapshot_type;
|
||||||
pub mod square_hole_work_delete_input_type;
|
pub mod square_hole_work_delete_input_type;
|
||||||
pub mod square_hole_work_get_input_type;
|
pub mod square_hole_work_get_input_type;
|
||||||
pub mod square_hole_work_procedure_result_type;
|
pub mod square_hole_work_procedure_result_type;
|
||||||
pub mod square_hole_work_profile_row_type;
|
pub mod square_hole_work_profile_row_type;
|
||||||
pub mod square_hole_work_profile_table;
|
pub mod square_hole_work_profile_table;
|
||||||
pub mod square_hole_work_publish_input_type;
|
pub mod square_hole_work_publish_input_type;
|
||||||
|
pub mod square_hole_work_snapshot_type;
|
||||||
pub mod square_hole_work_update_input_type;
|
pub mod square_hole_work_update_input_type;
|
||||||
pub mod square_hole_works_list_input_type;
|
pub mod square_hole_works_list_input_type;
|
||||||
pub mod square_hole_works_procedure_result_type;
|
pub mod square_hole_works_procedure_result_type;
|
||||||
@@ -829,24 +887,33 @@ pub mod user_browse_history_table;
|
|||||||
pub mod user_browse_history_type;
|
pub mod user_browse_history_type;
|
||||||
pub mod visual_novel_agent_message_finalize_input_type;
|
pub mod visual_novel_agent_message_finalize_input_type;
|
||||||
pub mod visual_novel_agent_message_row_type;
|
pub mod visual_novel_agent_message_row_type;
|
||||||
|
pub mod visual_novel_agent_message_snapshot_type;
|
||||||
pub mod visual_novel_agent_message_submit_input_type;
|
pub mod visual_novel_agent_message_submit_input_type;
|
||||||
pub mod visual_novel_agent_message_table;
|
pub mod visual_novel_agent_message_table;
|
||||||
pub mod visual_novel_agent_session_create_input_type;
|
pub mod visual_novel_agent_session_create_input_type;
|
||||||
pub mod visual_novel_agent_session_get_input_type;
|
pub mod visual_novel_agent_session_get_input_type;
|
||||||
pub mod visual_novel_agent_session_procedure_result_type;
|
pub mod visual_novel_agent_session_procedure_result_type;
|
||||||
pub mod visual_novel_agent_session_row_type;
|
pub mod visual_novel_agent_session_row_type;
|
||||||
|
pub mod visual_novel_agent_session_snapshot_type;
|
||||||
pub mod visual_novel_agent_session_table;
|
pub mod visual_novel_agent_session_table;
|
||||||
|
pub mod visual_novel_gallery_view_row_type;
|
||||||
|
pub mod visual_novel_gallery_view_table;
|
||||||
pub mod visual_novel_history_procedure_result_type;
|
pub mod visual_novel_history_procedure_result_type;
|
||||||
|
pub mod visual_novel_json_field_type;
|
||||||
|
pub mod visual_novel_json_value_type;
|
||||||
pub mod visual_novel_run_get_input_type;
|
pub mod visual_novel_run_get_input_type;
|
||||||
pub mod visual_novel_run_procedure_result_type;
|
pub mod visual_novel_run_procedure_result_type;
|
||||||
|
pub mod visual_novel_run_snapshot_type;
|
||||||
pub mod visual_novel_run_snapshot_upsert_input_type;
|
pub mod visual_novel_run_snapshot_upsert_input_type;
|
||||||
pub mod visual_novel_run_start_input_type;
|
pub mod visual_novel_run_start_input_type;
|
||||||
pub mod visual_novel_runtime_event_procedure_result_type;
|
pub mod visual_novel_runtime_event_procedure_result_type;
|
||||||
pub mod visual_novel_runtime_event_record_input_type;
|
pub mod visual_novel_runtime_event_record_input_type;
|
||||||
|
pub mod visual_novel_runtime_event_snapshot_type;
|
||||||
pub mod visual_novel_runtime_event_table;
|
pub mod visual_novel_runtime_event_table;
|
||||||
pub mod visual_novel_runtime_event_type;
|
pub mod visual_novel_runtime_event_type;
|
||||||
pub mod visual_novel_runtime_history_append_input_type;
|
pub mod visual_novel_runtime_history_append_input_type;
|
||||||
pub mod visual_novel_runtime_history_entry_row_type;
|
pub mod visual_novel_runtime_history_entry_row_type;
|
||||||
|
pub mod visual_novel_runtime_history_entry_snapshot_type;
|
||||||
pub mod visual_novel_runtime_history_entry_table;
|
pub mod visual_novel_runtime_history_entry_table;
|
||||||
pub mod visual_novel_runtime_history_list_input_type;
|
pub mod visual_novel_runtime_history_list_input_type;
|
||||||
pub mod visual_novel_runtime_run_row_type;
|
pub mod visual_novel_runtime_run_row_type;
|
||||||
@@ -858,6 +925,7 @@ pub mod visual_novel_work_procedure_result_type;
|
|||||||
pub mod visual_novel_work_profile_row_type;
|
pub mod visual_novel_work_profile_row_type;
|
||||||
pub mod visual_novel_work_profile_table;
|
pub mod visual_novel_work_profile_table;
|
||||||
pub mod visual_novel_work_publish_input_type;
|
pub mod visual_novel_work_publish_input_type;
|
||||||
|
pub mod visual_novel_work_snapshot_type;
|
||||||
pub mod visual_novel_work_update_input_type;
|
pub mod visual_novel_work_update_input_type;
|
||||||
pub mod visual_novel_works_list_input_type;
|
pub mod visual_novel_works_list_input_type;
|
||||||
pub mod visual_novel_works_procedure_result_type;
|
pub mod visual_novel_works_procedure_result_type;
|
||||||
@@ -951,6 +1019,7 @@ pub use auth_store_snapshot_type::AuthStoreSnapshot;
|
|||||||
pub use auth_store_snapshot_upsert_input_type::AuthStoreSnapshotUpsertInput;
|
pub use auth_store_snapshot_upsert_input_type::AuthStoreSnapshotUpsertInput;
|
||||||
pub use authorize_database_migration_operator_procedure::authorize_database_migration_operator;
|
pub use authorize_database_migration_operator_procedure::authorize_database_migration_operator;
|
||||||
pub use bark_battle_draft_config_row_type::BarkBattleDraftConfigRow;
|
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::*;
|
pub use bark_battle_draft_config_table::*;
|
||||||
pub use bark_battle_draft_config_upsert_input_type::BarkBattleDraftConfigUpsertInput;
|
pub use bark_battle_draft_config_upsert_input_type::BarkBattleDraftConfigUpsertInput;
|
||||||
pub use bark_battle_draft_create_input_type::BarkBattleDraftCreateInput;
|
pub use bark_battle_draft_create_input_type::BarkBattleDraftCreateInput;
|
||||||
@@ -963,8 +1032,10 @@ pub use bark_battle_published_config_row_type::BarkBattlePublishedConfigRow;
|
|||||||
pub use bark_battle_published_config_table::*;
|
pub use bark_battle_published_config_table::*;
|
||||||
pub use bark_battle_run_finish_input_type::BarkBattleRunFinishInput;
|
pub use bark_battle_run_finish_input_type::BarkBattleRunFinishInput;
|
||||||
pub use bark_battle_run_get_input_type::BarkBattleRunGetInput;
|
pub use bark_battle_run_get_input_type::BarkBattleRunGetInput;
|
||||||
|
pub use bark_battle_run_snapshot_type::BarkBattleRunSnapshot;
|
||||||
pub use bark_battle_run_start_input_type::BarkBattleRunStartInput;
|
pub use bark_battle_run_start_input_type::BarkBattleRunStartInput;
|
||||||
pub use bark_battle_runtime_config_get_input_type::BarkBattleRuntimeConfigGetInput;
|
pub use bark_battle_runtime_config_get_input_type::BarkBattleRuntimeConfigGetInput;
|
||||||
|
pub use bark_battle_runtime_config_snapshot_type::BarkBattleRuntimeConfigSnapshot;
|
||||||
pub use bark_battle_runtime_run_row_type::BarkBattleRuntimeRunRow;
|
pub use bark_battle_runtime_run_row_type::BarkBattleRuntimeRunRow;
|
||||||
pub use bark_battle_runtime_run_table::*;
|
pub use bark_battle_runtime_run_table::*;
|
||||||
pub use bark_battle_score_record_row_type::BarkBattleScoreRecordRow;
|
pub use bark_battle_score_record_row_type::BarkBattleScoreRecordRow;
|
||||||
@@ -1005,6 +1076,7 @@ pub use big_fish_draft_compile_input_type::BigFishDraftCompileInput;
|
|||||||
pub use big_fish_event_kind_type::BigFishEventKind;
|
pub use big_fish_event_kind_type::BigFishEventKind;
|
||||||
pub use big_fish_event_table::*;
|
pub use big_fish_event_table::*;
|
||||||
pub use big_fish_event_type::BigFishEvent;
|
pub use big_fish_event_type::BigFishEvent;
|
||||||
|
pub use big_fish_gallery_view_table::*;
|
||||||
pub use big_fish_game_draft_type::BigFishGameDraft;
|
pub use big_fish_game_draft_type::BigFishGameDraft;
|
||||||
pub use big_fish_input_submit_input_type::BigFishInputSubmitInput;
|
pub use big_fish_input_submit_input_type::BigFishInputSubmitInput;
|
||||||
pub use big_fish_level_blueprint_type::BigFishLevelBlueprint;
|
pub use big_fish_level_blueprint_type::BigFishLevelBlueprint;
|
||||||
@@ -1016,16 +1088,20 @@ pub use big_fish_run_get_input_type::BigFishRunGetInput;
|
|||||||
pub use big_fish_run_procedure_result_type::BigFishRunProcedureResult;
|
pub use big_fish_run_procedure_result_type::BigFishRunProcedureResult;
|
||||||
pub use big_fish_run_start_input_type::BigFishRunStartInput;
|
pub use big_fish_run_start_input_type::BigFishRunStartInput;
|
||||||
pub use big_fish_run_status_type::BigFishRunStatus;
|
pub use big_fish_run_status_type::BigFishRunStatus;
|
||||||
|
pub use big_fish_runtime_entity_snapshot_type::BigFishRuntimeEntitySnapshot;
|
||||||
pub use big_fish_runtime_params_type::BigFishRuntimeParams;
|
pub use big_fish_runtime_params_type::BigFishRuntimeParams;
|
||||||
pub use big_fish_runtime_run_table::*;
|
pub use big_fish_runtime_run_table::*;
|
||||||
pub use big_fish_runtime_run_type::BigFishRuntimeRun;
|
pub use big_fish_runtime_run_type::BigFishRuntimeRun;
|
||||||
|
pub use big_fish_runtime_snapshot_type::BigFishRuntimeSnapshot;
|
||||||
pub use big_fish_session_create_input_type::BigFishSessionCreateInput;
|
pub use big_fish_session_create_input_type::BigFishSessionCreateInput;
|
||||||
pub use big_fish_session_get_input_type::BigFishSessionGetInput;
|
pub use big_fish_session_get_input_type::BigFishSessionGetInput;
|
||||||
pub use big_fish_session_procedure_result_type::BigFishSessionProcedureResult;
|
pub use big_fish_session_procedure_result_type::BigFishSessionProcedureResult;
|
||||||
pub use big_fish_session_snapshot_type::BigFishSessionSnapshot;
|
pub use big_fish_session_snapshot_type::BigFishSessionSnapshot;
|
||||||
|
pub use big_fish_vector_2_type::BigFishVector2;
|
||||||
pub use big_fish_work_delete_input_type::BigFishWorkDeleteInput;
|
pub use big_fish_work_delete_input_type::BigFishWorkDeleteInput;
|
||||||
pub use big_fish_work_like_record_input_type::BigFishWorkLikeRecordInput;
|
pub use big_fish_work_like_record_input_type::BigFishWorkLikeRecordInput;
|
||||||
pub use big_fish_work_remix_input_type::BigFishWorkRemixInput;
|
pub use big_fish_work_remix_input_type::BigFishWorkRemixInput;
|
||||||
|
pub use big_fish_work_summary_snapshot_type::BigFishWorkSummarySnapshot;
|
||||||
pub use big_fish_works_list_input_type::BigFishWorksListInput;
|
pub use big_fish_works_list_input_type::BigFishWorksListInput;
|
||||||
pub use big_fish_works_procedure_result_type::BigFishWorksProcedureResult;
|
pub use big_fish_works_procedure_result_type::BigFishWorksProcedureResult;
|
||||||
pub use bind_asset_object_to_entity_and_return_procedure::bind_asset_object_to_entity_and_return;
|
pub use bind_asset_object_to_entity_and_return_procedure::bind_asset_object_to_entity_and_return;
|
||||||
@@ -1258,30 +1334,40 @@ pub use list_visual_novel_works_procedure::list_visual_novel_works;
|
|||||||
pub use mark_profile_recharge_order_paid_and_return_procedure::mark_profile_recharge_order_paid_and_return;
|
pub use mark_profile_recharge_order_paid_and_return_procedure::mark_profile_recharge_order_paid_and_return;
|
||||||
pub use match_3_d_agent_message_finalize_input_type::Match3DAgentMessageFinalizeInput;
|
pub use match_3_d_agent_message_finalize_input_type::Match3DAgentMessageFinalizeInput;
|
||||||
pub use match_3_d_agent_message_row_type::Match3DAgentMessageRow;
|
pub use match_3_d_agent_message_row_type::Match3DAgentMessageRow;
|
||||||
|
pub use match_3_d_agent_message_snapshot_type::Match3DAgentMessageSnapshot;
|
||||||
pub use match_3_d_agent_message_submit_input_type::Match3DAgentMessageSubmitInput;
|
pub use match_3_d_agent_message_submit_input_type::Match3DAgentMessageSubmitInput;
|
||||||
pub use match_3_d_agent_message_table::*;
|
pub use match_3_d_agent_message_table::*;
|
||||||
pub use match_3_d_agent_session_create_input_type::Match3DAgentSessionCreateInput;
|
pub use match_3_d_agent_session_create_input_type::Match3DAgentSessionCreateInput;
|
||||||
pub use match_3_d_agent_session_get_input_type::Match3DAgentSessionGetInput;
|
pub use match_3_d_agent_session_get_input_type::Match3DAgentSessionGetInput;
|
||||||
pub use match_3_d_agent_session_procedure_result_type::Match3DAgentSessionProcedureResult;
|
pub use match_3_d_agent_session_procedure_result_type::Match3DAgentSessionProcedureResult;
|
||||||
pub use match_3_d_agent_session_row_type::Match3DAgentSessionRow;
|
pub use match_3_d_agent_session_row_type::Match3DAgentSessionRow;
|
||||||
|
pub use match_3_d_agent_session_snapshot_type::Match3DAgentSessionSnapshot;
|
||||||
pub use match_3_d_agent_session_table::*;
|
pub use match_3_d_agent_session_table::*;
|
||||||
pub use match_3_d_click_item_procedure_result_type::Match3DClickItemProcedureResult;
|
pub use match_3_d_click_item_procedure_result_type::Match3DClickItemProcedureResult;
|
||||||
|
pub use match_3_d_creator_config_snapshot_type::Match3DCreatorConfigSnapshot;
|
||||||
pub use match_3_d_draft_compile_input_type::Match3DDraftCompileInput;
|
pub use match_3_d_draft_compile_input_type::Match3DDraftCompileInput;
|
||||||
|
pub use match_3_d_draft_snapshot_type::Match3DDraftSnapshot;
|
||||||
|
pub use match_3_d_gallery_view_row_type::Match3DGalleryViewRow;
|
||||||
|
pub use match_3_d_gallery_view_table::*;
|
||||||
|
pub use match_3_d_item_snapshot_type::Match3DItemSnapshot;
|
||||||
pub use match_3_d_run_click_input_type::Match3DRunClickInput;
|
pub use match_3_d_run_click_input_type::Match3DRunClickInput;
|
||||||
pub use match_3_d_run_get_input_type::Match3DRunGetInput;
|
pub use match_3_d_run_get_input_type::Match3DRunGetInput;
|
||||||
pub use match_3_d_run_procedure_result_type::Match3DRunProcedureResult;
|
pub use match_3_d_run_procedure_result_type::Match3DRunProcedureResult;
|
||||||
pub use match_3_d_run_restart_input_type::Match3DRunRestartInput;
|
pub use match_3_d_run_restart_input_type::Match3DRunRestartInput;
|
||||||
|
pub use match_3_d_run_snapshot_type::Match3DRunSnapshot;
|
||||||
pub use match_3_d_run_start_input_type::Match3DRunStartInput;
|
pub use match_3_d_run_start_input_type::Match3DRunStartInput;
|
||||||
pub use match_3_d_run_stop_input_type::Match3DRunStopInput;
|
pub use match_3_d_run_stop_input_type::Match3DRunStopInput;
|
||||||
pub use match_3_d_run_time_up_input_type::Match3DRunTimeUpInput;
|
pub use match_3_d_run_time_up_input_type::Match3DRunTimeUpInput;
|
||||||
pub use match_3_d_runtime_run_row_type::Match3DRuntimeRunRow;
|
pub use match_3_d_runtime_run_row_type::Match3DRuntimeRunRow;
|
||||||
pub use match_3_d_runtime_run_table::*;
|
pub use match_3_d_runtime_run_table::*;
|
||||||
|
pub use match_3_d_tray_slot_snapshot_type::Match3DTraySlotSnapshot;
|
||||||
pub use match_3_d_work_delete_input_type::Match3DWorkDeleteInput;
|
pub use match_3_d_work_delete_input_type::Match3DWorkDeleteInput;
|
||||||
pub use match_3_d_work_get_input_type::Match3DWorkGetInput;
|
pub use match_3_d_work_get_input_type::Match3DWorkGetInput;
|
||||||
pub use match_3_d_work_procedure_result_type::Match3DWorkProcedureResult;
|
pub use match_3_d_work_procedure_result_type::Match3DWorkProcedureResult;
|
||||||
pub use match_3_d_work_profile_row_type::Match3DWorkProfileRow;
|
pub use match_3_d_work_profile_row_type::Match3DWorkProfileRow;
|
||||||
pub use match_3_d_work_profile_table::*;
|
pub use match_3_d_work_profile_table::*;
|
||||||
pub use match_3_d_work_publish_input_type::Match3DWorkPublishInput;
|
pub use match_3_d_work_publish_input_type::Match3DWorkPublishInput;
|
||||||
|
pub use match_3_d_work_snapshot_type::Match3DWorkSnapshot;
|
||||||
pub use match_3_d_work_update_input_type::Match3DWorkUpdateInput;
|
pub use match_3_d_work_update_input_type::Match3DWorkUpdateInput;
|
||||||
pub use match_3_d_works_list_input_type::Match3DWorksListInput;
|
pub use match_3_d_works_list_input_type::Match3DWorksListInput;
|
||||||
pub use match_3_d_works_procedure_result_type::Match3DWorksProcedureResult;
|
pub use match_3_d_works_procedure_result_type::Match3DWorksProcedureResult;
|
||||||
@@ -1355,34 +1441,60 @@ pub use puzzle_agent_message_finalize_input_type::PuzzleAgentMessageFinalizeInpu
|
|||||||
pub use puzzle_agent_message_kind_type::PuzzleAgentMessageKind;
|
pub use puzzle_agent_message_kind_type::PuzzleAgentMessageKind;
|
||||||
pub use puzzle_agent_message_role_type::PuzzleAgentMessageRole;
|
pub use puzzle_agent_message_role_type::PuzzleAgentMessageRole;
|
||||||
pub use puzzle_agent_message_row_type::PuzzleAgentMessageRow;
|
pub use puzzle_agent_message_row_type::PuzzleAgentMessageRow;
|
||||||
|
pub use puzzle_agent_message_snapshot_type::PuzzleAgentMessageSnapshot;
|
||||||
pub use puzzle_agent_message_submit_input_type::PuzzleAgentMessageSubmitInput;
|
pub use puzzle_agent_message_submit_input_type::PuzzleAgentMessageSubmitInput;
|
||||||
pub use puzzle_agent_message_table::*;
|
pub use puzzle_agent_message_table::*;
|
||||||
pub use puzzle_agent_session_create_input_type::PuzzleAgentSessionCreateInput;
|
pub use puzzle_agent_session_create_input_type::PuzzleAgentSessionCreateInput;
|
||||||
pub use puzzle_agent_session_get_input_type::PuzzleAgentSessionGetInput;
|
pub use puzzle_agent_session_get_input_type::PuzzleAgentSessionGetInput;
|
||||||
pub use puzzle_agent_session_procedure_result_type::PuzzleAgentSessionProcedureResult;
|
pub use puzzle_agent_session_procedure_result_type::PuzzleAgentSessionProcedureResult;
|
||||||
pub use puzzle_agent_session_row_type::PuzzleAgentSessionRow;
|
pub use puzzle_agent_session_row_type::PuzzleAgentSessionRow;
|
||||||
|
pub use puzzle_agent_session_snapshot_type::PuzzleAgentSessionSnapshot;
|
||||||
pub use puzzle_agent_session_table::*;
|
pub use puzzle_agent_session_table::*;
|
||||||
pub use puzzle_agent_stage_type::PuzzleAgentStage;
|
pub use puzzle_agent_stage_type::PuzzleAgentStage;
|
||||||
|
pub use puzzle_agent_suggested_action_type::PuzzleAgentSuggestedAction;
|
||||||
|
pub use puzzle_anchor_item_type::PuzzleAnchorItem;
|
||||||
|
pub use puzzle_anchor_pack_type::PuzzleAnchorPack;
|
||||||
|
pub use puzzle_anchor_status_type::PuzzleAnchorStatus;
|
||||||
|
pub use puzzle_audio_asset_type::PuzzleAudioAsset;
|
||||||
|
pub use puzzle_board_snapshot_type::PuzzleBoardSnapshot;
|
||||||
|
pub use puzzle_cell_position_type::PuzzleCellPosition;
|
||||||
|
pub use puzzle_creator_intent_type::PuzzleCreatorIntent;
|
||||||
pub use puzzle_draft_compile_input_type::PuzzleDraftCompileInput;
|
pub use puzzle_draft_compile_input_type::PuzzleDraftCompileInput;
|
||||||
|
pub use puzzle_draft_level_type::PuzzleDraftLevel;
|
||||||
pub use puzzle_event_kind_type::PuzzleEventKind;
|
pub use puzzle_event_kind_type::PuzzleEventKind;
|
||||||
pub use puzzle_event_table::*;
|
pub use puzzle_event_table::*;
|
||||||
pub use puzzle_event_type::PuzzleEvent;
|
pub use puzzle_event_type::PuzzleEvent;
|
||||||
pub use puzzle_form_draft_save_input_type::PuzzleFormDraftSaveInput;
|
pub use puzzle_form_draft_save_input_type::PuzzleFormDraftSaveInput;
|
||||||
|
pub use puzzle_form_draft_type::PuzzleFormDraft;
|
||||||
|
pub use puzzle_gallery_card_view_row_type::PuzzleGalleryCardViewRow;
|
||||||
|
pub use puzzle_gallery_card_view_table::*;
|
||||||
pub use puzzle_gallery_view_table::*;
|
pub use puzzle_gallery_view_table::*;
|
||||||
|
pub use puzzle_generated_image_candidate_type::PuzzleGeneratedImageCandidate;
|
||||||
pub use puzzle_generated_images_save_input_type::PuzzleGeneratedImagesSaveInput;
|
pub use puzzle_generated_images_save_input_type::PuzzleGeneratedImagesSaveInput;
|
||||||
pub use puzzle_leaderboard_entry_row_type::PuzzleLeaderboardEntryRow;
|
pub use puzzle_leaderboard_entry_row_type::PuzzleLeaderboardEntryRow;
|
||||||
pub use puzzle_leaderboard_entry_table::*;
|
pub use puzzle_leaderboard_entry_table::*;
|
||||||
|
pub use puzzle_leaderboard_entry_type::PuzzleLeaderboardEntry;
|
||||||
pub use puzzle_leaderboard_submit_input_type::PuzzleLeaderboardSubmitInput;
|
pub use puzzle_leaderboard_submit_input_type::PuzzleLeaderboardSubmitInput;
|
||||||
|
pub use puzzle_merged_group_state_type::PuzzleMergedGroupState;
|
||||||
|
pub use puzzle_piece_state_type::PuzzlePieceState;
|
||||||
pub use puzzle_publication_status_type::PuzzlePublicationStatus;
|
pub use puzzle_publication_status_type::PuzzlePublicationStatus;
|
||||||
pub use puzzle_publish_input_type::PuzzlePublishInput;
|
pub use puzzle_publish_input_type::PuzzlePublishInput;
|
||||||
|
pub use puzzle_recommended_next_work_type::PuzzleRecommendedNextWork;
|
||||||
|
pub use puzzle_result_draft_type::PuzzleResultDraft;
|
||||||
|
pub use puzzle_result_preview_blocker_type::PuzzleResultPreviewBlocker;
|
||||||
|
pub use puzzle_result_preview_envelope_type::PuzzleResultPreviewEnvelope;
|
||||||
|
pub use puzzle_result_preview_finding_type::PuzzleResultPreviewFinding;
|
||||||
pub use puzzle_run_drag_input_type::PuzzleRunDragInput;
|
pub use puzzle_run_drag_input_type::PuzzleRunDragInput;
|
||||||
pub use puzzle_run_get_input_type::PuzzleRunGetInput;
|
pub use puzzle_run_get_input_type::PuzzleRunGetInput;
|
||||||
pub use puzzle_run_next_level_input_type::PuzzleRunNextLevelInput;
|
pub use puzzle_run_next_level_input_type::PuzzleRunNextLevelInput;
|
||||||
pub use puzzle_run_pause_input_type::PuzzleRunPauseInput;
|
pub use puzzle_run_pause_input_type::PuzzleRunPauseInput;
|
||||||
pub use puzzle_run_procedure_result_type::PuzzleRunProcedureResult;
|
pub use puzzle_run_procedure_result_type::PuzzleRunProcedureResult;
|
||||||
pub use puzzle_run_prop_input_type::PuzzleRunPropInput;
|
pub use puzzle_run_prop_input_type::PuzzleRunPropInput;
|
||||||
|
pub use puzzle_run_snapshot_type::PuzzleRunSnapshot;
|
||||||
pub use puzzle_run_start_input_type::PuzzleRunStartInput;
|
pub use puzzle_run_start_input_type::PuzzleRunStartInput;
|
||||||
pub use puzzle_run_swap_input_type::PuzzleRunSwapInput;
|
pub use puzzle_run_swap_input_type::PuzzleRunSwapInput;
|
||||||
|
pub use puzzle_runtime_level_snapshot_type::PuzzleRuntimeLevelSnapshot;
|
||||||
|
pub use puzzle_runtime_level_status_type::PuzzleRuntimeLevelStatus;
|
||||||
pub use puzzle_runtime_run_row_type::PuzzleRuntimeRunRow;
|
pub use puzzle_runtime_run_row_type::PuzzleRuntimeRunRow;
|
||||||
pub use puzzle_runtime_run_table::*;
|
pub use puzzle_runtime_run_table::*;
|
||||||
pub use puzzle_select_cover_image_input_type::PuzzleSelectCoverImageInput;
|
pub use puzzle_select_cover_image_input_type::PuzzleSelectCoverImageInput;
|
||||||
@@ -1394,6 +1506,7 @@ pub use puzzle_work_point_incentive_claim_input_type::PuzzleWorkPointIncentiveCl
|
|||||||
pub use puzzle_work_procedure_result_type::PuzzleWorkProcedureResult;
|
pub use puzzle_work_procedure_result_type::PuzzleWorkProcedureResult;
|
||||||
pub use puzzle_work_profile_row_type::PuzzleWorkProfileRow;
|
pub use puzzle_work_profile_row_type::PuzzleWorkProfileRow;
|
||||||
pub use puzzle_work_profile_table::*;
|
pub use puzzle_work_profile_table::*;
|
||||||
|
pub use puzzle_work_profile_type::PuzzleWorkProfile;
|
||||||
pub use puzzle_work_remix_input_type::PuzzleWorkRemixInput;
|
pub use puzzle_work_remix_input_type::PuzzleWorkRemixInput;
|
||||||
pub use puzzle_work_upsert_input_type::PuzzleWorkUpsertInput;
|
pub use puzzle_work_upsert_input_type::PuzzleWorkUpsertInput;
|
||||||
pub use puzzle_works_list_input_type::PuzzleWorksListInput;
|
pub use puzzle_works_list_input_type::PuzzleWorksListInput;
|
||||||
@@ -1585,30 +1698,43 @@ pub use seed_analytics_date_dimensions_reducer::seed_analytics_date_dimensions;
|
|||||||
pub use select_puzzle_cover_image_procedure::select_puzzle_cover_image;
|
pub use select_puzzle_cover_image_procedure::select_puzzle_cover_image;
|
||||||
pub use square_hole_agent_message_finalize_input_type::SquareHoleAgentMessageFinalizeInput;
|
pub use square_hole_agent_message_finalize_input_type::SquareHoleAgentMessageFinalizeInput;
|
||||||
pub use square_hole_agent_message_row_type::SquareHoleAgentMessageRow;
|
pub use square_hole_agent_message_row_type::SquareHoleAgentMessageRow;
|
||||||
|
pub use square_hole_agent_message_snapshot_type::SquareHoleAgentMessageSnapshot;
|
||||||
pub use square_hole_agent_message_submit_input_type::SquareHoleAgentMessageSubmitInput;
|
pub use square_hole_agent_message_submit_input_type::SquareHoleAgentMessageSubmitInput;
|
||||||
pub use square_hole_agent_message_table::*;
|
pub use square_hole_agent_message_table::*;
|
||||||
pub use square_hole_agent_session_create_input_type::SquareHoleAgentSessionCreateInput;
|
pub use square_hole_agent_session_create_input_type::SquareHoleAgentSessionCreateInput;
|
||||||
pub use square_hole_agent_session_get_input_type::SquareHoleAgentSessionGetInput;
|
pub use square_hole_agent_session_get_input_type::SquareHoleAgentSessionGetInput;
|
||||||
pub use square_hole_agent_session_procedure_result_type::SquareHoleAgentSessionProcedureResult;
|
pub use square_hole_agent_session_procedure_result_type::SquareHoleAgentSessionProcedureResult;
|
||||||
pub use square_hole_agent_session_row_type::SquareHoleAgentSessionRow;
|
pub use square_hole_agent_session_row_type::SquareHoleAgentSessionRow;
|
||||||
|
pub use square_hole_agent_session_snapshot_type::SquareHoleAgentSessionSnapshot;
|
||||||
pub use square_hole_agent_session_table::*;
|
pub use square_hole_agent_session_table::*;
|
||||||
|
pub use square_hole_creator_config_snapshot_type::SquareHoleCreatorConfigSnapshot;
|
||||||
pub use square_hole_draft_compile_input_type::SquareHoleDraftCompileInput;
|
pub use square_hole_draft_compile_input_type::SquareHoleDraftCompileInput;
|
||||||
|
pub use square_hole_draft_snapshot_type::SquareHoleDraftSnapshot;
|
||||||
|
pub use square_hole_drop_feedback_snapshot_type::SquareHoleDropFeedbackSnapshot;
|
||||||
pub use square_hole_drop_shape_procedure_result_type::SquareHoleDropShapeProcedureResult;
|
pub use square_hole_drop_shape_procedure_result_type::SquareHoleDropShapeProcedureResult;
|
||||||
|
pub use square_hole_gallery_view_row_type::SquareHoleGalleryViewRow;
|
||||||
|
pub use square_hole_gallery_view_table::*;
|
||||||
|
pub use square_hole_hole_option_snapshot_type::SquareHoleHoleOptionSnapshot;
|
||||||
|
pub use square_hole_hole_snapshot_type::SquareHoleHoleSnapshot;
|
||||||
pub use square_hole_run_drop_input_type::SquareHoleRunDropInput;
|
pub use square_hole_run_drop_input_type::SquareHoleRunDropInput;
|
||||||
pub use square_hole_run_get_input_type::SquareHoleRunGetInput;
|
pub use square_hole_run_get_input_type::SquareHoleRunGetInput;
|
||||||
pub use square_hole_run_procedure_result_type::SquareHoleRunProcedureResult;
|
pub use square_hole_run_procedure_result_type::SquareHoleRunProcedureResult;
|
||||||
pub use square_hole_run_restart_input_type::SquareHoleRunRestartInput;
|
pub use square_hole_run_restart_input_type::SquareHoleRunRestartInput;
|
||||||
|
pub use square_hole_run_snapshot_type::SquareHoleRunSnapshot;
|
||||||
pub use square_hole_run_start_input_type::SquareHoleRunStartInput;
|
pub use square_hole_run_start_input_type::SquareHoleRunStartInput;
|
||||||
pub use square_hole_run_stop_input_type::SquareHoleRunStopInput;
|
pub use square_hole_run_stop_input_type::SquareHoleRunStopInput;
|
||||||
pub use square_hole_run_time_up_input_type::SquareHoleRunTimeUpInput;
|
pub use square_hole_run_time_up_input_type::SquareHoleRunTimeUpInput;
|
||||||
pub use square_hole_runtime_run_row_type::SquareHoleRuntimeRunRow;
|
pub use square_hole_runtime_run_row_type::SquareHoleRuntimeRunRow;
|
||||||
pub use square_hole_runtime_run_table::*;
|
pub use square_hole_runtime_run_table::*;
|
||||||
|
pub use square_hole_shape_option_snapshot_type::SquareHoleShapeOptionSnapshot;
|
||||||
|
pub use square_hole_shape_snapshot_type::SquareHoleShapeSnapshot;
|
||||||
pub use square_hole_work_delete_input_type::SquareHoleWorkDeleteInput;
|
pub use square_hole_work_delete_input_type::SquareHoleWorkDeleteInput;
|
||||||
pub use square_hole_work_get_input_type::SquareHoleWorkGetInput;
|
pub use square_hole_work_get_input_type::SquareHoleWorkGetInput;
|
||||||
pub use square_hole_work_procedure_result_type::SquareHoleWorkProcedureResult;
|
pub use square_hole_work_procedure_result_type::SquareHoleWorkProcedureResult;
|
||||||
pub use square_hole_work_profile_row_type::SquareHoleWorkProfileRow;
|
pub use square_hole_work_profile_row_type::SquareHoleWorkProfileRow;
|
||||||
pub use square_hole_work_profile_table::*;
|
pub use square_hole_work_profile_table::*;
|
||||||
pub use square_hole_work_publish_input_type::SquareHoleWorkPublishInput;
|
pub use square_hole_work_publish_input_type::SquareHoleWorkPublishInput;
|
||||||
|
pub use square_hole_work_snapshot_type::SquareHoleWorkSnapshot;
|
||||||
pub use square_hole_work_update_input_type::SquareHoleWorkUpdateInput;
|
pub use square_hole_work_update_input_type::SquareHoleWorkUpdateInput;
|
||||||
pub use square_hole_works_list_input_type::SquareHoleWorksListInput;
|
pub use square_hole_works_list_input_type::SquareHoleWorksListInput;
|
||||||
pub use square_hole_works_procedure_result_type::SquareHoleWorksProcedureResult;
|
pub use square_hole_works_procedure_result_type::SquareHoleWorksProcedureResult;
|
||||||
@@ -1685,24 +1811,33 @@ pub use user_browse_history_table::*;
|
|||||||
pub use user_browse_history_type::UserBrowseHistory;
|
pub use user_browse_history_type::UserBrowseHistory;
|
||||||
pub use visual_novel_agent_message_finalize_input_type::VisualNovelAgentMessageFinalizeInput;
|
pub use visual_novel_agent_message_finalize_input_type::VisualNovelAgentMessageFinalizeInput;
|
||||||
pub use visual_novel_agent_message_row_type::VisualNovelAgentMessageRow;
|
pub use visual_novel_agent_message_row_type::VisualNovelAgentMessageRow;
|
||||||
|
pub use visual_novel_agent_message_snapshot_type::VisualNovelAgentMessageSnapshot;
|
||||||
pub use visual_novel_agent_message_submit_input_type::VisualNovelAgentMessageSubmitInput;
|
pub use visual_novel_agent_message_submit_input_type::VisualNovelAgentMessageSubmitInput;
|
||||||
pub use visual_novel_agent_message_table::*;
|
pub use visual_novel_agent_message_table::*;
|
||||||
pub use visual_novel_agent_session_create_input_type::VisualNovelAgentSessionCreateInput;
|
pub use visual_novel_agent_session_create_input_type::VisualNovelAgentSessionCreateInput;
|
||||||
pub use visual_novel_agent_session_get_input_type::VisualNovelAgentSessionGetInput;
|
pub use visual_novel_agent_session_get_input_type::VisualNovelAgentSessionGetInput;
|
||||||
pub use visual_novel_agent_session_procedure_result_type::VisualNovelAgentSessionProcedureResult;
|
pub use visual_novel_agent_session_procedure_result_type::VisualNovelAgentSessionProcedureResult;
|
||||||
pub use visual_novel_agent_session_row_type::VisualNovelAgentSessionRow;
|
pub use visual_novel_agent_session_row_type::VisualNovelAgentSessionRow;
|
||||||
|
pub use visual_novel_agent_session_snapshot_type::VisualNovelAgentSessionSnapshot;
|
||||||
pub use visual_novel_agent_session_table::*;
|
pub use visual_novel_agent_session_table::*;
|
||||||
|
pub use visual_novel_gallery_view_row_type::VisualNovelGalleryViewRow;
|
||||||
|
pub use visual_novel_gallery_view_table::*;
|
||||||
pub use visual_novel_history_procedure_result_type::VisualNovelHistoryProcedureResult;
|
pub use visual_novel_history_procedure_result_type::VisualNovelHistoryProcedureResult;
|
||||||
|
pub use visual_novel_json_field_type::VisualNovelJsonField;
|
||||||
|
pub use visual_novel_json_value_type::VisualNovelJsonValue;
|
||||||
pub use visual_novel_run_get_input_type::VisualNovelRunGetInput;
|
pub use visual_novel_run_get_input_type::VisualNovelRunGetInput;
|
||||||
pub use visual_novel_run_procedure_result_type::VisualNovelRunProcedureResult;
|
pub use visual_novel_run_procedure_result_type::VisualNovelRunProcedureResult;
|
||||||
|
pub use visual_novel_run_snapshot_type::VisualNovelRunSnapshot;
|
||||||
pub use visual_novel_run_snapshot_upsert_input_type::VisualNovelRunSnapshotUpsertInput;
|
pub use visual_novel_run_snapshot_upsert_input_type::VisualNovelRunSnapshotUpsertInput;
|
||||||
pub use visual_novel_run_start_input_type::VisualNovelRunStartInput;
|
pub use visual_novel_run_start_input_type::VisualNovelRunStartInput;
|
||||||
pub use visual_novel_runtime_event_procedure_result_type::VisualNovelRuntimeEventProcedureResult;
|
pub use visual_novel_runtime_event_procedure_result_type::VisualNovelRuntimeEventProcedureResult;
|
||||||
pub use visual_novel_runtime_event_record_input_type::VisualNovelRuntimeEventRecordInput;
|
pub use visual_novel_runtime_event_record_input_type::VisualNovelRuntimeEventRecordInput;
|
||||||
|
pub use visual_novel_runtime_event_snapshot_type::VisualNovelRuntimeEventSnapshot;
|
||||||
pub use visual_novel_runtime_event_table::*;
|
pub use visual_novel_runtime_event_table::*;
|
||||||
pub use visual_novel_runtime_event_type::VisualNovelRuntimeEvent;
|
pub use visual_novel_runtime_event_type::VisualNovelRuntimeEvent;
|
||||||
pub use visual_novel_runtime_history_append_input_type::VisualNovelRuntimeHistoryAppendInput;
|
pub use visual_novel_runtime_history_append_input_type::VisualNovelRuntimeHistoryAppendInput;
|
||||||
pub use visual_novel_runtime_history_entry_row_type::VisualNovelRuntimeHistoryEntryRow;
|
pub use visual_novel_runtime_history_entry_row_type::VisualNovelRuntimeHistoryEntryRow;
|
||||||
|
pub use visual_novel_runtime_history_entry_snapshot_type::VisualNovelRuntimeHistoryEntrySnapshot;
|
||||||
pub use visual_novel_runtime_history_entry_table::*;
|
pub use visual_novel_runtime_history_entry_table::*;
|
||||||
pub use visual_novel_runtime_history_list_input_type::VisualNovelRuntimeHistoryListInput;
|
pub use visual_novel_runtime_history_list_input_type::VisualNovelRuntimeHistoryListInput;
|
||||||
pub use visual_novel_runtime_run_row_type::VisualNovelRuntimeRunRow;
|
pub use visual_novel_runtime_run_row_type::VisualNovelRuntimeRunRow;
|
||||||
@@ -1714,6 +1849,7 @@ pub use visual_novel_work_procedure_result_type::VisualNovelWorkProcedureResult;
|
|||||||
pub use visual_novel_work_profile_row_type::VisualNovelWorkProfileRow;
|
pub use visual_novel_work_profile_row_type::VisualNovelWorkProfileRow;
|
||||||
pub use visual_novel_work_profile_table::*;
|
pub use visual_novel_work_profile_table::*;
|
||||||
pub use visual_novel_work_publish_input_type::VisualNovelWorkPublishInput;
|
pub use visual_novel_work_publish_input_type::VisualNovelWorkPublishInput;
|
||||||
|
pub use visual_novel_work_snapshot_type::VisualNovelWorkSnapshot;
|
||||||
pub use visual_novel_work_update_input_type::VisualNovelWorkUpdateInput;
|
pub use visual_novel_work_update_input_type::VisualNovelWorkUpdateInput;
|
||||||
pub use visual_novel_works_list_input_type::VisualNovelWorksListInput;
|
pub use visual_novel_works_list_input_type::VisualNovelWorksListInput;
|
||||||
pub use visual_novel_works_procedure_result_type::VisualNovelWorksProcedureResult;
|
pub use visual_novel_works_procedure_result_type::VisualNovelWorksProcedureResult;
|
||||||
@@ -2014,6 +2150,7 @@ pub struct DbUpdate {
|
|||||||
big_fish_asset_slot: __sdk::TableUpdate<BigFishAssetSlot>,
|
big_fish_asset_slot: __sdk::TableUpdate<BigFishAssetSlot>,
|
||||||
big_fish_creation_session: __sdk::TableUpdate<BigFishCreationSession>,
|
big_fish_creation_session: __sdk::TableUpdate<BigFishCreationSession>,
|
||||||
big_fish_event: __sdk::TableUpdate<BigFishEvent>,
|
big_fish_event: __sdk::TableUpdate<BigFishEvent>,
|
||||||
|
big_fish_gallery_view: __sdk::TableUpdate<BigFishWorkSummarySnapshot>,
|
||||||
big_fish_runtime_run: __sdk::TableUpdate<BigFishRuntimeRun>,
|
big_fish_runtime_run: __sdk::TableUpdate<BigFishRuntimeRun>,
|
||||||
chapter_progression: __sdk::TableUpdate<ChapterProgression>,
|
chapter_progression: __sdk::TableUpdate<ChapterProgression>,
|
||||||
creation_entry_config: __sdk::TableUpdate<CreationEntryConfig>,
|
creation_entry_config: __sdk::TableUpdate<CreationEntryConfig>,
|
||||||
@@ -2030,6 +2167,7 @@ pub struct DbUpdate {
|
|||||||
inventory_slot: __sdk::TableUpdate<InventorySlot>,
|
inventory_slot: __sdk::TableUpdate<InventorySlot>,
|
||||||
match_3_d_agent_message: __sdk::TableUpdate<Match3DAgentMessageRow>,
|
match_3_d_agent_message: __sdk::TableUpdate<Match3DAgentMessageRow>,
|
||||||
match_3_d_agent_session: __sdk::TableUpdate<Match3DAgentSessionRow>,
|
match_3_d_agent_session: __sdk::TableUpdate<Match3DAgentSessionRow>,
|
||||||
|
match_3_d_gallery_view: __sdk::TableUpdate<Match3DGalleryViewRow>,
|
||||||
match_3_d_runtime_run: __sdk::TableUpdate<Match3DRuntimeRunRow>,
|
match_3_d_runtime_run: __sdk::TableUpdate<Match3DRuntimeRunRow>,
|
||||||
match_3_d_work_profile: __sdk::TableUpdate<Match3DWorkProfileRow>,
|
match_3_d_work_profile: __sdk::TableUpdate<Match3DWorkProfileRow>,
|
||||||
npc_state: __sdk::TableUpdate<NpcState>,
|
npc_state: __sdk::TableUpdate<NpcState>,
|
||||||
@@ -2054,7 +2192,8 @@ pub struct DbUpdate {
|
|||||||
puzzle_agent_message: __sdk::TableUpdate<PuzzleAgentMessageRow>,
|
puzzle_agent_message: __sdk::TableUpdate<PuzzleAgentMessageRow>,
|
||||||
puzzle_agent_session: __sdk::TableUpdate<PuzzleAgentSessionRow>,
|
puzzle_agent_session: __sdk::TableUpdate<PuzzleAgentSessionRow>,
|
||||||
puzzle_event: __sdk::TableUpdate<PuzzleEvent>,
|
puzzle_event: __sdk::TableUpdate<PuzzleEvent>,
|
||||||
puzzle_gallery_view: __sdk::TableUpdate<PuzzleGalleryViewRow>,
|
puzzle_gallery_card_view: __sdk::TableUpdate<PuzzleGalleryCardViewRow>,
|
||||||
|
puzzle_gallery_view: __sdk::TableUpdate<PuzzleWorkProfile>,
|
||||||
puzzle_leaderboard_entry: __sdk::TableUpdate<PuzzleLeaderboardEntryRow>,
|
puzzle_leaderboard_entry: __sdk::TableUpdate<PuzzleLeaderboardEntryRow>,
|
||||||
puzzle_runtime_run: __sdk::TableUpdate<PuzzleRuntimeRunRow>,
|
puzzle_runtime_run: __sdk::TableUpdate<PuzzleRuntimeRunRow>,
|
||||||
puzzle_work_profile: __sdk::TableUpdate<PuzzleWorkProfileRow>,
|
puzzle_work_profile: __sdk::TableUpdate<PuzzleWorkProfileRow>,
|
||||||
@@ -2065,6 +2204,7 @@ pub struct DbUpdate {
|
|||||||
runtime_snapshot: __sdk::TableUpdate<RuntimeSnapshotRow>,
|
runtime_snapshot: __sdk::TableUpdate<RuntimeSnapshotRow>,
|
||||||
square_hole_agent_message: __sdk::TableUpdate<SquareHoleAgentMessageRow>,
|
square_hole_agent_message: __sdk::TableUpdate<SquareHoleAgentMessageRow>,
|
||||||
square_hole_agent_session: __sdk::TableUpdate<SquareHoleAgentSessionRow>,
|
square_hole_agent_session: __sdk::TableUpdate<SquareHoleAgentSessionRow>,
|
||||||
|
square_hole_gallery_view: __sdk::TableUpdate<SquareHoleGalleryViewRow>,
|
||||||
square_hole_runtime_run: __sdk::TableUpdate<SquareHoleRuntimeRunRow>,
|
square_hole_runtime_run: __sdk::TableUpdate<SquareHoleRuntimeRunRow>,
|
||||||
square_hole_work_profile: __sdk::TableUpdate<SquareHoleWorkProfileRow>,
|
square_hole_work_profile: __sdk::TableUpdate<SquareHoleWorkProfileRow>,
|
||||||
story_event: __sdk::TableUpdate<StoryEvent>,
|
story_event: __sdk::TableUpdate<StoryEvent>,
|
||||||
@@ -2076,6 +2216,7 @@ pub struct DbUpdate {
|
|||||||
user_browse_history: __sdk::TableUpdate<UserBrowseHistory>,
|
user_browse_history: __sdk::TableUpdate<UserBrowseHistory>,
|
||||||
visual_novel_agent_message: __sdk::TableUpdate<VisualNovelAgentMessageRow>,
|
visual_novel_agent_message: __sdk::TableUpdate<VisualNovelAgentMessageRow>,
|
||||||
visual_novel_agent_session: __sdk::TableUpdate<VisualNovelAgentSessionRow>,
|
visual_novel_agent_session: __sdk::TableUpdate<VisualNovelAgentSessionRow>,
|
||||||
|
visual_novel_gallery_view: __sdk::TableUpdate<VisualNovelGalleryViewRow>,
|
||||||
visual_novel_runtime_event: __sdk::TableUpdate<VisualNovelRuntimeEvent>,
|
visual_novel_runtime_event: __sdk::TableUpdate<VisualNovelRuntimeEvent>,
|
||||||
visual_novel_runtime_history_entry: __sdk::TableUpdate<VisualNovelRuntimeHistoryEntryRow>,
|
visual_novel_runtime_history_entry: __sdk::TableUpdate<VisualNovelRuntimeHistoryEntryRow>,
|
||||||
visual_novel_runtime_run: __sdk::TableUpdate<VisualNovelRuntimeRunRow>,
|
visual_novel_runtime_run: __sdk::TableUpdate<VisualNovelRuntimeRunRow>,
|
||||||
@@ -2166,6 +2307,9 @@ impl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate {
|
|||||||
"big_fish_event" => db_update
|
"big_fish_event" => db_update
|
||||||
.big_fish_event
|
.big_fish_event
|
||||||
.append(big_fish_event_table::parse_table_update(table_update)?),
|
.append(big_fish_event_table::parse_table_update(table_update)?),
|
||||||
|
"big_fish_gallery_view" => db_update.big_fish_gallery_view.append(
|
||||||
|
big_fish_gallery_view_table::parse_table_update(table_update)?,
|
||||||
|
),
|
||||||
"big_fish_runtime_run" => db_update.big_fish_runtime_run.append(
|
"big_fish_runtime_run" => db_update.big_fish_runtime_run.append(
|
||||||
big_fish_runtime_run_table::parse_table_update(table_update)?,
|
big_fish_runtime_run_table::parse_table_update(table_update)?,
|
||||||
),
|
),
|
||||||
@@ -2216,6 +2360,9 @@ impl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate {
|
|||||||
"match_3_d_agent_session" => db_update.match_3_d_agent_session.append(
|
"match_3_d_agent_session" => db_update.match_3_d_agent_session.append(
|
||||||
match_3_d_agent_session_table::parse_table_update(table_update)?,
|
match_3_d_agent_session_table::parse_table_update(table_update)?,
|
||||||
),
|
),
|
||||||
|
"match_3_d_gallery_view" => db_update.match_3_d_gallery_view.append(
|
||||||
|
match_3_d_gallery_view_table::parse_table_update(table_update)?,
|
||||||
|
),
|
||||||
"match_3_d_runtime_run" => db_update.match_3_d_runtime_run.append(
|
"match_3_d_runtime_run" => db_update.match_3_d_runtime_run.append(
|
||||||
match_3_d_runtime_run_table::parse_table_update(table_update)?,
|
match_3_d_runtime_run_table::parse_table_update(table_update)?,
|
||||||
),
|
),
|
||||||
@@ -2290,6 +2437,9 @@ impl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate {
|
|||||||
"puzzle_event" => db_update
|
"puzzle_event" => db_update
|
||||||
.puzzle_event
|
.puzzle_event
|
||||||
.append(puzzle_event_table::parse_table_update(table_update)?),
|
.append(puzzle_event_table::parse_table_update(table_update)?),
|
||||||
|
"puzzle_gallery_card_view" => db_update.puzzle_gallery_card_view.append(
|
||||||
|
puzzle_gallery_card_view_table::parse_table_update(table_update)?,
|
||||||
|
),
|
||||||
"puzzle_gallery_view" => db_update
|
"puzzle_gallery_view" => db_update
|
||||||
.puzzle_gallery_view
|
.puzzle_gallery_view
|
||||||
.append(puzzle_gallery_view_table::parse_table_update(table_update)?),
|
.append(puzzle_gallery_view_table::parse_table_update(table_update)?),
|
||||||
@@ -2323,6 +2473,9 @@ impl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate {
|
|||||||
"square_hole_agent_session" => db_update.square_hole_agent_session.append(
|
"square_hole_agent_session" => db_update.square_hole_agent_session.append(
|
||||||
square_hole_agent_session_table::parse_table_update(table_update)?,
|
square_hole_agent_session_table::parse_table_update(table_update)?,
|
||||||
),
|
),
|
||||||
|
"square_hole_gallery_view" => db_update.square_hole_gallery_view.append(
|
||||||
|
square_hole_gallery_view_table::parse_table_update(table_update)?,
|
||||||
|
),
|
||||||
"square_hole_runtime_run" => db_update.square_hole_runtime_run.append(
|
"square_hole_runtime_run" => db_update.square_hole_runtime_run.append(
|
||||||
square_hole_runtime_run_table::parse_table_update(table_update)?,
|
square_hole_runtime_run_table::parse_table_update(table_update)?,
|
||||||
),
|
),
|
||||||
@@ -2356,6 +2509,9 @@ impl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate {
|
|||||||
"visual_novel_agent_session" => db_update.visual_novel_agent_session.append(
|
"visual_novel_agent_session" => db_update.visual_novel_agent_session.append(
|
||||||
visual_novel_agent_session_table::parse_table_update(table_update)?,
|
visual_novel_agent_session_table::parse_table_update(table_update)?,
|
||||||
),
|
),
|
||||||
|
"visual_novel_gallery_view" => db_update.visual_novel_gallery_view.append(
|
||||||
|
visual_novel_gallery_view_table::parse_table_update(table_update)?,
|
||||||
|
),
|
||||||
"visual_novel_runtime_event" => db_update.visual_novel_runtime_event.append(
|
"visual_novel_runtime_event" => db_update.visual_novel_runtime_event.append(
|
||||||
visual_novel_runtime_event_table::parse_table_update(table_update)?,
|
visual_novel_runtime_event_table::parse_table_update(table_update)?,
|
||||||
),
|
),
|
||||||
@@ -2848,10 +3004,30 @@ impl __sdk::DbUpdate for DbUpdate {
|
|||||||
&self.visual_novel_work_profile,
|
&self.visual_novel_work_profile,
|
||||||
)
|
)
|
||||||
.with_updates_by_pk(|row| &row.profile_id);
|
.with_updates_by_pk(|row| &row.profile_id);
|
||||||
diff.puzzle_gallery_view = cache.apply_diff_to_table::<PuzzleGalleryViewRow>(
|
diff.big_fish_gallery_view = cache.apply_diff_to_table::<BigFishWorkSummarySnapshot>(
|
||||||
|
"big_fish_gallery_view",
|
||||||
|
&self.big_fish_gallery_view,
|
||||||
|
);
|
||||||
|
diff.match_3_d_gallery_view = cache.apply_diff_to_table::<Match3DGalleryViewRow>(
|
||||||
|
"match_3_d_gallery_view",
|
||||||
|
&self.match_3_d_gallery_view,
|
||||||
|
);
|
||||||
|
diff.puzzle_gallery_card_view = cache.apply_diff_to_table::<PuzzleGalleryCardViewRow>(
|
||||||
|
"puzzle_gallery_card_view",
|
||||||
|
&self.puzzle_gallery_card_view,
|
||||||
|
);
|
||||||
|
diff.puzzle_gallery_view = cache.apply_diff_to_table::<PuzzleWorkProfile>(
|
||||||
"puzzle_gallery_view",
|
"puzzle_gallery_view",
|
||||||
&self.puzzle_gallery_view,
|
&self.puzzle_gallery_view,
|
||||||
);
|
);
|
||||||
|
diff.square_hole_gallery_view = cache.apply_diff_to_table::<SquareHoleGalleryViewRow>(
|
||||||
|
"square_hole_gallery_view",
|
||||||
|
&self.square_hole_gallery_view,
|
||||||
|
);
|
||||||
|
diff.visual_novel_gallery_view = cache.apply_diff_to_table::<VisualNovelGalleryViewRow>(
|
||||||
|
"visual_novel_gallery_view",
|
||||||
|
&self.visual_novel_gallery_view,
|
||||||
|
);
|
||||||
|
|
||||||
diff
|
diff
|
||||||
}
|
}
|
||||||
@@ -2931,6 +3107,9 @@ impl __sdk::DbUpdate for DbUpdate {
|
|||||||
"big_fish_event" => db_update
|
"big_fish_event" => db_update
|
||||||
.big_fish_event
|
.big_fish_event
|
||||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||||
|
"big_fish_gallery_view" => db_update
|
||||||
|
.big_fish_gallery_view
|
||||||
|
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||||
"big_fish_runtime_run" => db_update
|
"big_fish_runtime_run" => db_update
|
||||||
.big_fish_runtime_run
|
.big_fish_runtime_run
|
||||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||||
@@ -2979,6 +3158,9 @@ impl __sdk::DbUpdate for DbUpdate {
|
|||||||
"match_3_d_agent_session" => db_update
|
"match_3_d_agent_session" => db_update
|
||||||
.match_3_d_agent_session
|
.match_3_d_agent_session
|
||||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||||
|
"match_3_d_gallery_view" => db_update
|
||||||
|
.match_3_d_gallery_view
|
||||||
|
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||||
"match_3_d_runtime_run" => db_update
|
"match_3_d_runtime_run" => db_update
|
||||||
.match_3_d_runtime_run
|
.match_3_d_runtime_run
|
||||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||||
@@ -3051,6 +3233,9 @@ impl __sdk::DbUpdate for DbUpdate {
|
|||||||
"puzzle_event" => db_update
|
"puzzle_event" => db_update
|
||||||
.puzzle_event
|
.puzzle_event
|
||||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||||
|
"puzzle_gallery_card_view" => db_update
|
||||||
|
.puzzle_gallery_card_view
|
||||||
|
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||||
"puzzle_gallery_view" => db_update
|
"puzzle_gallery_view" => db_update
|
||||||
.puzzle_gallery_view
|
.puzzle_gallery_view
|
||||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||||
@@ -3084,6 +3269,9 @@ impl __sdk::DbUpdate for DbUpdate {
|
|||||||
"square_hole_agent_session" => db_update
|
"square_hole_agent_session" => db_update
|
||||||
.square_hole_agent_session
|
.square_hole_agent_session
|
||||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||||
|
"square_hole_gallery_view" => db_update
|
||||||
|
.square_hole_gallery_view
|
||||||
|
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||||
"square_hole_runtime_run" => db_update
|
"square_hole_runtime_run" => db_update
|
||||||
.square_hole_runtime_run
|
.square_hole_runtime_run
|
||||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||||
@@ -3117,6 +3305,9 @@ impl __sdk::DbUpdate for DbUpdate {
|
|||||||
"visual_novel_agent_session" => db_update
|
"visual_novel_agent_session" => db_update
|
||||||
.visual_novel_agent_session
|
.visual_novel_agent_session
|
||||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||||
|
"visual_novel_gallery_view" => db_update
|
||||||
|
.visual_novel_gallery_view
|
||||||
|
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||||
"visual_novel_runtime_event" => db_update
|
"visual_novel_runtime_event" => db_update
|
||||||
.visual_novel_runtime_event
|
.visual_novel_runtime_event
|
||||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||||
@@ -3214,6 +3405,9 @@ impl __sdk::DbUpdate for DbUpdate {
|
|||||||
"big_fish_event" => db_update
|
"big_fish_event" => db_update
|
||||||
.big_fish_event
|
.big_fish_event
|
||||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||||
|
"big_fish_gallery_view" => db_update
|
||||||
|
.big_fish_gallery_view
|
||||||
|
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||||
"big_fish_runtime_run" => db_update
|
"big_fish_runtime_run" => db_update
|
||||||
.big_fish_runtime_run
|
.big_fish_runtime_run
|
||||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||||
@@ -3262,6 +3456,9 @@ impl __sdk::DbUpdate for DbUpdate {
|
|||||||
"match_3_d_agent_session" => db_update
|
"match_3_d_agent_session" => db_update
|
||||||
.match_3_d_agent_session
|
.match_3_d_agent_session
|
||||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||||
|
"match_3_d_gallery_view" => db_update
|
||||||
|
.match_3_d_gallery_view
|
||||||
|
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||||
"match_3_d_runtime_run" => db_update
|
"match_3_d_runtime_run" => db_update
|
||||||
.match_3_d_runtime_run
|
.match_3_d_runtime_run
|
||||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||||
@@ -3334,6 +3531,9 @@ impl __sdk::DbUpdate for DbUpdate {
|
|||||||
"puzzle_event" => db_update
|
"puzzle_event" => db_update
|
||||||
.puzzle_event
|
.puzzle_event
|
||||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||||
|
"puzzle_gallery_card_view" => db_update
|
||||||
|
.puzzle_gallery_card_view
|
||||||
|
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||||
"puzzle_gallery_view" => db_update
|
"puzzle_gallery_view" => db_update
|
||||||
.puzzle_gallery_view
|
.puzzle_gallery_view
|
||||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||||
@@ -3367,6 +3567,9 @@ impl __sdk::DbUpdate for DbUpdate {
|
|||||||
"square_hole_agent_session" => db_update
|
"square_hole_agent_session" => db_update
|
||||||
.square_hole_agent_session
|
.square_hole_agent_session
|
||||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||||
|
"square_hole_gallery_view" => db_update
|
||||||
|
.square_hole_gallery_view
|
||||||
|
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||||
"square_hole_runtime_run" => db_update
|
"square_hole_runtime_run" => db_update
|
||||||
.square_hole_runtime_run
|
.square_hole_runtime_run
|
||||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||||
@@ -3400,6 +3603,9 @@ impl __sdk::DbUpdate for DbUpdate {
|
|||||||
"visual_novel_agent_session" => db_update
|
"visual_novel_agent_session" => db_update
|
||||||
.visual_novel_agent_session
|
.visual_novel_agent_session
|
||||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||||
|
"visual_novel_gallery_view" => db_update
|
||||||
|
.visual_novel_gallery_view
|
||||||
|
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||||
"visual_novel_runtime_event" => db_update
|
"visual_novel_runtime_event" => db_update
|
||||||
.visual_novel_runtime_event
|
.visual_novel_runtime_event
|
||||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||||
@@ -3453,6 +3659,7 @@ pub struct AppliedDiff<'r> {
|
|||||||
big_fish_asset_slot: __sdk::TableAppliedDiff<'r, BigFishAssetSlot>,
|
big_fish_asset_slot: __sdk::TableAppliedDiff<'r, BigFishAssetSlot>,
|
||||||
big_fish_creation_session: __sdk::TableAppliedDiff<'r, BigFishCreationSession>,
|
big_fish_creation_session: __sdk::TableAppliedDiff<'r, BigFishCreationSession>,
|
||||||
big_fish_event: __sdk::TableAppliedDiff<'r, BigFishEvent>,
|
big_fish_event: __sdk::TableAppliedDiff<'r, BigFishEvent>,
|
||||||
|
big_fish_gallery_view: __sdk::TableAppliedDiff<'r, BigFishWorkSummarySnapshot>,
|
||||||
big_fish_runtime_run: __sdk::TableAppliedDiff<'r, BigFishRuntimeRun>,
|
big_fish_runtime_run: __sdk::TableAppliedDiff<'r, BigFishRuntimeRun>,
|
||||||
chapter_progression: __sdk::TableAppliedDiff<'r, ChapterProgression>,
|
chapter_progression: __sdk::TableAppliedDiff<'r, ChapterProgression>,
|
||||||
creation_entry_config: __sdk::TableAppliedDiff<'r, CreationEntryConfig>,
|
creation_entry_config: __sdk::TableAppliedDiff<'r, CreationEntryConfig>,
|
||||||
@@ -3469,6 +3676,7 @@ pub struct AppliedDiff<'r> {
|
|||||||
inventory_slot: __sdk::TableAppliedDiff<'r, InventorySlot>,
|
inventory_slot: __sdk::TableAppliedDiff<'r, InventorySlot>,
|
||||||
match_3_d_agent_message: __sdk::TableAppliedDiff<'r, Match3DAgentMessageRow>,
|
match_3_d_agent_message: __sdk::TableAppliedDiff<'r, Match3DAgentMessageRow>,
|
||||||
match_3_d_agent_session: __sdk::TableAppliedDiff<'r, Match3DAgentSessionRow>,
|
match_3_d_agent_session: __sdk::TableAppliedDiff<'r, Match3DAgentSessionRow>,
|
||||||
|
match_3_d_gallery_view: __sdk::TableAppliedDiff<'r, Match3DGalleryViewRow>,
|
||||||
match_3_d_runtime_run: __sdk::TableAppliedDiff<'r, Match3DRuntimeRunRow>,
|
match_3_d_runtime_run: __sdk::TableAppliedDiff<'r, Match3DRuntimeRunRow>,
|
||||||
match_3_d_work_profile: __sdk::TableAppliedDiff<'r, Match3DWorkProfileRow>,
|
match_3_d_work_profile: __sdk::TableAppliedDiff<'r, Match3DWorkProfileRow>,
|
||||||
npc_state: __sdk::TableAppliedDiff<'r, NpcState>,
|
npc_state: __sdk::TableAppliedDiff<'r, NpcState>,
|
||||||
@@ -3493,7 +3701,8 @@ pub struct AppliedDiff<'r> {
|
|||||||
puzzle_agent_message: __sdk::TableAppliedDiff<'r, PuzzleAgentMessageRow>,
|
puzzle_agent_message: __sdk::TableAppliedDiff<'r, PuzzleAgentMessageRow>,
|
||||||
puzzle_agent_session: __sdk::TableAppliedDiff<'r, PuzzleAgentSessionRow>,
|
puzzle_agent_session: __sdk::TableAppliedDiff<'r, PuzzleAgentSessionRow>,
|
||||||
puzzle_event: __sdk::TableAppliedDiff<'r, PuzzleEvent>,
|
puzzle_event: __sdk::TableAppliedDiff<'r, PuzzleEvent>,
|
||||||
puzzle_gallery_view: __sdk::TableAppliedDiff<'r, PuzzleGalleryViewRow>,
|
puzzle_gallery_card_view: __sdk::TableAppliedDiff<'r, PuzzleGalleryCardViewRow>,
|
||||||
|
puzzle_gallery_view: __sdk::TableAppliedDiff<'r, PuzzleWorkProfile>,
|
||||||
puzzle_leaderboard_entry: __sdk::TableAppliedDiff<'r, PuzzleLeaderboardEntryRow>,
|
puzzle_leaderboard_entry: __sdk::TableAppliedDiff<'r, PuzzleLeaderboardEntryRow>,
|
||||||
puzzle_runtime_run: __sdk::TableAppliedDiff<'r, PuzzleRuntimeRunRow>,
|
puzzle_runtime_run: __sdk::TableAppliedDiff<'r, PuzzleRuntimeRunRow>,
|
||||||
puzzle_work_profile: __sdk::TableAppliedDiff<'r, PuzzleWorkProfileRow>,
|
puzzle_work_profile: __sdk::TableAppliedDiff<'r, PuzzleWorkProfileRow>,
|
||||||
@@ -3504,6 +3713,7 @@ pub struct AppliedDiff<'r> {
|
|||||||
runtime_snapshot: __sdk::TableAppliedDiff<'r, RuntimeSnapshotRow>,
|
runtime_snapshot: __sdk::TableAppliedDiff<'r, RuntimeSnapshotRow>,
|
||||||
square_hole_agent_message: __sdk::TableAppliedDiff<'r, SquareHoleAgentMessageRow>,
|
square_hole_agent_message: __sdk::TableAppliedDiff<'r, SquareHoleAgentMessageRow>,
|
||||||
square_hole_agent_session: __sdk::TableAppliedDiff<'r, SquareHoleAgentSessionRow>,
|
square_hole_agent_session: __sdk::TableAppliedDiff<'r, SquareHoleAgentSessionRow>,
|
||||||
|
square_hole_gallery_view: __sdk::TableAppliedDiff<'r, SquareHoleGalleryViewRow>,
|
||||||
square_hole_runtime_run: __sdk::TableAppliedDiff<'r, SquareHoleRuntimeRunRow>,
|
square_hole_runtime_run: __sdk::TableAppliedDiff<'r, SquareHoleRuntimeRunRow>,
|
||||||
square_hole_work_profile: __sdk::TableAppliedDiff<'r, SquareHoleWorkProfileRow>,
|
square_hole_work_profile: __sdk::TableAppliedDiff<'r, SquareHoleWorkProfileRow>,
|
||||||
story_event: __sdk::TableAppliedDiff<'r, StoryEvent>,
|
story_event: __sdk::TableAppliedDiff<'r, StoryEvent>,
|
||||||
@@ -3515,6 +3725,7 @@ pub struct AppliedDiff<'r> {
|
|||||||
user_browse_history: __sdk::TableAppliedDiff<'r, UserBrowseHistory>,
|
user_browse_history: __sdk::TableAppliedDiff<'r, UserBrowseHistory>,
|
||||||
visual_novel_agent_message: __sdk::TableAppliedDiff<'r, VisualNovelAgentMessageRow>,
|
visual_novel_agent_message: __sdk::TableAppliedDiff<'r, VisualNovelAgentMessageRow>,
|
||||||
visual_novel_agent_session: __sdk::TableAppliedDiff<'r, VisualNovelAgentSessionRow>,
|
visual_novel_agent_session: __sdk::TableAppliedDiff<'r, VisualNovelAgentSessionRow>,
|
||||||
|
visual_novel_gallery_view: __sdk::TableAppliedDiff<'r, VisualNovelGalleryViewRow>,
|
||||||
visual_novel_runtime_event: __sdk::TableAppliedDiff<'r, VisualNovelRuntimeEvent>,
|
visual_novel_runtime_event: __sdk::TableAppliedDiff<'r, VisualNovelRuntimeEvent>,
|
||||||
visual_novel_runtime_history_entry:
|
visual_novel_runtime_history_entry:
|
||||||
__sdk::TableAppliedDiff<'r, VisualNovelRuntimeHistoryEntryRow>,
|
__sdk::TableAppliedDiff<'r, VisualNovelRuntimeHistoryEntryRow>,
|
||||||
@@ -3645,6 +3856,11 @@ impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> {
|
|||||||
&self.big_fish_event,
|
&self.big_fish_event,
|
||||||
event,
|
event,
|
||||||
);
|
);
|
||||||
|
callbacks.invoke_table_row_callbacks::<BigFishWorkSummarySnapshot>(
|
||||||
|
"big_fish_gallery_view",
|
||||||
|
&self.big_fish_gallery_view,
|
||||||
|
event,
|
||||||
|
);
|
||||||
callbacks.invoke_table_row_callbacks::<BigFishRuntimeRun>(
|
callbacks.invoke_table_row_callbacks::<BigFishRuntimeRun>(
|
||||||
"big_fish_runtime_run",
|
"big_fish_runtime_run",
|
||||||
&self.big_fish_runtime_run,
|
&self.big_fish_runtime_run,
|
||||||
@@ -3725,6 +3941,11 @@ impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> {
|
|||||||
&self.match_3_d_agent_session,
|
&self.match_3_d_agent_session,
|
||||||
event,
|
event,
|
||||||
);
|
);
|
||||||
|
callbacks.invoke_table_row_callbacks::<Match3DGalleryViewRow>(
|
||||||
|
"match_3_d_gallery_view",
|
||||||
|
&self.match_3_d_gallery_view,
|
||||||
|
event,
|
||||||
|
);
|
||||||
callbacks.invoke_table_row_callbacks::<Match3DRuntimeRunRow>(
|
callbacks.invoke_table_row_callbacks::<Match3DRuntimeRunRow>(
|
||||||
"match_3_d_runtime_run",
|
"match_3_d_runtime_run",
|
||||||
&self.match_3_d_runtime_run,
|
&self.match_3_d_runtime_run,
|
||||||
@@ -3841,7 +4062,12 @@ impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> {
|
|||||||
&self.puzzle_event,
|
&self.puzzle_event,
|
||||||
event,
|
event,
|
||||||
);
|
);
|
||||||
callbacks.invoke_table_row_callbacks::<PuzzleGalleryViewRow>(
|
callbacks.invoke_table_row_callbacks::<PuzzleGalleryCardViewRow>(
|
||||||
|
"puzzle_gallery_card_view",
|
||||||
|
&self.puzzle_gallery_card_view,
|
||||||
|
event,
|
||||||
|
);
|
||||||
|
callbacks.invoke_table_row_callbacks::<PuzzleWorkProfile>(
|
||||||
"puzzle_gallery_view",
|
"puzzle_gallery_view",
|
||||||
&self.puzzle_gallery_view,
|
&self.puzzle_gallery_view,
|
||||||
event,
|
event,
|
||||||
@@ -3892,6 +4118,11 @@ impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> {
|
|||||||
&self.square_hole_agent_session,
|
&self.square_hole_agent_session,
|
||||||
event,
|
event,
|
||||||
);
|
);
|
||||||
|
callbacks.invoke_table_row_callbacks::<SquareHoleGalleryViewRow>(
|
||||||
|
"square_hole_gallery_view",
|
||||||
|
&self.square_hole_gallery_view,
|
||||||
|
event,
|
||||||
|
);
|
||||||
callbacks.invoke_table_row_callbacks::<SquareHoleRuntimeRunRow>(
|
callbacks.invoke_table_row_callbacks::<SquareHoleRuntimeRunRow>(
|
||||||
"square_hole_runtime_run",
|
"square_hole_runtime_run",
|
||||||
&self.square_hole_runtime_run,
|
&self.square_hole_runtime_run,
|
||||||
@@ -3943,6 +4174,11 @@ impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> {
|
|||||||
&self.visual_novel_agent_session,
|
&self.visual_novel_agent_session,
|
||||||
event,
|
event,
|
||||||
);
|
);
|
||||||
|
callbacks.invoke_table_row_callbacks::<VisualNovelGalleryViewRow>(
|
||||||
|
"visual_novel_gallery_view",
|
||||||
|
&self.visual_novel_gallery_view,
|
||||||
|
event,
|
||||||
|
);
|
||||||
callbacks.invoke_table_row_callbacks::<VisualNovelRuntimeEvent>(
|
callbacks.invoke_table_row_callbacks::<VisualNovelRuntimeEvent>(
|
||||||
"visual_novel_runtime_event",
|
"visual_novel_runtime_event",
|
||||||
&self.visual_novel_runtime_event,
|
&self.visual_novel_runtime_event,
|
||||||
@@ -4647,6 +4883,7 @@ impl __sdk::SpacetimeModule for RemoteModule {
|
|||||||
big_fish_asset_slot_table::register_table(client_cache);
|
big_fish_asset_slot_table::register_table(client_cache);
|
||||||
big_fish_creation_session_table::register_table(client_cache);
|
big_fish_creation_session_table::register_table(client_cache);
|
||||||
big_fish_event_table::register_table(client_cache);
|
big_fish_event_table::register_table(client_cache);
|
||||||
|
big_fish_gallery_view_table::register_table(client_cache);
|
||||||
big_fish_runtime_run_table::register_table(client_cache);
|
big_fish_runtime_run_table::register_table(client_cache);
|
||||||
chapter_progression_table::register_table(client_cache);
|
chapter_progression_table::register_table(client_cache);
|
||||||
creation_entry_config_table::register_table(client_cache);
|
creation_entry_config_table::register_table(client_cache);
|
||||||
@@ -4663,6 +4900,7 @@ impl __sdk::SpacetimeModule for RemoteModule {
|
|||||||
inventory_slot_table::register_table(client_cache);
|
inventory_slot_table::register_table(client_cache);
|
||||||
match_3_d_agent_message_table::register_table(client_cache);
|
match_3_d_agent_message_table::register_table(client_cache);
|
||||||
match_3_d_agent_session_table::register_table(client_cache);
|
match_3_d_agent_session_table::register_table(client_cache);
|
||||||
|
match_3_d_gallery_view_table::register_table(client_cache);
|
||||||
match_3_d_runtime_run_table::register_table(client_cache);
|
match_3_d_runtime_run_table::register_table(client_cache);
|
||||||
match_3_d_work_profile_table::register_table(client_cache);
|
match_3_d_work_profile_table::register_table(client_cache);
|
||||||
npc_state_table::register_table(client_cache);
|
npc_state_table::register_table(client_cache);
|
||||||
@@ -4687,6 +4925,7 @@ impl __sdk::SpacetimeModule for RemoteModule {
|
|||||||
puzzle_agent_message_table::register_table(client_cache);
|
puzzle_agent_message_table::register_table(client_cache);
|
||||||
puzzle_agent_session_table::register_table(client_cache);
|
puzzle_agent_session_table::register_table(client_cache);
|
||||||
puzzle_event_table::register_table(client_cache);
|
puzzle_event_table::register_table(client_cache);
|
||||||
|
puzzle_gallery_card_view_table::register_table(client_cache);
|
||||||
puzzle_gallery_view_table::register_table(client_cache);
|
puzzle_gallery_view_table::register_table(client_cache);
|
||||||
puzzle_leaderboard_entry_table::register_table(client_cache);
|
puzzle_leaderboard_entry_table::register_table(client_cache);
|
||||||
puzzle_runtime_run_table::register_table(client_cache);
|
puzzle_runtime_run_table::register_table(client_cache);
|
||||||
@@ -4698,6 +4937,7 @@ impl __sdk::SpacetimeModule for RemoteModule {
|
|||||||
runtime_snapshot_table::register_table(client_cache);
|
runtime_snapshot_table::register_table(client_cache);
|
||||||
square_hole_agent_message_table::register_table(client_cache);
|
square_hole_agent_message_table::register_table(client_cache);
|
||||||
square_hole_agent_session_table::register_table(client_cache);
|
square_hole_agent_session_table::register_table(client_cache);
|
||||||
|
square_hole_gallery_view_table::register_table(client_cache);
|
||||||
square_hole_runtime_run_table::register_table(client_cache);
|
square_hole_runtime_run_table::register_table(client_cache);
|
||||||
square_hole_work_profile_table::register_table(client_cache);
|
square_hole_work_profile_table::register_table(client_cache);
|
||||||
story_event_table::register_table(client_cache);
|
story_event_table::register_table(client_cache);
|
||||||
@@ -4709,6 +4949,7 @@ impl __sdk::SpacetimeModule for RemoteModule {
|
|||||||
user_browse_history_table::register_table(client_cache);
|
user_browse_history_table::register_table(client_cache);
|
||||||
visual_novel_agent_message_table::register_table(client_cache);
|
visual_novel_agent_message_table::register_table(client_cache);
|
||||||
visual_novel_agent_session_table::register_table(client_cache);
|
visual_novel_agent_session_table::register_table(client_cache);
|
||||||
|
visual_novel_gallery_view_table::register_table(client_cache);
|
||||||
visual_novel_runtime_event_table::register_table(client_cache);
|
visual_novel_runtime_event_table::register_table(client_cache);
|
||||||
visual_novel_runtime_history_entry_table::register_table(client_cache);
|
visual_novel_runtime_history_entry_table::register_table(client_cache);
|
||||||
visual_novel_runtime_run_table::register_table(client_cache);
|
visual_novel_runtime_run_table::register_table(client_cache);
|
||||||
@@ -4739,6 +4980,7 @@ impl __sdk::SpacetimeModule for RemoteModule {
|
|||||||
"big_fish_asset_slot",
|
"big_fish_asset_slot",
|
||||||
"big_fish_creation_session",
|
"big_fish_creation_session",
|
||||||
"big_fish_event",
|
"big_fish_event",
|
||||||
|
"big_fish_gallery_view",
|
||||||
"big_fish_runtime_run",
|
"big_fish_runtime_run",
|
||||||
"chapter_progression",
|
"chapter_progression",
|
||||||
"creation_entry_config",
|
"creation_entry_config",
|
||||||
@@ -4755,6 +4997,7 @@ impl __sdk::SpacetimeModule for RemoteModule {
|
|||||||
"inventory_slot",
|
"inventory_slot",
|
||||||
"match_3_d_agent_message",
|
"match_3_d_agent_message",
|
||||||
"match_3_d_agent_session",
|
"match_3_d_agent_session",
|
||||||
|
"match_3_d_gallery_view",
|
||||||
"match_3_d_runtime_run",
|
"match_3_d_runtime_run",
|
||||||
"match_3_d_work_profile",
|
"match_3_d_work_profile",
|
||||||
"npc_state",
|
"npc_state",
|
||||||
@@ -4779,6 +5022,7 @@ impl __sdk::SpacetimeModule for RemoteModule {
|
|||||||
"puzzle_agent_message",
|
"puzzle_agent_message",
|
||||||
"puzzle_agent_session",
|
"puzzle_agent_session",
|
||||||
"puzzle_event",
|
"puzzle_event",
|
||||||
|
"puzzle_gallery_card_view",
|
||||||
"puzzle_gallery_view",
|
"puzzle_gallery_view",
|
||||||
"puzzle_leaderboard_entry",
|
"puzzle_leaderboard_entry",
|
||||||
"puzzle_runtime_run",
|
"puzzle_runtime_run",
|
||||||
@@ -4790,6 +5034,7 @@ impl __sdk::SpacetimeModule for RemoteModule {
|
|||||||
"runtime_snapshot",
|
"runtime_snapshot",
|
||||||
"square_hole_agent_message",
|
"square_hole_agent_message",
|
||||||
"square_hole_agent_session",
|
"square_hole_agent_session",
|
||||||
|
"square_hole_gallery_view",
|
||||||
"square_hole_runtime_run",
|
"square_hole_runtime_run",
|
||||||
"square_hole_work_profile",
|
"square_hole_work_profile",
|
||||||
"story_event",
|
"story_event",
|
||||||
@@ -4801,6 +5046,7 @@ impl __sdk::SpacetimeModule for RemoteModule {
|
|||||||
"user_browse_history",
|
"user_browse_history",
|
||||||
"visual_novel_agent_message",
|
"visual_novel_agent_message",
|
||||||
"visual_novel_agent_session",
|
"visual_novel_agent_session",
|
||||||
|
"visual_novel_gallery_view",
|
||||||
"visual_novel_runtime_event",
|
"visual_novel_runtime_event",
|
||||||
"visual_novel_runtime_history_entry",
|
"visual_novel_runtime_history_entry",
|
||||||
"visual_novel_runtime_run",
|
"visual_novel_runtime_run",
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// 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::puzzle_agent_message_kind_type::PuzzleAgentMessageKind;
|
||||||
|
use super::puzzle_agent_message_role_type::PuzzleAgentMessageRole;
|
||||||
|
|
||||||
|
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||||
|
#[sats(crate = __lib)]
|
||||||
|
pub struct PuzzleAgentMessageSnapshot {
|
||||||
|
pub message_id: String,
|
||||||
|
pub session_id: String,
|
||||||
|
pub role: PuzzleAgentMessageRole,
|
||||||
|
pub kind: PuzzleAgentMessageKind,
|
||||||
|
pub text: String,
|
||||||
|
pub created_at_micros: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for PuzzleAgentMessageSnapshot {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -4,11 +4,13 @@
|
|||||||
#![allow(unused, clippy::all)]
|
#![allow(unused, clippy::all)]
|
||||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||||
|
|
||||||
|
use super::puzzle_agent_session_snapshot_type::PuzzleAgentSessionSnapshot;
|
||||||
|
|
||||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||||
#[sats(crate = __lib)]
|
#[sats(crate = __lib)]
|
||||||
pub struct PuzzleAgentSessionProcedureResult {
|
pub struct PuzzleAgentSessionProcedureResult {
|
||||||
pub ok: bool,
|
pub ok: bool,
|
||||||
pub session_json: Option<String>,
|
pub session: Option<PuzzleAgentSessionSnapshot>,
|
||||||
pub error_message: Option<String>,
|
pub error_message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// 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::puzzle_agent_message_snapshot_type::PuzzleAgentMessageSnapshot;
|
||||||
|
use super::puzzle_agent_stage_type::PuzzleAgentStage;
|
||||||
|
use super::puzzle_agent_suggested_action_type::PuzzleAgentSuggestedAction;
|
||||||
|
use super::puzzle_anchor_pack_type::PuzzleAnchorPack;
|
||||||
|
use super::puzzle_result_draft_type::PuzzleResultDraft;
|
||||||
|
use super::puzzle_result_preview_envelope_type::PuzzleResultPreviewEnvelope;
|
||||||
|
|
||||||
|
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||||
|
#[sats(crate = __lib)]
|
||||||
|
pub struct PuzzleAgentSessionSnapshot {
|
||||||
|
pub session_id: String,
|
||||||
|
pub owner_user_id: String,
|
||||||
|
pub seed_text: String,
|
||||||
|
pub current_turn: u32,
|
||||||
|
pub progress_percent: u32,
|
||||||
|
pub stage: PuzzleAgentStage,
|
||||||
|
pub anchor_pack: PuzzleAnchorPack,
|
||||||
|
pub draft: Option<PuzzleResultDraft>,
|
||||||
|
pub messages: Vec<PuzzleAgentMessageSnapshot>,
|
||||||
|
pub last_assistant_reply: Option<String>,
|
||||||
|
pub published_profile_id: Option<String>,
|
||||||
|
pub suggested_actions: Vec<PuzzleAgentSuggestedAction>,
|
||||||
|
pub result_preview: Option<PuzzleResultPreviewEnvelope>,
|
||||||
|
pub created_at_micros: i64,
|
||||||
|
pub updated_at_micros: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for PuzzleAgentSessionSnapshot {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// 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 PuzzleAgentSuggestedAction {
|
||||||
|
pub id: String,
|
||||||
|
pub action_type: String,
|
||||||
|
pub label: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for PuzzleAgentSuggestedAction {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// 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::puzzle_anchor_status_type::PuzzleAnchorStatus;
|
||||||
|
|
||||||
|
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||||
|
#[sats(crate = __lib)]
|
||||||
|
pub struct PuzzleAnchorItem {
|
||||||
|
pub key: String,
|
||||||
|
pub label: String,
|
||||||
|
pub value: String,
|
||||||
|
pub status: PuzzleAnchorStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for PuzzleAnchorItem {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// 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::puzzle_anchor_item_type::PuzzleAnchorItem;
|
||||||
|
|
||||||
|
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||||
|
#[sats(crate = __lib)]
|
||||||
|
pub struct PuzzleAnchorPack {
|
||||||
|
pub theme_promise: PuzzleAnchorItem,
|
||||||
|
pub visual_subject: PuzzleAnchorItem,
|
||||||
|
pub visual_mood: PuzzleAnchorItem,
|
||||||
|
pub composition_hooks: PuzzleAnchorItem,
|
||||||
|
pub tags_and_forbidden: PuzzleAnchorItem,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for PuzzleAnchorPack {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -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)]
|
||||||
|
#[derive(Copy, Eq, Hash)]
|
||||||
|
pub enum PuzzleAnchorStatus {
|
||||||
|
Missing,
|
||||||
|
|
||||||
|
Inferred,
|
||||||
|
|
||||||
|
Confirmed,
|
||||||
|
|
||||||
|
Locked,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for PuzzleAnchorStatus {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -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 PuzzleAudioAsset {
|
||||||
|
pub task_id: String,
|
||||||
|
pub provider: String,
|
||||||
|
pub asset_object_id: Option<String>,
|
||||||
|
pub asset_kind: Option<String>,
|
||||||
|
pub audio_src: String,
|
||||||
|
pub prompt: Option<String>,
|
||||||
|
pub title: Option<String>,
|
||||||
|
pub updated_at: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for PuzzleAudioAsset {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// 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::puzzle_merged_group_state_type::PuzzleMergedGroupState;
|
||||||
|
use super::puzzle_piece_state_type::PuzzlePieceState;
|
||||||
|
|
||||||
|
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||||
|
#[sats(crate = __lib)]
|
||||||
|
pub struct PuzzleBoardSnapshot {
|
||||||
|
pub rows: u32,
|
||||||
|
pub cols: u32,
|
||||||
|
pub pieces: Vec<PuzzlePieceState>,
|
||||||
|
pub merged_groups: Vec<PuzzleMergedGroupState>,
|
||||||
|
pub selected_piece_id: Option<String>,
|
||||||
|
pub all_tiles_resolved: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for PuzzleBoardSnapshot {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// 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 PuzzleCellPosition {
|
||||||
|
pub row: u32,
|
||||||
|
pub col: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for PuzzleCellPosition {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -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 PuzzleCreatorIntent {
|
||||||
|
pub source_mode: String,
|
||||||
|
pub raw_messages_summary: String,
|
||||||
|
pub theme_promise: String,
|
||||||
|
pub visual_subject: String,
|
||||||
|
pub visual_mood: Vec<String>,
|
||||||
|
pub composition_hooks: Vec<String>,
|
||||||
|
pub theme_tags: Vec<String>,
|
||||||
|
pub forbidden_directives: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for PuzzleCreatorIntent {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -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};
|
||||||
|
|
||||||
|
use super::puzzle_audio_asset_type::PuzzleAudioAsset;
|
||||||
|
use super::puzzle_generated_image_candidate_type::PuzzleGeneratedImageCandidate;
|
||||||
|
|
||||||
|
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||||
|
#[sats(crate = __lib)]
|
||||||
|
pub struct PuzzleDraftLevel {
|
||||||
|
pub level_id: String,
|
||||||
|
pub level_name: String,
|
||||||
|
pub picture_description: String,
|
||||||
|
pub picture_reference: Option<String>,
|
||||||
|
pub ui_background_prompt: Option<String>,
|
||||||
|
pub ui_background_image_src: Option<String>,
|
||||||
|
pub ui_background_image_object_key: Option<String>,
|
||||||
|
pub background_music: Option<PuzzleAudioAsset>,
|
||||||
|
pub candidates: Vec<PuzzleGeneratedImageCandidate>,
|
||||||
|
pub selected_candidate_id: Option<String>,
|
||||||
|
pub cover_image_src: Option<String>,
|
||||||
|
pub cover_asset_id: Option<String>,
|
||||||
|
pub generation_status: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for PuzzleDraftLevel {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// 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 PuzzleFormDraft {
|
||||||
|
pub work_title: Option<String>,
|
||||||
|
pub work_description: Option<String>,
|
||||||
|
pub picture_description: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for PuzzleFormDraft {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
// 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::puzzle_publication_status_type::PuzzlePublicationStatus;
|
||||||
|
|
||||||
|
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||||
|
#[sats(crate = __lib)]
|
||||||
|
pub struct PuzzleGalleryCardViewRow {
|
||||||
|
pub work_id: String,
|
||||||
|
pub profile_id: String,
|
||||||
|
pub owner_user_id: String,
|
||||||
|
pub source_session_id: Option<String>,
|
||||||
|
pub author_display_name: String,
|
||||||
|
pub work_title: String,
|
||||||
|
pub work_description: String,
|
||||||
|
pub level_name: String,
|
||||||
|
pub summary: String,
|
||||||
|
pub theme_tags: Vec<String>,
|
||||||
|
pub cover_image_src: Option<String>,
|
||||||
|
pub cover_asset_id: Option<String>,
|
||||||
|
pub publication_status: PuzzlePublicationStatus,
|
||||||
|
pub updated_at_micros: i64,
|
||||||
|
pub published_at_micros: Option<i64>,
|
||||||
|
pub play_count: u32,
|
||||||
|
pub remix_count: u32,
|
||||||
|
pub like_count: u32,
|
||||||
|
pub point_incentive_total_half_points: u64,
|
||||||
|
pub point_incentive_claimed_points: u64,
|
||||||
|
pub publish_ready: bool,
|
||||||
|
pub generation_status: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for PuzzleGalleryCardViewRow {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Column accessor struct for the table `PuzzleGalleryCardViewRow`.
|
||||||
|
///
|
||||||
|
/// Provides typed access to columns for query building.
|
||||||
|
pub struct PuzzleGalleryCardViewRowCols {
|
||||||
|
pub work_id: __sdk::__query_builder::Col<PuzzleGalleryCardViewRow, String>,
|
||||||
|
pub profile_id: __sdk::__query_builder::Col<PuzzleGalleryCardViewRow, String>,
|
||||||
|
pub owner_user_id: __sdk::__query_builder::Col<PuzzleGalleryCardViewRow, String>,
|
||||||
|
pub source_session_id: __sdk::__query_builder::Col<PuzzleGalleryCardViewRow, Option<String>>,
|
||||||
|
pub author_display_name: __sdk::__query_builder::Col<PuzzleGalleryCardViewRow, String>,
|
||||||
|
pub work_title: __sdk::__query_builder::Col<PuzzleGalleryCardViewRow, String>,
|
||||||
|
pub work_description: __sdk::__query_builder::Col<PuzzleGalleryCardViewRow, String>,
|
||||||
|
pub level_name: __sdk::__query_builder::Col<PuzzleGalleryCardViewRow, String>,
|
||||||
|
pub summary: __sdk::__query_builder::Col<PuzzleGalleryCardViewRow, String>,
|
||||||
|
pub theme_tags: __sdk::__query_builder::Col<PuzzleGalleryCardViewRow, Vec<String>>,
|
||||||
|
pub cover_image_src: __sdk::__query_builder::Col<PuzzleGalleryCardViewRow, Option<String>>,
|
||||||
|
pub cover_asset_id: __sdk::__query_builder::Col<PuzzleGalleryCardViewRow, Option<String>>,
|
||||||
|
pub publication_status:
|
||||||
|
__sdk::__query_builder::Col<PuzzleGalleryCardViewRow, PuzzlePublicationStatus>,
|
||||||
|
pub updated_at_micros: __sdk::__query_builder::Col<PuzzleGalleryCardViewRow, i64>,
|
||||||
|
pub published_at_micros: __sdk::__query_builder::Col<PuzzleGalleryCardViewRow, Option<i64>>,
|
||||||
|
pub play_count: __sdk::__query_builder::Col<PuzzleGalleryCardViewRow, u32>,
|
||||||
|
pub remix_count: __sdk::__query_builder::Col<PuzzleGalleryCardViewRow, u32>,
|
||||||
|
pub like_count: __sdk::__query_builder::Col<PuzzleGalleryCardViewRow, u32>,
|
||||||
|
pub point_incentive_total_half_points:
|
||||||
|
__sdk::__query_builder::Col<PuzzleGalleryCardViewRow, u64>,
|
||||||
|
pub point_incentive_claimed_points: __sdk::__query_builder::Col<PuzzleGalleryCardViewRow, u64>,
|
||||||
|
pub publish_ready: __sdk::__query_builder::Col<PuzzleGalleryCardViewRow, bool>,
|
||||||
|
pub generation_status: __sdk::__query_builder::Col<PuzzleGalleryCardViewRow, Option<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::__query_builder::HasCols for PuzzleGalleryCardViewRow {
|
||||||
|
type Cols = PuzzleGalleryCardViewRowCols;
|
||||||
|
fn cols(table_name: &'static str) -> Self::Cols {
|
||||||
|
PuzzleGalleryCardViewRowCols {
|
||||||
|
work_id: __sdk::__query_builder::Col::new(table_name, "work_id"),
|
||||||
|
profile_id: __sdk::__query_builder::Col::new(table_name, "profile_id"),
|
||||||
|
owner_user_id: __sdk::__query_builder::Col::new(table_name, "owner_user_id"),
|
||||||
|
source_session_id: __sdk::__query_builder::Col::new(table_name, "source_session_id"),
|
||||||
|
author_display_name: __sdk::__query_builder::Col::new(
|
||||||
|
table_name,
|
||||||
|
"author_display_name",
|
||||||
|
),
|
||||||
|
work_title: __sdk::__query_builder::Col::new(table_name, "work_title"),
|
||||||
|
work_description: __sdk::__query_builder::Col::new(table_name, "work_description"),
|
||||||
|
level_name: __sdk::__query_builder::Col::new(table_name, "level_name"),
|
||||||
|
summary: __sdk::__query_builder::Col::new(table_name, "summary"),
|
||||||
|
theme_tags: __sdk::__query_builder::Col::new(table_name, "theme_tags"),
|
||||||
|
cover_image_src: __sdk::__query_builder::Col::new(table_name, "cover_image_src"),
|
||||||
|
cover_asset_id: __sdk::__query_builder::Col::new(table_name, "cover_asset_id"),
|
||||||
|
publication_status: __sdk::__query_builder::Col::new(table_name, "publication_status"),
|
||||||
|
updated_at_micros: __sdk::__query_builder::Col::new(table_name, "updated_at_micros"),
|
||||||
|
published_at_micros: __sdk::__query_builder::Col::new(
|
||||||
|
table_name,
|
||||||
|
"published_at_micros",
|
||||||
|
),
|
||||||
|
play_count: __sdk::__query_builder::Col::new(table_name, "play_count"),
|
||||||
|
remix_count: __sdk::__query_builder::Col::new(table_name, "remix_count"),
|
||||||
|
like_count: __sdk::__query_builder::Col::new(table_name, "like_count"),
|
||||||
|
point_incentive_total_half_points: __sdk::__query_builder::Col::new(
|
||||||
|
table_name,
|
||||||
|
"point_incentive_total_half_points",
|
||||||
|
),
|
||||||
|
point_incentive_claimed_points: __sdk::__query_builder::Col::new(
|
||||||
|
table_name,
|
||||||
|
"point_incentive_claimed_points",
|
||||||
|
),
|
||||||
|
publish_ready: __sdk::__query_builder::Col::new(table_name, "publish_ready"),
|
||||||
|
generation_status: __sdk::__query_builder::Col::new(table_name, "generation_status"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
// 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::puzzle_gallery_card_view_row_type::PuzzleGalleryCardViewRow;
|
||||||
|
use super::puzzle_publication_status_type::PuzzlePublicationStatus;
|
||||||
|
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||||
|
|
||||||
|
/// Table handle for the table `puzzle_gallery_card_view`.
|
||||||
|
///
|
||||||
|
/// Obtain a handle from the [`PuzzleGalleryCardViewTableAccess::puzzle_gallery_card_view`] method on [`super::RemoteTables`],
|
||||||
|
/// like `ctx.db.puzzle_gallery_card_view()`.
|
||||||
|
///
|
||||||
|
/// Users are encouraged not to explicitly reference this type,
|
||||||
|
/// but to directly chain method calls,
|
||||||
|
/// like `ctx.db.puzzle_gallery_card_view().on_insert(...)`.
|
||||||
|
pub struct PuzzleGalleryCardViewTableHandle<'ctx> {
|
||||||
|
imp: __sdk::TableHandle<PuzzleGalleryCardViewRow>,
|
||||||
|
ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
/// Extension trait for access to the table `puzzle_gallery_card_view`.
|
||||||
|
///
|
||||||
|
/// Implemented for [`super::RemoteTables`].
|
||||||
|
pub trait PuzzleGalleryCardViewTableAccess {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
/// Obtain a [`PuzzleGalleryCardViewTableHandle`], which mediates access to the table `puzzle_gallery_card_view`.
|
||||||
|
fn puzzle_gallery_card_view(&self) -> PuzzleGalleryCardViewTableHandle<'_>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PuzzleGalleryCardViewTableAccess for super::RemoteTables {
|
||||||
|
fn puzzle_gallery_card_view(&self) -> PuzzleGalleryCardViewTableHandle<'_> {
|
||||||
|
PuzzleGalleryCardViewTableHandle {
|
||||||
|
imp: self
|
||||||
|
.imp
|
||||||
|
.get_table::<PuzzleGalleryCardViewRow>("puzzle_gallery_card_view"),
|
||||||
|
ctx: std::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PuzzleGalleryCardViewInsertCallbackId(__sdk::CallbackId);
|
||||||
|
pub struct PuzzleGalleryCardViewDeleteCallbackId(__sdk::CallbackId);
|
||||||
|
|
||||||
|
impl<'ctx> __sdk::Table for PuzzleGalleryCardViewTableHandle<'ctx> {
|
||||||
|
type Row = PuzzleGalleryCardViewRow;
|
||||||
|
type EventContext = super::EventContext;
|
||||||
|
|
||||||
|
fn count(&self) -> u64 {
|
||||||
|
self.imp.count()
|
||||||
|
}
|
||||||
|
fn iter(&self) -> impl Iterator<Item = PuzzleGalleryCardViewRow> + '_ {
|
||||||
|
self.imp.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
type InsertCallbackId = PuzzleGalleryCardViewInsertCallbackId;
|
||||||
|
|
||||||
|
fn on_insert(
|
||||||
|
&self,
|
||||||
|
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
|
||||||
|
) -> PuzzleGalleryCardViewInsertCallbackId {
|
||||||
|
PuzzleGalleryCardViewInsertCallbackId(self.imp.on_insert(Box::new(callback)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_on_insert(&self, callback: PuzzleGalleryCardViewInsertCallbackId) {
|
||||||
|
self.imp.remove_on_insert(callback.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteCallbackId = PuzzleGalleryCardViewDeleteCallbackId;
|
||||||
|
|
||||||
|
fn on_delete(
|
||||||
|
&self,
|
||||||
|
callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static,
|
||||||
|
) -> PuzzleGalleryCardViewDeleteCallbackId {
|
||||||
|
PuzzleGalleryCardViewDeleteCallbackId(self.imp.on_delete(Box::new(callback)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_on_delete(&self, callback: PuzzleGalleryCardViewDeleteCallbackId) {
|
||||||
|
self.imp.remove_on_delete(callback.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
|
||||||
|
let _table =
|
||||||
|
client_cache.get_or_make_table::<PuzzleGalleryCardViewRow>("puzzle_gallery_card_view");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub(super) fn parse_table_update(
|
||||||
|
raw_updates: __ws::v2::TableUpdate,
|
||||||
|
) -> __sdk::Result<__sdk::TableUpdate<PuzzleGalleryCardViewRow>> {
|
||||||
|
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
|
||||||
|
__sdk::InternalError::failed_parse("TableUpdate<PuzzleGalleryCardViewRow>", "TableUpdate")
|
||||||
|
.with_cause(e)
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
/// Extension trait for query builder access to the table `PuzzleGalleryCardViewRow`.
|
||||||
|
///
|
||||||
|
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||||
|
pub trait puzzle_gallery_card_viewQueryTableAccess {
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
/// Get a query builder for the table `PuzzleGalleryCardViewRow`.
|
||||||
|
fn puzzle_gallery_card_view(&self) -> __sdk::__query_builder::Table<PuzzleGalleryCardViewRow>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl puzzle_gallery_card_viewQueryTableAccess for __sdk::QueryTableAccessor {
|
||||||
|
fn puzzle_gallery_card_view(&self) -> __sdk::__query_builder::Table<PuzzleGalleryCardViewRow> {
|
||||||
|
__sdk::__query_builder::Table::new("puzzle_gallery_card_view")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,246 +2,12 @@
|
|||||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||||
|
|
||||||
#![allow(unused, clippy::all)]
|
#![allow(unused, clippy::all)]
|
||||||
|
use super::puzzle_anchor_pack_type::PuzzleAnchorPack;
|
||||||
|
use super::puzzle_draft_level_type::PuzzleDraftLevel;
|
||||||
|
use super::puzzle_publication_status_type::PuzzlePublicationStatus;
|
||||||
|
use super::puzzle_work_profile_type::PuzzleWorkProfile;
|
||||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||||
|
|
||||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, Copy, PartialEq, Debug)]
|
|
||||||
#[sats(crate = __lib)]
|
|
||||||
#[derive(Eq, Hash)]
|
|
||||||
pub enum PuzzleGalleryAnchorStatus {
|
|
||||||
Missing,
|
|
||||||
Inferred,
|
|
||||||
Confirmed,
|
|
||||||
Locked,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
|
||||||
#[sats(crate = __lib)]
|
|
||||||
pub struct PuzzleGalleryAnchorItem {
|
|
||||||
pub key: String,
|
|
||||||
pub label: String,
|
|
||||||
pub value: String,
|
|
||||||
pub status: PuzzleGalleryAnchorStatus,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
|
||||||
#[sats(crate = __lib)]
|
|
||||||
pub struct PuzzleGalleryAnchorPack {
|
|
||||||
pub theme_promise: PuzzleGalleryAnchorItem,
|
|
||||||
pub visual_subject: PuzzleGalleryAnchorItem,
|
|
||||||
pub visual_mood: PuzzleGalleryAnchorItem,
|
|
||||||
pub composition_hooks: PuzzleGalleryAnchorItem,
|
|
||||||
pub tags_and_forbidden: PuzzleGalleryAnchorItem,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
|
||||||
#[sats(crate = __lib)]
|
|
||||||
pub struct PuzzleGalleryGeneratedImageCandidate {
|
|
||||||
pub candidate_id: String,
|
|
||||||
pub image_src: String,
|
|
||||||
pub asset_id: String,
|
|
||||||
pub prompt: String,
|
|
||||||
pub actual_prompt: Option<String>,
|
|
||||||
pub source_type: String,
|
|
||||||
pub selected: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
|
||||||
#[sats(crate = __lib)]
|
|
||||||
pub struct PuzzleGalleryAudioAsset {
|
|
||||||
pub task_id: String,
|
|
||||||
pub provider: String,
|
|
||||||
pub asset_object_id: Option<String>,
|
|
||||||
pub asset_kind: Option<String>,
|
|
||||||
pub audio_src: String,
|
|
||||||
pub prompt: Option<String>,
|
|
||||||
pub title: Option<String>,
|
|
||||||
pub updated_at: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
|
||||||
#[sats(crate = __lib)]
|
|
||||||
pub struct PuzzleGalleryDraftLevel {
|
|
||||||
pub level_id: String,
|
|
||||||
pub level_name: String,
|
|
||||||
pub picture_description: String,
|
|
||||||
pub picture_reference: Option<String>,
|
|
||||||
pub ui_background_prompt: Option<String>,
|
|
||||||
pub ui_background_image_src: Option<String>,
|
|
||||||
pub ui_background_image_object_key: Option<String>,
|
|
||||||
pub background_music: Option<PuzzleGalleryAudioAsset>,
|
|
||||||
pub candidates: Vec<PuzzleGalleryGeneratedImageCandidate>,
|
|
||||||
pub selected_candidate_id: Option<String>,
|
|
||||||
pub cover_image_src: Option<String>,
|
|
||||||
pub cover_asset_id: Option<String>,
|
|
||||||
pub generation_status: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, Copy, PartialEq, Debug)]
|
|
||||||
#[sats(crate = __lib)]
|
|
||||||
#[derive(Eq, Hash)]
|
|
||||||
pub enum PuzzleGalleryPublicationStatus {
|
|
||||||
Draft,
|
|
||||||
Published,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
|
||||||
#[sats(crate = __lib)]
|
|
||||||
pub struct PuzzleGalleryViewRow {
|
|
||||||
pub work_id: String,
|
|
||||||
pub profile_id: String,
|
|
||||||
pub owner_user_id: String,
|
|
||||||
pub source_session_id: Option<String>,
|
|
||||||
pub author_display_name: String,
|
|
||||||
pub work_title: String,
|
|
||||||
pub work_description: String,
|
|
||||||
pub level_name: String,
|
|
||||||
pub summary: String,
|
|
||||||
pub theme_tags: Vec<String>,
|
|
||||||
pub cover_image_src: Option<String>,
|
|
||||||
pub cover_asset_id: Option<String>,
|
|
||||||
pub levels: Vec<PuzzleGalleryDraftLevel>,
|
|
||||||
pub publication_status: PuzzleGalleryPublicationStatus,
|
|
||||||
pub updated_at_micros: i64,
|
|
||||||
pub published_at_micros: Option<i64>,
|
|
||||||
pub play_count: u32,
|
|
||||||
pub remix_count: u32,
|
|
||||||
pub like_count: u32,
|
|
||||||
pub recent_play_count_7d: u32,
|
|
||||||
pub point_incentive_total_half_points: u64,
|
|
||||||
pub point_incentive_claimed_points: u64,
|
|
||||||
pub publish_ready: bool,
|
|
||||||
pub anchor_pack: PuzzleGalleryAnchorPack,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PuzzleGalleryAnchorStatus> for module_puzzle::PuzzleAnchorStatus {
|
|
||||||
fn from(status: PuzzleGalleryAnchorStatus) -> Self {
|
|
||||||
match status {
|
|
||||||
PuzzleGalleryAnchorStatus::Missing => Self::Missing,
|
|
||||||
PuzzleGalleryAnchorStatus::Inferred => Self::Inferred,
|
|
||||||
PuzzleGalleryAnchorStatus::Confirmed => Self::Confirmed,
|
|
||||||
PuzzleGalleryAnchorStatus::Locked => Self::Locked,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PuzzleGalleryAnchorItem> for module_puzzle::PuzzleAnchorItem {
|
|
||||||
fn from(item: PuzzleGalleryAnchorItem) -> Self {
|
|
||||||
Self {
|
|
||||||
key: item.key,
|
|
||||||
label: item.label,
|
|
||||||
value: item.value,
|
|
||||||
status: item.status.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PuzzleGalleryAnchorPack> for module_puzzle::PuzzleAnchorPack {
|
|
||||||
fn from(pack: PuzzleGalleryAnchorPack) -> Self {
|
|
||||||
Self {
|
|
||||||
theme_promise: pack.theme_promise.into(),
|
|
||||||
visual_subject: pack.visual_subject.into(),
|
|
||||||
visual_mood: pack.visual_mood.into(),
|
|
||||||
composition_hooks: pack.composition_hooks.into(),
|
|
||||||
tags_and_forbidden: pack.tags_and_forbidden.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PuzzleGalleryGeneratedImageCandidate>
|
|
||||||
for module_puzzle::PuzzleGeneratedImageCandidate
|
|
||||||
{
|
|
||||||
fn from(candidate: PuzzleGalleryGeneratedImageCandidate) -> Self {
|
|
||||||
Self {
|
|
||||||
candidate_id: candidate.candidate_id,
|
|
||||||
image_src: candidate.image_src,
|
|
||||||
asset_id: candidate.asset_id,
|
|
||||||
prompt: candidate.prompt,
|
|
||||||
actual_prompt: candidate.actual_prompt,
|
|
||||||
source_type: candidate.source_type,
|
|
||||||
selected: candidate.selected,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PuzzleGalleryAudioAsset> for module_puzzle::PuzzleAudioAsset {
|
|
||||||
fn from(asset: PuzzleGalleryAudioAsset) -> Self {
|
|
||||||
Self {
|
|
||||||
task_id: asset.task_id,
|
|
||||||
provider: asset.provider,
|
|
||||||
asset_object_id: asset.asset_object_id,
|
|
||||||
asset_kind: asset.asset_kind,
|
|
||||||
audio_src: asset.audio_src,
|
|
||||||
prompt: asset.prompt,
|
|
||||||
title: asset.title,
|
|
||||||
updated_at: asset.updated_at,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PuzzleGalleryDraftLevel> for module_puzzle::PuzzleDraftLevel {
|
|
||||||
fn from(level: PuzzleGalleryDraftLevel) -> Self {
|
|
||||||
Self {
|
|
||||||
level_id: level.level_id,
|
|
||||||
level_name: level.level_name,
|
|
||||||
picture_description: level.picture_description,
|
|
||||||
picture_reference: level.picture_reference,
|
|
||||||
ui_background_prompt: level.ui_background_prompt,
|
|
||||||
ui_background_image_src: level.ui_background_image_src,
|
|
||||||
ui_background_image_object_key: level.ui_background_image_object_key,
|
|
||||||
background_music: level.background_music.map(Into::into),
|
|
||||||
candidates: level.candidates.into_iter().map(Into::into).collect(),
|
|
||||||
selected_candidate_id: level.selected_candidate_id,
|
|
||||||
cover_image_src: level.cover_image_src,
|
|
||||||
cover_asset_id: level.cover_asset_id,
|
|
||||||
generation_status: level.generation_status,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PuzzleGalleryPublicationStatus> for module_puzzle::PuzzlePublicationStatus {
|
|
||||||
fn from(status: PuzzleGalleryPublicationStatus) -> Self {
|
|
||||||
match status {
|
|
||||||
PuzzleGalleryPublicationStatus::Draft => Self::Draft,
|
|
||||||
PuzzleGalleryPublicationStatus::Published => Self::Published,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PuzzleGalleryViewRow> for module_puzzle::PuzzleWorkProfile {
|
|
||||||
fn from(row: PuzzleGalleryViewRow) -> Self {
|
|
||||||
Self {
|
|
||||||
work_id: row.work_id,
|
|
||||||
profile_id: row.profile_id,
|
|
||||||
owner_user_id: row.owner_user_id,
|
|
||||||
source_session_id: row.source_session_id,
|
|
||||||
author_display_name: row.author_display_name,
|
|
||||||
work_title: row.work_title,
|
|
||||||
work_description: row.work_description,
|
|
||||||
level_name: row.level_name,
|
|
||||||
summary: row.summary,
|
|
||||||
theme_tags: row.theme_tags,
|
|
||||||
cover_image_src: row.cover_image_src,
|
|
||||||
cover_asset_id: row.cover_asset_id,
|
|
||||||
levels: row.levels.into_iter().map(Into::into).collect(),
|
|
||||||
publication_status: row.publication_status.into(),
|
|
||||||
updated_at_micros: row.updated_at_micros,
|
|
||||||
published_at_micros: row.published_at_micros,
|
|
||||||
play_count: row.play_count,
|
|
||||||
remix_count: row.remix_count,
|
|
||||||
like_count: row.like_count,
|
|
||||||
recent_play_count_7d: row.recent_play_count_7d,
|
|
||||||
point_incentive_total_half_points: row.point_incentive_total_half_points,
|
|
||||||
point_incentive_claimed_points: row.point_incentive_claimed_points,
|
|
||||||
publish_ready: row.publish_ready,
|
|
||||||
anchor_pack: row.anchor_pack.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl __sdk::InModule for PuzzleGalleryViewRow {
|
|
||||||
type Module = super::RemoteModule;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Table handle for the table `puzzle_gallery_view`.
|
/// Table handle for the table `puzzle_gallery_view`.
|
||||||
///
|
///
|
||||||
/// Obtain a handle from the [`PuzzleGalleryViewTableAccess::puzzle_gallery_view`] method on [`super::RemoteTables`],
|
/// Obtain a handle from the [`PuzzleGalleryViewTableAccess::puzzle_gallery_view`] method on [`super::RemoteTables`],
|
||||||
@@ -251,7 +17,7 @@ impl __sdk::InModule for PuzzleGalleryViewRow {
|
|||||||
/// but to directly chain method calls,
|
/// but to directly chain method calls,
|
||||||
/// like `ctx.db.puzzle_gallery_view().on_insert(...)`.
|
/// like `ctx.db.puzzle_gallery_view().on_insert(...)`.
|
||||||
pub struct PuzzleGalleryViewTableHandle<'ctx> {
|
pub struct PuzzleGalleryViewTableHandle<'ctx> {
|
||||||
imp: __sdk::TableHandle<PuzzleGalleryViewRow>,
|
imp: __sdk::TableHandle<PuzzleWorkProfile>,
|
||||||
ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +36,7 @@ impl PuzzleGalleryViewTableAccess for super::RemoteTables {
|
|||||||
PuzzleGalleryViewTableHandle {
|
PuzzleGalleryViewTableHandle {
|
||||||
imp: self
|
imp: self
|
||||||
.imp
|
.imp
|
||||||
.get_table::<PuzzleGalleryViewRow>("puzzle_gallery_view"),
|
.get_table::<PuzzleWorkProfile>("puzzle_gallery_view"),
|
||||||
ctx: std::marker::PhantomData,
|
ctx: std::marker::PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -280,13 +46,13 @@ pub struct PuzzleGalleryViewInsertCallbackId(__sdk::CallbackId);
|
|||||||
pub struct PuzzleGalleryViewDeleteCallbackId(__sdk::CallbackId);
|
pub struct PuzzleGalleryViewDeleteCallbackId(__sdk::CallbackId);
|
||||||
|
|
||||||
impl<'ctx> __sdk::Table for PuzzleGalleryViewTableHandle<'ctx> {
|
impl<'ctx> __sdk::Table for PuzzleGalleryViewTableHandle<'ctx> {
|
||||||
type Row = PuzzleGalleryViewRow;
|
type Row = PuzzleWorkProfile;
|
||||||
type EventContext = super::EventContext;
|
type EventContext = super::EventContext;
|
||||||
|
|
||||||
fn count(&self) -> u64 {
|
fn count(&self) -> u64 {
|
||||||
self.imp.count()
|
self.imp.count()
|
||||||
}
|
}
|
||||||
fn iter(&self) -> impl Iterator<Item = PuzzleGalleryViewRow> + '_ {
|
fn iter(&self) -> impl Iterator<Item = PuzzleWorkProfile> + '_ {
|
||||||
self.imp.iter()
|
self.imp.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,32 +85,32 @@ impl<'ctx> __sdk::Table for PuzzleGalleryViewTableHandle<'ctx> {
|
|||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
|
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
|
||||||
let _table = client_cache.get_or_make_table::<PuzzleGalleryViewRow>("puzzle_gallery_view");
|
let _table = client_cache.get_or_make_table::<PuzzleWorkProfile>("puzzle_gallery_view");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub(super) fn parse_table_update(
|
pub(super) fn parse_table_update(
|
||||||
raw_updates: __ws::v2::TableUpdate,
|
raw_updates: __ws::v2::TableUpdate,
|
||||||
) -> __sdk::Result<__sdk::TableUpdate<PuzzleGalleryViewRow>> {
|
) -> __sdk::Result<__sdk::TableUpdate<PuzzleWorkProfile>> {
|
||||||
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
|
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
|
||||||
__sdk::InternalError::failed_parse("TableUpdate<PuzzleGalleryViewRow>", "TableUpdate")
|
__sdk::InternalError::failed_parse("TableUpdate<PuzzleWorkProfile>", "TableUpdate")
|
||||||
.with_cause(e)
|
.with_cause(e)
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
/// Extension trait for query builder access to the table `PuzzleGalleryViewRow`.
|
/// Extension trait for query builder access to the table `PuzzleWorkProfile`.
|
||||||
///
|
///
|
||||||
/// Implemented for [`__sdk::QueryTableAccessor`].
|
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||||
pub trait puzzle_gallery_viewQueryTableAccess {
|
pub trait puzzle_gallery_viewQueryTableAccess {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
/// Get a query builder for the table `PuzzleGalleryViewRow`.
|
/// Get a query builder for the table `PuzzleWorkProfile`.
|
||||||
fn puzzle_gallery_view(&self) -> __sdk::__query_builder::Table<PuzzleGalleryViewRow>;
|
fn puzzle_gallery_view(&self) -> __sdk::__query_builder::Table<PuzzleWorkProfile>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl puzzle_gallery_viewQueryTableAccess for __sdk::QueryTableAccessor {
|
impl puzzle_gallery_viewQueryTableAccess for __sdk::QueryTableAccessor {
|
||||||
fn puzzle_gallery_view(&self) -> __sdk::__query_builder::Table<PuzzleGalleryViewRow> {
|
fn puzzle_gallery_view(&self) -> __sdk::__query_builder::Table<PuzzleWorkProfile> {
|
||||||
__sdk::__query_builder::Table::new("puzzle_gallery_view")
|
__sdk::__query_builder::Table::new("puzzle_gallery_view")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// 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 PuzzleGeneratedImageCandidate {
|
||||||
|
pub candidate_id: String,
|
||||||
|
pub image_src: String,
|
||||||
|
pub asset_id: String,
|
||||||
|
pub prompt: String,
|
||||||
|
pub actual_prompt: Option<String>,
|
||||||
|
pub source_type: String,
|
||||||
|
pub selected: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl __sdk::InModule for PuzzleGeneratedImageCandidate {
|
||||||
|
type Module = super::RemoteModule;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user