diff --git a/.env.local b/.env.local
index 34b87a66..f8079111 100644
--- a/.env.local
+++ b/.env.local
@@ -70,3 +70,9 @@ GENARRATIVE_SPACETIME_TOKEN=""
GENARRATIVE_ADMIN_USERNAME=admin
GENARRATIVE_ADMIN_PASSWORD=123456
ADMIN_API_TARGET=http://127.0.0.1:3100
+
+# OTLP
+GENARRATIVE_OTEL_ENABLED=true
+OTEL_SERVICE_NAME=genarrative-api
+OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4318
+OTEL_RESOURCE_ATTRIBUTES=deployment.environment=local,service.namespace=genarrative
diff --git a/.hermes/shared-memory/decision-log.md b/.hermes/shared-memory/decision-log.md
index c8893c4a..78fa5b46 100644
--- a/.hermes/shared-memory/decision-log.md
+++ b/.hermes/shared-memory/decision-log.md
@@ -16,6 +16,15 @@
---
+## 2026-05-16 api-server OpenTelemetry 统一补齐 traces metrics logs
+
+- 背景:压测与运行观测需要把 HTTP、SpacetimeDB 调用和应用日志串起来,同时保留本地 `journalctl` / 文件日志做故障排障。
+- 决策:`api-server` 通过 OTLP HTTP base endpoint 发送 traces、metrics 和 logs;Collector 统一用 `otelcol-contrib`,`npm run otel:debug` 负责 debug 采集,`npm run otel:rider` 负责转发到 Rider;Rider 只是接收与可视化端,不直接替代 Collector。
+- 日志口径:Rider Logs 面板只展示 log event 自身字段,请求完成日志需要直接携带 `request_id`、HTTP method、规范化 route、scheme、path、status、status_class、latency 和 slow_request;更完整的 request attributes 仍以 trace/span 为准。
+- 影响范围:`server-rs/crates/shared-logging`、`server-rs/crates/api-server`、`scripts/run-otelcol.mjs`、压测与运维文档。
+- 验证方式:`cargo test -p shared-logging --manifest-path server-rs/Cargo.toml generic_otlp_http_endpoint_expands_to_signal_paths`、`cargo test -p api-server --manifest-path server-rs/Cargo.toml observability_route_keeps_metrics_labels_low_cardinality`、`cargo test -p api-server --manifest-path server-rs/Cargo.toml resolve_request_scheme_uses_forwarded_proto_first_value`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`。
+- 关联文档:`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`、`scripts/loadtest/README.md`。
+
## 2026-05-14 创作页图像输入统一封装为图像组件
- 背景:拼图创作页已经具备“画面描述生图 / 多参考图生图 / 上传主图后 AI 重绘 / 上传主图后不重绘”四条路径,抓大鹅封面和后续创作页也会复用同一套交互;继续在页面内复制会导致参考图、预览、删除确认和重绘开关漂移。
diff --git a/.hermes/shared-memory/development-workflow.md b/.hermes/shared-memory/development-workflow.md
index 49bff2ef..9b077958 100644
--- a/.hermes/shared-memory/development-workflow.md
+++ b/.hermes/shared-memory/development-workflow.md
@@ -195,6 +195,13 @@ npm run check:server-rs-ddd
- `docs/technical/SPACETIMEDB_TABLE_CATALOG.md`
- `docs/technical/MAINCLOUD_REFERENCE_REMOVAL_POLICY_2026-05-06.md`
+## 生产压测与观测默认口径
+
+- 作品列表 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` 为准。
+- 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 串联。
+
## 前端相关默认验证
前端修改后,应根据修改范围选择:
diff --git a/.hermes/shared-memory/pitfalls.md b/.hermes/shared-memory/pitfalls.md
index 79e0216d..831163a6 100644
--- a/.hermes/shared-memory/pitfalls.md
+++ b/.hermes/shared-memory/pitfalls.md
@@ -374,6 +374,14 @@
- 验证:请求返回 JSON,相关页面不再出现 HTML parse 错误。
- 关联:`docs/technical/PROFILE_MAIN_ROUTE_VITE_PROXY_FIX_2026-05-02.md`。
+## `npm run build` 因 Vite warning 被 build-gate 判失败
+
+- 现象:主站或后台 Vite 已经输出 `built in ...`,但根命令最后仍失败并打印 `Build gate failed because warnings were emitted`。
+- 原因:`scripts/build-gate.mjs` 会收集 stdout / stderr 中的 warning 行并作为硬失败;常见触发是产物 chunk 超过 `vite.config.ts` 或 `apps/admin-web/vite.config.ts` 的 `chunkSizeWarningLimit`。
+- 处理:先看 warning 原文确认来源。若是合理的入口级 chunk 体积增长,调整对应 Vite 配置阈值或做真实拆包;不要把这类失败按 Rust / SpacetimeDB 编译错误排查。
+- 验证:重新执行 `npm run build`,主站与后台均构建完成且没有 build-gate warning 汇总。
+- 关联:`scripts/build-gate.mjs`、`vite.config.ts`、`apps/admin-web/vite.config.ts`。
+
## 反馈页清空 file input 前必须先拷贝 FileList
- 现象:点击上传凭证会打开文件选择框,但选择图片后页面没有展示预览,提交时也没有携带图片凭证。
diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index f6906f2e..00000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,10 +0,0 @@
-# 默认忽略的文件
-/shelf/
-/workspace.xml
-# 基于编辑器的 HTTP 客户端请求
-/httpRequests/
-# 已忽略包含查询文件的默认文件夹
-/queries/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
diff --git a/.idea/.name b/.idea/.name
deleted file mode 100644
index cf8f80f7..00000000
--- a/.idea/.name
+++ /dev/null
@@ -1 +0,0 @@
-mod.rs
\ No newline at end of file
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
deleted file mode 100644
index 932f7d1b..00000000
--- a/.idea/codeStyles/Project.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
deleted file mode 100644
index 79ee123c..00000000
--- a/.idea/codeStyles/codeStyleConfig.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/editor.xml b/.idea/editor.xml
deleted file mode 100644
index ead1d8a3..00000000
--- a/.idea/editor.xml
+++ /dev/null
@@ -1,248 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
deleted file mode 100644
index 03d9549e..00000000
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index 315bbf8a..00000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/prettier.xml b/.idea/prettier.xml
deleted file mode 100644
index b0c1c68f..00000000
--- a/.idea/prettier.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 35eb1ddf..00000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/deploy/env/api-server.env.example b/deploy/env/api-server.env.example
index 7420d6c9..1d727052 100644
--- a/deploy/env/api-server.env.example
+++ b/deploy/env/api-server.env.example
@@ -5,6 +5,12 @@ GENARRATIVE_ENV=production
GENARRATIVE_API_HOST=127.0.0.1
GENARRATIVE_API_PORT=8082
GENARRATIVE_API_LOG=info,tower_http=info
+GENARRATIVE_API_LISTEN_BACKLOG=1024
+GENARRATIVE_API_WORKER_THREADS=4
+GENARRATIVE_OTEL_ENABLED=false
+OTEL_SERVICE_NAME=genarrative-api
+OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4318
+OTEL_RESOURCE_ATTRIBUTES=deployment.environment=production,service.namespace=genarrative
GENARRATIVE_ADMIN_USERNAME=
GENARRATIVE_ADMIN_PASSWORD=
diff --git a/deploy/nginx/genarrative-dev-http.conf b/deploy/nginx/genarrative-dev-http.conf
index 824a8f5a..d9e85b16 100644
--- a/deploy/nginx/genarrative-dev-http.conf
+++ b/deploy/nginx/genarrative-dev-http.conf
@@ -1,9 +1,23 @@
# 开发服无域名时使用的 HTTP 入口,只允许用于 DEPLOY_TARGET=development。
# 没有域名时,将 SERVER_NAME 填为开发机 IP 或临时主机名。
# 生产 release 仍必须使用 genarrative.conf 的 HTTPS 配置。
+log_format genarrative_upstream
+ '$remote_addr - $remote_user [$time_local] "$request" '
+ '$status $body_bytes_sent "$http_referer" "$http_user_agent" '
+ 'request_time=$request_time upstream_connect_time=$upstream_connect_time '
+ 'upstream_header_time=$upstream_header_time upstream_response_time=$upstream_response_time '
+ 'upstream_status=$upstream_status request_id=$request_id';
+
+upstream genarrative_api {
+ server 127.0.0.1:8082;
+ keepalive 64;
+}
+
server {
listen 80;
server_name genarrative.example.com;
+ access_log /var/log/nginx/genarrative.access.log genarrative_upstream;
+ error_log /var/log/nginx/genarrative.error.log warn;
gzip on;
gzip_vary on;
@@ -34,8 +48,9 @@ server {
return 503 '{"ok":false,"error":{"code":"MAINTENANCE","message":"服务维护中"}}';
}
- proxy_pass http://127.0.0.1:8082/admin/api/;
+ 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;
@@ -73,12 +88,13 @@ server {
return 503 '{"ok":false,"error":{"code":"MAINTENANCE","message":"服务维护中"}}';
}
- proxy_pass http://127.0.0.1:8082;
+ 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;
diff --git a/deploy/nginx/genarrative.conf b/deploy/nginx/genarrative.conf
index 06a3bf86..e0854442 100644
--- a/deploy/nginx/genarrative.conf
+++ b/deploy/nginx/genarrative.conf
@@ -1,7 +1,21 @@
# 生产域名需要在部署前替换为真实域名,并由 certbot 或等价流程写入 HTTPS 证书配置。
+log_format genarrative_upstream
+ '$remote_addr - $remote_user [$time_local] "$request" '
+ '$status $body_bytes_sent "$http_referer" "$http_user_agent" '
+ 'request_time=$request_time upstream_connect_time=$upstream_connect_time '
+ 'upstream_header_time=$upstream_header_time upstream_response_time=$upstream_response_time '
+ 'upstream_status=$upstream_status request_id=$request_id';
+
+upstream genarrative_api {
+ server 127.0.0.1:8082;
+ keepalive 64;
+}
+
server {
listen 80;
server_name genarrative.example.com;
+ access_log /var/log/nginx/genarrative.access.log genarrative_upstream;
+ error_log /var/log/nginx/genarrative.error.log warn;
location /.well-known/acme-challenge/ {
root /var/www/html;
@@ -15,6 +29,8 @@ server {
server {
listen 443 ssl http2;
server_name genarrative.example.com;
+ access_log /var/log/nginx/genarrative.access.log genarrative_upstream;
+ error_log /var/log/nginx/genarrative.error.log warn;
gzip on;
gzip_vary on;
@@ -48,8 +64,9 @@ server {
return 503 '{"ok":false,"error":{"code":"MAINTENANCE","message":"服务维护中"}}';
}
- proxy_pass http://127.0.0.1:8082/admin/api/;
+ 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;
@@ -87,12 +104,13 @@ server {
return 503 '{"ok":false,"error":{"code":"MAINTENANCE","message":"服务维护中"}}';
}
- proxy_pass http://127.0.0.1:8082;
+ 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;
diff --git a/deploy/systemd/genarrative-api.service b/deploy/systemd/genarrative-api.service
index 1a22b75d..bba53a79 100644
--- a/deploy/systemd/genarrative-api.service
+++ b/deploy/systemd/genarrative-api.service
@@ -15,6 +15,8 @@ Restart=always
RestartSec=5
KillSignal=SIGINT
TimeoutStopSec=30
+LimitNOFILE=65535
+TasksMax=2048
# api-server 只读发布目录,运行态写入必须显式落到环境变量指定的服务端私有目录。
NoNewPrivileges=true
diff --git a/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md b/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md
index 6cc9b533..1709ffc4 100644
--- a/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md
+++ b/docs/【开发运维】本地开发验证与生产运维-2026-05-15.md
@@ -79,6 +79,8 @@ npm run lint
npm run check
```
+`npm run build` 由 `scripts/build-gate.mjs` 串行构建主站和后台;该门禁会把 Vite warning 当成失败处理。若看到 `Build gate failed because warnings were emitted`,先看 warning 原文,例如 chunk 体积超过 `vite.config.ts` / `apps/admin-web/vite.config.ts` 的 `chunkSizeWarningLimit`,不要先按 Rust 编译失败排查。
+
视觉小说负向扫描与验收门禁:
```bash
@@ -149,6 +151,25 @@ Jenkins 按 web / api / Spacetime module / build / deploy / publish 拆分
生产环境变量模板:`deploy/env/api-server.env.example`。真实密钥只放服务器,不提交 Git,不写入文档示例。
+50 HTTP req/s 首版压测优化口径:
+
+- `api-server` 生产模板默认 `GENARRATIVE_API_LISTEN_BACKLOG=1024`、`GENARRATIVE_API_WORKER_THREADS=4`;本地未设置 worker threads 时继续使用 Tokio 默认值。
+- `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`。
+- 作品列表 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`。
+- 50 HTTP req/s 验收目标为 `http_req_failed < 1%`、`p95 < 2s`、`dropped_iterations = 0`,同时压测窗口内 Nginx 无新增 502。
+
+OpenTelemetry 现阶段可选 OTLP traces / metrics / logs,但本地日志与 Nginx 文件日志仍保留:
+
+- 默认 `GENARRATIVE_OTEL_ENABLED=false`,未开启时 api-server 不依赖 Collector。
+- Collector 使用官方 `otelcol-contrib`,只监听 `127.0.0.1:4317/4318`;本地用 `npm run otel:debug` 启动 debug exporter,用 `npm run otel:rider` 转发到 Rider,再接 Jaeger、Tempo、Prometheus、Grafana 或托管平台。
+- api-server 开启时使用 `OTEL_SERVICE_NAME=genarrative-api`、`OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4318`。
+- 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`。
+- debug exporter / Rider 转发都会同时接收 traces、metrics 和 logs。
+- 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。
+
常见外部服务变量:
- `GENARRATIVE_SPACETIME_SERVER_URL`
diff --git a/package-lock.json b/package-lock.json
index 9008c606..b30a634e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -72,6 +72,7 @@
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
+ "peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
@@ -1515,7 +1516,6 @@
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true,
- "peer": true,
"engines": {
"node": ">=10"
},
@@ -1528,7 +1528,6 @@
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
"dev": true,
- "peer": true,
"dependencies": {
"ansi-regex": "^5.0.1",
"ansi-styles": "^5.0.0",
@@ -1542,8 +1541,7 @@
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
- "dev": true,
- "peer": true
+ "dev": true
},
"node_modules/@testing-library/react": {
"version": "16.3.2",
@@ -1606,8 +1604,7 @@
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
- "dev": true,
- "peer": true
+ "dev": true
},
"node_modules/@types/babel__core": {
"version": "7.20.5",
@@ -1650,7 +1647,8 @@
"version": "4.3.20",
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.20.tgz",
"integrity": "sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==",
- "dev": true
+ "dev": true,
+ "peer": true
},
"node_modules/@types/chai-subset": {
"version": "1.3.6",
@@ -1696,6 +1694,7 @@
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
"dev": true,
+ "peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -1705,6 +1704,7 @@
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"dev": true,
+ "peer": true,
"peerDependencies": {
"@types/react": "^19.2.0"
}
@@ -1796,6 +1796,7 @@
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz",
"integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==",
"dev": true,
+ "peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "6.21.0",
"@typescript-eslint/types": "6.21.0",
@@ -2126,6 +2127,7 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"dev": true,
+ "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -2216,7 +2218,6 @@
"integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
"dev": true,
"license": "Apache-2.0",
- "peer": true,
"dependencies": {
"dequal": "^2.0.3"
}
@@ -2338,6 +2339,7 @@
"url": "https://github.com/sponsors/ai"
}
],
+ "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -2629,7 +2631,6 @@
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=6"
}
@@ -2685,8 +2686,7 @@
"version": "0.5.16",
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
- "dev": true,
- "peer": true
+ "dev": true
},
"node_modules/domexception": {
"version": "4.0.0",
@@ -2873,6 +2873,7 @@
"integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
"dev": true,
+ "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
@@ -3697,6 +3698,7 @@
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-22.1.0.tgz",
"integrity": "sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==",
"dev": true,
+ "peer": true,
"dependencies": {
"abab": "^2.0.6",
"cssstyle": "^3.0.0",
@@ -4096,7 +4098,6 @@
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
"dev": true,
- "peer": true,
"bin": {
"lz-string": "bin/bin.js"
}
@@ -4435,6 +4436,7 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "peer": true,
"engines": {
"node": ">=12"
},
@@ -4486,6 +4488,7 @@
"url": "https://github.com/sponsors/ai"
}
],
+ "peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@@ -4619,6 +4622,7 @@
"version": "19.2.4",
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
+ "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -4627,6 +4631,7 @@
"version": "19.2.4",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
+ "peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -5074,6 +5079,7 @@
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
"integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
"devOptional": true,
+ "peer": true,
"dependencies": {
"esbuild": "~0.27.0",
"get-tsconfig": "^4.7.5"
@@ -5126,6 +5132,7 @@
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"dev": true,
+ "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -5207,6 +5214,7 @@
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
+ "peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.4.4",
@@ -7027,6 +7035,7 @@
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
+ "peer": true,
"requires": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
@@ -7835,15 +7844,13 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
- "dev": true,
- "peer": true
+ "dev": true
},
"pretty-format": {
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
"dev": true,
- "peer": true,
"requires": {
"ansi-regex": "^5.0.1",
"ansi-styles": "^5.0.0",
@@ -7854,8 +7861,7 @@
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
- "dev": true,
- "peer": true
+ "dev": true
}
}
},
@@ -7891,8 +7897,7 @@
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
- "dev": true,
- "peer": true
+ "dev": true
},
"@types/babel__core": {
"version": "7.20.5",
@@ -7935,7 +7940,8 @@
"version": "4.3.20",
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.20.tgz",
"integrity": "sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==",
- "dev": true
+ "dev": true,
+ "peer": true
},
"@types/chai-subset": {
"version": "1.3.6",
@@ -7978,6 +7984,7 @@
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
"dev": true,
+ "peer": true,
"requires": {
"csstype": "^3.2.2"
}
@@ -7987,6 +7994,7 @@
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"dev": true,
+ "peer": true,
"requires": {}
},
"@types/semver": {
@@ -8053,6 +8061,7 @@
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz",
"integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==",
"dev": true,
+ "peer": true,
"requires": {
"@typescript-eslint/scope-manager": "6.21.0",
"@typescript-eslint/types": "6.21.0",
@@ -8263,7 +8272,8 @@
"version": "8.16.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
- "dev": true
+ "dev": true,
+ "peer": true
},
"acorn-jsx": {
"version": "5.3.2",
@@ -8326,7 +8336,6 @@
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
"integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
"dev": true,
- "peer": true,
"requires": {
"dequal": "^2.0.3"
}
@@ -8396,6 +8405,7 @@
"version": "4.28.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
"integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
+ "peer": true,
"requires": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -8605,8 +8615,7 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
- "dev": true,
- "peer": true
+ "dev": true
},
"detect-libc": {
"version": "2.1.2",
@@ -8646,8 +8655,7 @@
"version": "0.5.16",
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
- "dev": true,
- "peer": true
+ "dev": true
},
"domexception": {
"version": "4.0.0",
@@ -8782,6 +8790,7 @@
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
"integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
"dev": true,
+ "peer": true,
"requires": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
@@ -9360,6 +9369,7 @@
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-22.1.0.tgz",
"integrity": "sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==",
"dev": true,
+ "peer": true,
"requires": {
"abab": "^2.0.6",
"cssstyle": "^3.0.0",
@@ -9566,8 +9576,7 @@
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
- "dev": true,
- "peer": true
+ "dev": true
},
"magic-string": {
"version": "0.30.21",
@@ -9813,7 +9822,8 @@
"picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "peer": true
},
"pkg-types": {
"version": "1.3.1",
@@ -9843,6 +9853,7 @@
"version": "8.5.8",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz",
"integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
+ "peer": true,
"requires": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@@ -9926,12 +9937,14 @@
"react": {
"version": "19.2.4",
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
- "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="
+ "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
+ "peer": true
},
"react-dom": {
"version": "19.2.4",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
+ "peer": true,
"requires": {
"scheduler": "^0.27.0"
}
@@ -10256,6 +10269,7 @@
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
"integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
"devOptional": true,
+ "peer": true,
"requires": {
"esbuild": "~0.27.0",
"fsevents": "~2.3.3",
@@ -10287,7 +10301,8 @@
"version": "5.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
- "dev": true
+ "dev": true,
+ "peer": true
},
"ufo": {
"version": "1.6.3",
@@ -10339,6 +10354,7 @@
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz",
"integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
+ "peer": true,
"requires": {
"esbuild": "^0.25.0",
"fdir": "^6.4.4",
diff --git a/package.json b/package.json
index 325149e9..6ddc5166 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,8 @@
"dev:web": "node scripts/dev.mjs web",
"dev:admin-web": "node scripts/dev.mjs admin-web",
"dev:spacetime:logs": "node scripts/run-bash-script.mjs scripts/spacetime-logs-local.sh",
+ "otel:debug": "node scripts/run-otelcol.mjs debug",
+ "otel:rider": "node scripts/run-otelcol.mjs rider",
"admin-web:build": "node scripts/admin-web-build.mjs build",
"admin-web:typecheck": "node scripts/admin-web-build.mjs typecheck",
"admin-web:preview": "npm --prefix apps/admin-web run preview --",
diff --git a/scripts/loadtest/README.md b/scripts/loadtest/README.md
index 0b406675..be788df5 100644
--- a/scripts/loadtest/README.md
+++ b/scripts/loadtest/README.md
@@ -113,6 +113,17 @@ $env:WORKS_DATA="data/works-list.local.json"
npm run loadtest:k6:works -- --summary-trend-stats="avg,min,med,p(90),p(95),p(99),max"
```
+## 50 HTTP req/s 口径
+
+`k6-works-list.js` 默认一次 iteration 会依次请求两个公开列表接口:`/api/runtime/puzzle/gallery` 和 `/api/runtime/custom-world-gallery`。因此目标约 50 HTTP req/s 时,`ramping-arrival-rate` 的 `PEAK_RPS` 应设置为 `25`。如果传入 `AUTH_TOKEN` 或把 `DETAIL_RATIO` 设为大于 0,每次 iteration 的请求数会增加,需要重新折算。
+
+验收目标:
+
+- `http_req_failed < 1%`
+- `http_req_duration p95 < 2000ms`
+- `dropped_iterations = 0`
+- 压测窗口内 Nginx 无新增 502
+
## Smoke
```bash
@@ -151,17 +162,38 @@ BASE_URL=http://127.0.0.1:8787 \
WORKS_DATA=data/works-list.local.json \
SCENARIO=spike \
START_RPS=5 \
-PEAK_RPS=100 \
-HOLD=2m \
+PEAK_RPS=25 \
+HOLD=60s \
DETAIL_RATIO=0 \
npm run loadtest:k6:works
```
默认阈值:
-- `http_req_failed < 5%`
+- `http_req_failed < 1%`
- `http_req_duration p95 < 2000ms`
-- `works_list_shape_error_rate < 5%`
+- `dropped_iterations = 0`
+- `works_list_shape_error_rate < 1%`
+
+PowerShell:
+
+```powershell
+$env:BASE_URL="https://genarrative.world"
+$env:WORKS_DATA="data/works-list.local.json"
+$env:SCENARIO="spike"
+$env:START_RPS="5"
+$env:PEAK_RPS="25"
+$env:HOLD="60s"
+$env:END_RPS="5"
+$env:DETAIL_RATIO="0"
+npm run loadtest:k6:works -- --summary-trend-stats="avg,min,med,p(90),p(95),p(99),max"
+```
+
+线上 release 回归可使用同一组环境变量:
+
+```bash
+SCENARIO=spike START_RPS=5 PEAK_RPS=25 HOLD=60s END_RPS=5 DETAIL_RATIO=0 npm run loadtest:k6:works
+```
## 带登录态压测个人作品列表
@@ -197,6 +229,96 @@ npm run loadtest:k6:works
- 如果个人作品列表返回 401,确认 `AUTH_TOKEN` 是当前 api-server 可识别的 access token。
- 如果详情全部 404,确认是否已向目标环境导入与 `WORKS_DATA` 一致的数据。
+## 压测窗口采集
+
+Nginx upstream timing:
+
+```bash
+sudo tail -f /var/log/nginx/genarrative.access.log
+sudo tail -f /var/log/nginx/genarrative.error.log
+```
+
+api-server 与 SpacetimeDB 日志:
+
+```bash
+sudo journalctl -u genarrative-api.service -f
+sudo journalctl -u spacetimedb.service -f
+```
+
+api-server 的 OpenTelemetry 默认关闭。需要验证 OTLP traces / metrics / logs 时,先在服务器本机启动只监听 `127.0.0.1` 的 `otelcol-contrib` debug exporter:
+
+```bash
+npm run otel:debug
+```
+
+如果要把本机数据转发给 Rider OpenTelemetry 面板,先在 Rider 的 OpenTelemetry 设置中启用固定 OTLP server port,例如 `17011`,再运行:
+
+```bash
+RIDER_OTLP_GRPC_ENDPOINT=127.0.0.1:17011 npm run otel:rider
+```
+
+脚本会在 `.codex-temp/otelcol/` 生成临时 collector 配置,默认接收 api-server 发到 `http://127.0.0.1:4318` 的 OTLP HTTP 数据。需要改端口时可设置:
+
+- `OTELCOL_OTLP_HTTP_ENDPOINT`,默认 `127.0.0.1:4318`
+- `OTELCOL_OTLP_GRPC_ENDPOINT`,默认 `127.0.0.1:4317`
+- `RIDER_OTLP_GRPC_ENDPOINT`,默认 `127.0.0.1:17011`
+- `OTELCOL_BIN`,默认 `otelcol-contrib`
+
+等价的 debug collector 配置如下:
+
+```yaml
+receivers:
+ otlp:
+ protocols:
+ grpc:
+ endpoint: 127.0.0.1:4317
+ http:
+ endpoint: 127.0.0.1:4318
+
+exporters:
+ debug:
+ verbosity: detailed
+
+service:
+ pipelines:
+ traces:
+ receivers: [otlp]
+ exporters: [debug]
+ metrics:
+ receivers: [otlp]
+ exporters: [debug]
+ logs:
+ receivers: [otlp]
+ exporters: [debug]
+```
+
+```bash
+otelcol-contrib --config /etc/otelcol-contrib/genarrative-debug.yaml
+```
+
+然后在 `/etc/genarrative/api-server.env` 中打开:
+
+```env
+GENARRATIVE_OTEL_ENABLED=true
+OTEL_SERVICE_NAME=genarrative-api
+OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4318
+```
+
+注意 `api-server` 当前使用 OTLP HTTP exporter,`OTEL_EXPORTER_OTLP_ENDPOINT` 必须指向 Collector 的 HTTP base endpoint `http://127.0.0.1:4318`。不要把它改成 Collector gRPC 端口 `4317`,也不要直接指向 Rider 的 gRPC 端口;Rider 只由 `npm run otel:rider` 启动的 Collector 通过 `RIDER_OTLP_GRPC_ENDPOINT` 转发。
+
+OTLP logs 是远端观测增量,不替代本地日志;api-server 日志仍看 `journalctl` / `logs/api-server/`,Nginx 日志仍看文件。日志等级继续用 `GENARRATIVE_API_LOG` / `RUST_LOG` 控制,例如 `info,tower_http=info,spacetime_client=info`。
+
+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 关联查看。
+
+线上回归辅助命令:
+
+```bash
+systemctl show genarrative-api.service -p LimitNOFILE -p TasksMax
+cat /proc/$(pidof api-server)/limits
+ss -ltnp | grep 8082
+curl -sS http://127.0.0.1:8082/healthz
+```
+
## 验证命令
```bash
diff --git a/scripts/loadtest/k6-works-list.js b/scripts/loadtest/k6-works-list.js
index 45e51a82..67d6abd0 100644
--- a/scripts/loadtest/k6-works-list.js
+++ b/scripts/loadtest/k6-works-list.js
@@ -56,20 +56,22 @@ const scenarioOptions = {
scenarios: {
spike: {
executor: 'ramping-arrival-rate',
+ startRate: Number(__ENV.START_RPS || 5),
preAllocatedVUs: Number(__ENV.PREALLOCATED_VUS || 50),
maxVUs: Number(__ENV.MAX_VUS || 200),
timeUnit: '1s',
stages: [
- { target: Number(__ENV.START_RPS || 5), duration: __ENV.RAMP_UP || '30s' },
- { target: Number(__ENV.PEAK_RPS || 100), duration: __ENV.HOLD || '2m' },
+ { target: Number(__ENV.PEAK_RPS || 25), duration: __ENV.RAMP_UP || '30s' },
+ { target: Number(__ENV.PEAK_RPS || 25), duration: __ENV.HOLD || '2m' },
{ target: Number(__ENV.END_RPS || 5), duration: __ENV.RAMP_DOWN || '30s' },
],
},
},
thresholds: {
- http_req_failed: ['rate<0.05'],
+ http_req_failed: ['rate<0.01'],
http_req_duration: ['p(95)<2000'],
- works_list_shape_error_rate: ['rate<0.05'],
+ dropped_iterations: ['count==0'],
+ works_list_shape_error_rate: ['rate<0.01'],
},
},
};
diff --git a/scripts/run-otelcol.mjs b/scripts/run-otelcol.mjs
new file mode 100644
index 00000000..d070bfdc
--- /dev/null
+++ b/scripts/run-otelcol.mjs
@@ -0,0 +1,119 @@
+import {spawn} from 'node:child_process';
+import {mkdirSync, writeFileSync} from 'node:fs';
+import path from 'node:path';
+
+const [, , rawMode = 'debug', ...args] = process.argv;
+const mode = rawMode.trim();
+const printConfigOnly = args.includes('--print-config');
+
+const supportedModes = new Set(['debug', 'rider']);
+if (!supportedModes.has(mode)) {
+ console.error('[otelcol] mode must be one of: debug, rider');
+ process.exit(1);
+}
+
+const otlpHttpEndpoint = readEnv('OTELCOL_OTLP_HTTP_ENDPOINT', '127.0.0.1:4318');
+const otlpGrpcEndpoint = readEnv('OTELCOL_OTLP_GRPC_ENDPOINT', '127.0.0.1:4317');
+const riderEndpoint = readEnv('RIDER_OTLP_GRPC_ENDPOINT', '127.0.0.1:17011');
+const debugVerbosity = readEnv('OTELCOL_DEBUG_VERBOSITY', 'detailed');
+const otelcolBin = readEnv('OTELCOL_BIN', 'otelcol-contrib');
+
+const configText = buildConfig(mode);
+const configDir = path.resolve('.codex-temp', 'otelcol');
+const configPath = path.join(configDir, `genarrative-${mode}.yaml`);
+mkdirSync(configDir, {recursive: true});
+writeFileSync(configPath, configText, 'utf8');
+
+console.log(`[otelcol] wrote ${configPath}`);
+console.log(`[otelcol] receiving OTLP HTTP at http://${otlpHttpEndpoint}`);
+console.log(`[otelcol] receiving OTLP gRPC at ${otlpGrpcEndpoint}`);
+if (mode === 'rider') {
+ console.log(`[otelcol] forwarding traces/metrics/logs to Rider OTLP gRPC at ${riderEndpoint}`);
+}
+console.log(
+ '[otelcol] api-server env: GENARRATIVE_OTEL_ENABLED=true OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4318'
+);
+
+if (printConfigOnly) {
+ console.log(configText);
+ process.exit(0);
+}
+
+const child = spawn(otelcolBin, ['--config', configPath], {
+ cwd: process.cwd(),
+ env: process.env,
+ stdio: 'inherit',
+});
+
+const stopChild = () => {
+ if (!child.killed) {
+ child.kill();
+ }
+};
+
+for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
+ process.on(signal, () => {
+ stopChild();
+ process.exit(130);
+ });
+}
+
+process.on('exit', stopChild);
+
+child.on('error', (error) => {
+ console.error(`[otelcol] failed to start ${otelcolBin}: ${error.message}`);
+ console.error('[otelcol] install otelcol-contrib and make sure it is on PATH, or set OTELCOL_BIN.');
+ process.exit(1);
+});
+
+child.on('exit', (code, signal) => {
+ if (signal) {
+ console.error(`[otelcol] exited by signal: ${signal}`);
+ process.exit(1);
+ }
+ process.exit(code ?? 0);
+});
+
+function readEnv(key, fallback) {
+ const value = process.env[key]?.trim();
+ return value ? value : fallback;
+}
+
+function buildConfig(selectedMode) {
+ const exporters =
+ selectedMode === 'rider'
+ ? ` otlp/rider:
+ endpoint: ${riderEndpoint}
+ tls:
+ insecure: true
+ debug:
+ verbosity: ${debugVerbosity}`
+ : ` debug:
+ verbosity: ${debugVerbosity}`;
+
+ const pipelineExporters = selectedMode === 'rider' ? '[otlp/rider, debug]' : '[debug]';
+
+ return `receivers:
+ otlp:
+ protocols:
+ grpc:
+ endpoint: ${otlpGrpcEndpoint}
+ http:
+ endpoint: ${otlpHttpEndpoint}
+
+exporters:
+${exporters}
+
+service:
+ pipelines:
+ traces:
+ receivers: [otlp]
+ exporters: ${pipelineExporters}
+ metrics:
+ receivers: [otlp]
+ exporters: ${pipelineExporters}
+ logs:
+ receivers: [otlp]
+ exporters: ${pipelineExporters}
+`;
+}
diff --git a/server-rs/Cargo.lock b/server-rs/Cargo.lock
index 74415c0e..0eb3d4a3 100644
--- a/server-rs/Cargo.lock
+++ b/server-rs/Cargo.lock
@@ -105,6 +105,7 @@ dependencies = [
"module-square-hole",
"module-story",
"module-visual-novel",
+ "opentelemetry",
"platform-agent",
"platform-auth",
"platform-llm",
@@ -118,6 +119,7 @@ dependencies = [
"shared-contracts",
"shared-kernel",
"shared-logging",
+ "socket2 0.6.3",
"spacetime-client",
"time",
"tokio",
@@ -2070,6 +2072,90 @@ dependencies = [
"vcpkg",
]
+[[package]]
+name = "opentelemetry"
+version = "0.31.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b84bcd6ae87133e903af7ef497404dda70c60d0ea14895fc8a5e6722754fc2a0"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+ "js-sys",
+ "pin-project-lite",
+ "thiserror 2.0.18",
+ "tracing",
+]
+
+[[package]]
+name = "opentelemetry-appender-tracing"
+version = "0.31.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef6a1ac5ca3accf562b8c306fa8483c85f4390f768185ab775f242f7fe8fdcc2"
+dependencies = [
+ "opentelemetry",
+ "tracing",
+ "tracing-core",
+ "tracing-opentelemetry",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "opentelemetry-http"
+version = "0.31.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7a6d09a73194e6b66df7c8f1b680f156d916a1a942abf2de06823dd02b7855d"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "http 1.4.0",
+ "opentelemetry",
+ "reqwest 0.12.28",
+]
+
+[[package]]
+name = "opentelemetry-otlp"
+version = "0.31.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f69cd6acbb9af919df949cd1ec9e5e7fdc2ef15d234b6b795aaa525cc02f71f"
+dependencies = [
+ "http 1.4.0",
+ "opentelemetry",
+ "opentelemetry-http",
+ "opentelemetry-proto",
+ "opentelemetry_sdk",
+ "prost",
+ "reqwest 0.12.28",
+ "thiserror 2.0.18",
+]
+
+[[package]]
+name = "opentelemetry-proto"
+version = "0.31.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7175df06de5eaee9909d4805a3d07e28bb752c34cab57fa9cff549da596b30f"
+dependencies = [
+ "opentelemetry",
+ "opentelemetry_sdk",
+ "prost",
+ "tonic",
+ "tonic-prost",
+]
+
+[[package]]
+name = "opentelemetry_sdk"
+version = "0.31.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e14ae4f5991976fd48df6d843de219ca6d31b01daaab2dad5af2badeded372bd"
+dependencies = [
+ "futures-channel",
+ "futures-executor",
+ "futures-util",
+ "opentelemetry",
+ "percent-encoding",
+ "rand 0.9.4",
+ "thiserror 2.0.18",
+]
+
[[package]]
name = "parking_lot"
version = "0.12.5"
@@ -2151,6 +2237,26 @@ dependencies = [
"indexmap 2.14.0",
]
+[[package]]
+name = "pin-project"
+version = "1.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "pin-project-lite"
version = "0.2.17"
@@ -2320,6 +2426,29 @@ dependencies = [
"thiserror 2.0.18",
]
+[[package]]
+name = "prost"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568"
+dependencies = [
+ "bytes",
+ "prost-derive",
+]
+
+[[package]]
+name = "prost-derive"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b"
+dependencies = [
+ "anyhow",
+ "itertools",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "protobuf"
version = "3.7.2"
@@ -2622,6 +2751,7 @@ checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
dependencies = [
"base64 0.22.1",
"bytes",
+ "futures-channel",
"futures-core",
"futures-util",
"http 1.4.0",
@@ -3036,6 +3166,12 @@ dependencies = [
name = "shared-logging"
version = "0.1.0"
dependencies = [
+ "opentelemetry",
+ "opentelemetry-appender-tracing",
+ "opentelemetry-otlp",
+ "opentelemetry_sdk",
+ "tracing",
+ "tracing-opentelemetry",
"tracing-subscriber",
]
@@ -3130,6 +3266,7 @@ dependencies = [
"module-square-hole",
"module-story",
"module-visual-novel",
+ "opentelemetry",
"serde",
"serde_json",
"shared-contracts",
@@ -3137,6 +3274,7 @@ dependencies = [
"spacetimedb-sdk",
"time",
"tokio",
+ "tracing",
]
[[package]]
@@ -3807,6 +3945,38 @@ version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db"
+[[package]]
+name = "tonic"
+version = "0.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef"
+dependencies = [
+ "async-trait",
+ "base64 0.22.1",
+ "bytes",
+ "http 1.4.0",
+ "http-body 1.0.1",
+ "http-body-util",
+ "percent-encoding",
+ "pin-project",
+ "sync_wrapper 1.0.2",
+ "tokio-stream",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tonic-prost"
+version = "0.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50849f68853be452acf590cde0b146665b8d507b3b8af17261df47e02c209ea0"
+dependencies = [
+ "bytes",
+ "prost",
+ "tonic",
+]
+
[[package]]
name = "tower"
version = "0.5.3"
@@ -3898,6 +4068,22 @@ dependencies = [
"tracing-core",
]
+[[package]]
+name = "tracing-opentelemetry"
+version = "0.32.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ac28f2d093c6c477eaa76b23525478f38de514fa9aeb1285738d4b97a9552fc"
+dependencies = [
+ "js-sys",
+ "opentelemetry",
+ "smallvec",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+ "tracing-subscriber",
+ "web-time",
+]
+
[[package]]
name = "tracing-subscriber"
version = "0.3.23"
diff --git a/server-rs/Cargo.toml b/server-rs/Cargo.toml
index 3a6ea980..6500ac2f 100644
--- a/server-rs/Cargo.toml
+++ b/server-rs/Cargo.toml
@@ -100,6 +100,7 @@ serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_urlencoded = "0.7"
sha2 = "0.10"
+socket2 = "0.6"
spacetimedb = "2.2.0"
spacetimedb-sdk = "2.2.0"
spacetimedb-lib = { version = "2.2.0", default-features = false }
@@ -110,6 +111,11 @@ tokio-tungstenite = "0.27"
tower = "0.5"
tower-http = "0.6"
tracing = "0.1"
+opentelemetry = "0.31"
+opentelemetry-appender-tracing = { version = "0.31", default-features = false, features = ["experimental_use_tracing_span_context"] }
+opentelemetry-otlp = { version = "0.31", default-features = false, features = ["http-proto", "reqwest-blocking-client", "trace", "metrics", "logs"] }
+opentelemetry_sdk = { version = "0.31", default-features = false, features = ["trace", "metrics", "logs"] }
+tracing-opentelemetry = { version = "0.32", default-features = false }
tracing-subscriber = "0.3"
url = "2"
urlencoding = "2"
diff --git a/server-rs/crates/api-server/Cargo.toml b/server-rs/crates/api-server/Cargo.toml
index 90ab2c7b..92b07599 100644
--- a/server-rs/crates/api-server/Cargo.toml
+++ b/server-rs/crates/api-server/Cargo.toml
@@ -43,6 +43,7 @@ sha2 = { workspace = true }
shared-contracts = { workspace = true, features = ["oss-contracts"] }
shared-kernel = { workspace = true }
shared-logging = { workspace = true }
+socket2 = { workspace = true }
spacetime-client = { workspace = true }
tokio = { workspace = true, features = ["macros", "rt-multi-thread", "net", "time"] }
tokio-stream = { workspace = true }
@@ -50,6 +51,7 @@ futures-util = { workspace = true }
time = { workspace = true, features = ["formatting"] }
tower-http = { workspace = true, features = ["trace"] }
tracing = { workspace = true }
+opentelemetry = { workspace = true }
url = { workspace = true }
urlencoding = { workspace = true }
uuid = { workspace = true, features = ["v4"] }
diff --git a/server-rs/crates/api-server/src/app.rs b/server-rs/crates/api-server/src/app.rs
index 17956263..70cda406 100644
--- a/server-rs/crates/api-server/src/app.rs
+++ b/server-rs/crates/api-server/src/app.rs
@@ -11,7 +11,7 @@ use tower_http::{
classify::ServerErrorsFailureClass,
trace::{DefaultOnRequest, TraceLayer},
};
-use tracing::{Level, Span, error, info, info_span, warn};
+use tracing::{Level, Span, error, info_span};
use crate::{
auth::{AuthenticatedAccessToken, require_bearer_auth},
@@ -22,6 +22,7 @@ use crate::{
response_headers::propagate_request_id_header,
runtime_inventory::get_runtime_inventory_state,
state::AppState,
+ telemetry::record_http_observability,
tracking::record_route_tracking_event_after_success,
vector_engine_audio_generation::{
create_background_music_task, create_sound_effect_task,
@@ -42,8 +43,6 @@ use crate::{
// 统一由这里构造 Axum 路由树,后续再逐项挂接中间件与业务路由。
pub fn build_router(state: AppState) -> Router {
- let slow_request_threshold_ms = state.config.slow_request_threshold_ms;
-
Router::new()
.merge(modules::admin::router(state.clone()))
.merge(modules::health::router(state.clone()))
@@ -86,47 +85,55 @@ pub fn build_router(state: AppState) -> Router {
state.clone(),
record_api_tracking_after_success,
))
+ // HTTP 指标与请求完成日志放在 tracing span 内侧,日志事件可以继承当前 trace/span context。
+ .layer(middleware::from_fn_with_state(
+ state.clone(),
+ record_http_observability,
+ ))
// 当前阶段先统一挂接 HTTP tracing,后续 request_id、响应头与错误中间件继续在这里扩展。
.layer(
TraceLayer::new_for_http()
.make_span_with(|request: &Request
| {
let request_id =
resolve_request_id(request).unwrap_or_else(|| "unknown".to_string());
+ let route = crate::telemetry::observability_route(request.uri().path());
+ let scheme = crate::telemetry::resolve_request_scheme(request.headers());
+ let span_name = format!("{} {}", request.method(), route);
info_span!(
"http.request",
+ otel.kind = "server",
+ otel.name = %span_name,
+ otel.status_code = tracing::field::Empty,
+ http.response.status_code = tracing::field::Empty,
method = %request.method(),
- uri = %request.uri(),
+ http.request.method = %request.method(),
+ http.route = %route,
+ url.scheme = %scheme,
+ url.path = %request.uri().path(),
request_id = %request_id,
+ status = tracing::field::Empty,
+ latency_ms = tracing::field::Empty,
)
})
.on_request(DefaultOnRequest::new().level(Level::INFO))
.on_response(
- move |response: &axum::response::Response,
- latency: std::time::Duration,
- span: &Span| {
+ |response: &axum::response::Response,
+ latency: std::time::Duration,
+ span: &Span| {
let latency_ms = latency.as_millis().min(u64::MAX as u128) as u64;
let status = response.status().as_u16();
- let slow_request = latency_ms >= slow_request_threshold_ms;
span.record("status", status);
+ span.record("http.response.status_code", status);
+ span.record(
+ "otel.status_code",
+ if response.status().is_server_error() {
+ "ERROR"
+ } else {
+ "OK"
+ },
+ );
span.record("latency_ms", latency_ms);
- if slow_request {
- warn!(
- parent: span,
- status,
- latency_ms,
- slow_request = true,
- "http request completed slowly"
- );
- } else {
- info!(
- parent: span,
- status,
- latency_ms,
- slow_request = false,
- "http request completed"
- );
- }
},
)
.on_failure(
diff --git a/server-rs/crates/api-server/src/assets.rs b/server-rs/crates/api-server/src/assets.rs
index 8b3afd6b..33d46ae5 100644
--- a/server-rs/crates/api-server/src/assets.rs
+++ b/server-rs/crates/api-server/src/assets.rs
@@ -752,10 +752,14 @@ mod tests {
};
use hmac::{Hmac, Mac};
use http_body_util::BodyExt;
+ use platform_auth::{
+ AccessTokenClaims, AccessTokenClaimsInput, AuthProvider, BindingStatus, sign_access_token,
+ };
use reqwest::{Method, multipart};
use serde_json::{Value, json};
use sha2::{Digest, Sha256};
use shared_kernel::new_uuid_simple_string;
+ use time::OffsetDateTime;
use tower::ServiceExt;
use crate::{app::build_router, config::AppConfig, state::AppState};
@@ -873,13 +877,17 @@ mod tests {
..AppConfig::default()
};
- let app = build_router(AppState::new(config).expect("state should build"));
+ let state = AppState::new(config).expect("state should build");
+ let token =
+ seed_authenticated_token(&state, "13800138120", "sess_assets_direct_upload").await;
+ let app = build_router(state);
let response = app
.oneshot(
Request::builder()
.method("POST")
.uri("/api/assets/direct-upload-tickets")
+ .header("authorization", format!("Bearer {token}"))
.header("content-type", "application/json")
.header("x-request-id", "req-oss-ticket")
.header("x-genarrative-response-envelope", "1")
@@ -1693,6 +1701,33 @@ mod tests {
Ok(fields)
}
+ async fn seed_authenticated_token(
+ state: &AppState,
+ phone_number: &str,
+ session_seed: &str,
+ ) -> String {
+ let user = state
+ .seed_test_phone_user_with_password(phone_number, "secret123")
+ .await;
+ let claims = AccessTokenClaims::from_input(
+ AccessTokenClaimsInput {
+ user_id: user.id.clone(),
+ session_id: state.seed_test_refresh_session_for_user(&user, session_seed),
+ provider: AuthProvider::Password,
+ roles: vec!["user".to_string()],
+ token_version: user.token_version,
+ phone_verified: true,
+ binding_status: BindingStatus::Active,
+ display_name: Some(user.display_name.clone()),
+ },
+ state.auth_jwt_config(),
+ OffsetDateTime::now_utc(),
+ )
+ .expect("claims should build");
+
+ sign_access_token(&claims, state.auth_jwt_config()).expect("token should sign")
+ }
+
fn build_object_url(
config: &AppConfig,
object_key: &str,
diff --git a/server-rs/crates/api-server/src/config.rs b/server-rs/crates/api-server/src/config.rs
index b8af62a4..955664de 100644
--- a/server-rs/crates/api-server/src/config.rs
+++ b/server-rs/crates/api-server/src/config.rs
@@ -20,7 +20,10 @@ pub(crate) const DEFAULT_VECTOR_ENGINE_IMAGE_REQUEST_TIMEOUT_MS: u64 = 1_000_000
pub struct AppConfig {
pub bind_host: String,
pub bind_port: u16,
+ pub listen_backlog: i32,
+ pub worker_threads: Option,
pub log_filter: String,
+ pub otel_enabled: bool,
pub admin_username: Option,
pub admin_password: Option,
pub admin_token_ttl_seconds: u64,
@@ -147,7 +150,10 @@ impl Default for AppConfig {
Self {
bind_host: "127.0.0.1".to_string(),
bind_port: 3000,
+ listen_backlog: 1024,
+ worker_threads: None,
log_filter: "info,tower_http=info".to_string(),
+ otel_enabled: false,
admin_username: None,
admin_password: None,
admin_token_ttl_seconds: 4 * 60 * 60,
@@ -301,6 +307,17 @@ impl AppConfig {
{
config.log_filter = log_filter;
}
+ if let Some(listen_backlog) =
+ read_first_positive_i32_env(&["GENARRATIVE_API_LISTEN_BACKLOG"])
+ {
+ config.listen_backlog = listen_backlog;
+ }
+ if let Some(worker_threads) = read_first_usize_env(&["GENARRATIVE_API_WORKER_THREADS"]) {
+ config.worker_threads = Some(worker_threads);
+ }
+ if let Some(otel_enabled) = read_first_bool_env(&["GENARRATIVE_OTEL_ENABLED"]) {
+ config.otel_enabled = otel_enabled;
+ }
config.admin_username = read_first_non_empty_env(&["GENARRATIVE_ADMIN_USERNAME"]);
config.admin_password = read_first_non_empty_env(&["GENARRATIVE_ADMIN_PASSWORD"]);
@@ -881,6 +898,14 @@ fn read_first_positive_u32_env(keys: &[&str]) -> Option {
})
}
+fn read_first_positive_i32_env(keys: &[&str]) -> Option {
+ keys.iter().find_map(|key| {
+ env::var(key)
+ .ok()
+ .and_then(|value| parse_positive_i32(&value))
+ })
+}
+
fn read_first_positive_u64_env(keys: &[&str]) -> Option {
keys.iter().find_map(|key| {
env::var(key)
@@ -971,6 +996,15 @@ fn parse_positive_u32(raw: &str) -> Option {
Some(value)
}
+fn parse_positive_i32(raw: &str) -> Option {
+ let value = raw.trim().parse::().ok()?;
+ if value <= 0 {
+ return None;
+ }
+
+ Some(value)
+}
+
fn parse_u32(raw: &str) -> Option {
raw.trim().parse::().ok()
}
@@ -1151,6 +1185,34 @@ mod tests {
}
}
+ #[test]
+ fn from_env_reads_api_runtime_performance_settings() {
+ let _guard = ENV_LOCK
+ .get_or_init(|| Mutex::new(()))
+ .lock()
+ .expect("env lock should not poison");
+
+ unsafe {
+ std::env::remove_var("GENARRATIVE_API_LISTEN_BACKLOG");
+ std::env::remove_var("GENARRATIVE_API_WORKER_THREADS");
+ std::env::remove_var("GENARRATIVE_OTEL_ENABLED");
+ std::env::set_var("GENARRATIVE_API_LISTEN_BACKLOG", "2048");
+ std::env::set_var("GENARRATIVE_API_WORKER_THREADS", "6");
+ std::env::set_var("GENARRATIVE_OTEL_ENABLED", "true");
+ }
+
+ let config = AppConfig::from_env();
+ assert_eq!(config.listen_backlog, 2048);
+ assert_eq!(config.worker_threads, Some(6));
+ assert!(config.otel_enabled);
+
+ unsafe {
+ std::env::remove_var("GENARRATIVE_API_LISTEN_BACKLOG");
+ std::env::remove_var("GENARRATIVE_API_WORKER_THREADS");
+ std::env::remove_var("GENARRATIVE_OTEL_ENABLED");
+ }
+ }
+
#[test]
fn from_env_reads_wechat_pay_settings() {
let _guard = ENV_LOCK
diff --git a/server-rs/crates/api-server/src/main.rs b/server-rs/crates/api-server/src/main.rs
index db6d0d28..d1f15cd9 100644
--- a/server-rs/crates/api-server/src/main.rs
+++ b/server-rs/crates/api-server/src/main.rs
@@ -75,6 +75,7 @@ mod square_hole_agent_turn;
mod state;
mod story_battles;
mod story_sessions;
+mod telemetry;
mod tracking;
mod vector_engine_audio_generation;
mod visual_novel;
@@ -85,8 +86,15 @@ mod wechat_provider;
mod work_author;
mod work_play_tracking;
-use shared_logging::init_tracing;
-use std::{collections::HashSet, env, fs, io, panic, thread, time::Duration};
+use shared_logging::{OtelConfig, init_tracing};
+use socket2::{Domain, Protocol, Socket, Type};
+use std::{
+ collections::HashSet,
+ env, fs, io,
+ net::{SocketAddr, TcpListener as StdTcpListener},
+ panic, thread,
+ time::Duration,
+};
use tokio::net::TcpListener;
use tokio::runtime::Builder as TokioRuntimeBuilder;
use tokio::time::timeout;
@@ -103,12 +111,18 @@ fn main() -> Result<(), io::Error> {
.name("api-server-bootstrap".to_string())
.stack_size(API_SERVER_STARTUP_STACK_SIZE_BYTES)
.spawn(|| {
- TokioRuntimeBuilder::new_multi_thread()
+ load_local_env_files();
+ let config = AppConfig::from_env();
+ let mut runtime_builder = TokioRuntimeBuilder::new_multi_thread();
+ runtime_builder
.enable_all()
.thread_name("api-server-worker")
- .thread_stack_size(API_SERVER_STARTUP_STACK_SIZE_BYTES)
- .build()?
- .block_on(run_server())
+ .thread_stack_size(API_SERVER_STARTUP_STACK_SIZE_BYTES);
+ if let Some(worker_threads) = config.worker_threads {
+ runtime_builder.worker_threads(worker_threads);
+ }
+
+ runtime_builder.build()?.block_on(run_server(config))
})?;
match server_thread.join() {
@@ -117,28 +131,49 @@ fn main() -> Result<(), io::Error> {
}
}
-async fn run_server() -> Result<(), io::Error> {
- // 运行本地开发与联调时,优先从仓库根目录加载本地变量。
- // 只尊重外层 shell 先注入的变量;后续本地文件需要能覆盖前序本地文件。
- load_local_env_files();
-
- // 统一先从配置对象读取监听地址,避免后续把环境变量读取散落到入口和路由层。
- let config = AppConfig::from_env();
- init_tracing(&config.log_filter)?;
+async fn run_server(config: AppConfig) -> Result<(), io::Error> {
+ init_tracing(
+ &config.log_filter,
+ OtelConfig {
+ enabled: config.otel_enabled,
+ },
+ )?;
let bind_address = config.bind_socket_addr();
- let listener = TcpListener::bind(bind_address).await?;
+ let listen_backlog = config.listen_backlog;
+ let worker_threads = config.worker_threads;
+ let otel_enabled = config.otel_enabled;
+ let listener = build_tcp_listener(bind_address, listen_backlog)?;
let state = restore_app_state_for_startup(config)
.await
.map_err(|error| std::io::Error::other(format!("初始化应用状态失败:{error}")))?;
let router = build_router(state);
- info!(%bind_address, "api-server 已完成 tracing 初始化并开始监听");
+ info!(
+ %bind_address,
+ listen_backlog,
+ worker_threads = worker_threads.unwrap_or(0),
+ otel_enabled,
+ "api-server 已完成 tracing 初始化并开始监听"
+ );
axum::serve(listener, router).await
}
+fn build_tcp_listener(
+ bind_address: SocketAddr,
+ listen_backlog: i32,
+) -> Result {
+ let domain = Domain::for_address(bind_address);
+ let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))?;
+ socket.set_reuse_address(true)?;
+ socket.set_nonblocking(true)?;
+ socket.bind(&bind_address.into())?;
+ socket.listen(listen_backlog)?;
+ TcpListener::from_std(StdTcpListener::from(socket))
+}
+
async fn restore_app_state_for_startup(
config: AppConfig,
) -> Result {
diff --git a/server-rs/crates/api-server/src/telemetry.rs b/server-rs/crates/api-server/src/telemetry.rs
new file mode 100644
index 00000000..40347d8d
--- /dev/null
+++ b/server-rs/crates/api-server/src/telemetry.rs
@@ -0,0 +1,182 @@
+use axum::{
+ body::Body,
+ extract::State,
+ http::{HeaderMap, Request, Response},
+ middleware::Next,
+};
+use opentelemetry::{KeyValue, global, metrics::Counter};
+use tracing::{info, warn};
+
+use crate::{request_context::resolve_request_id, state::AppState};
+
+// 集中维护 api-server HTTP 观测,避免在 handler 中散落高基数字段或重复创建 instrument。
+pub async fn record_http_observability(
+ State(state): State,
+ request: Request,
+ next: Next,
+) -> Response {
+ let method = request.method().as_str().to_string();
+ let route = observability_route(request.uri().path());
+ let scheme = resolve_request_scheme(request.headers());
+ let path = request.uri().path().to_string();
+ let request_id = resolve_request_id(&request).unwrap_or_else(|| "unknown".to_string());
+ let base_labels = http_base_labels(method.clone(), route.clone());
+ let metrics = http_metrics();
+ metrics.in_flight.add(1, &base_labels);
+ let started_at = std::time::Instant::now();
+
+ let response = next.run(request).await;
+ let status = response.status().as_u16();
+ let status_class = status_class(status);
+ let latency_ms = started_at.elapsed().as_millis().min(u64::MAX as u128) as u64;
+ let slow_request = latency_ms >= state.config.slow_request_threshold_ms;
+ let labels = http_response_labels(base_labels, status);
+ metrics.requests.add(1, &labels);
+ metrics
+ .duration
+ .record(started_at.elapsed().as_secs_f64(), &labels);
+ metrics.in_flight.add(-1, &labels[..2]);
+
+ if slow_request {
+ warn!(
+ request_id = %request_id,
+ http.request.method = %method,
+ http.route = %route,
+ url.scheme = %scheme,
+ url.path = %path,
+ http.response.status_code = status,
+ status,
+ status_class,
+ latency_ms,
+ slow_request = true,
+ "http request completed slowly"
+ );
+ } else {
+ info!(
+ request_id = %request_id,
+ http.request.method = %method,
+ http.route = %route,
+ url.scheme = %scheme,
+ url.path = %path,
+ http.response.status_code = status,
+ status,
+ status_class,
+ latency_ms,
+ slow_request = false,
+ "http request completed"
+ );
+ }
+
+ response
+}
+
+struct HttpMetrics {
+ requests: Counter,
+ in_flight: opentelemetry::metrics::UpDownCounter,
+ duration: opentelemetry::metrics::Histogram,
+}
+
+fn http_metrics() -> &'static HttpMetrics {
+ static METRICS: std::sync::OnceLock = std::sync::OnceLock::new();
+ METRICS.get_or_init(|| {
+ let meter = global::meter("genarrative-api");
+ HttpMetrics {
+ requests: meter
+ .u64_counter("genarrative.http.server.requests")
+ .with_description("HTTP request count grouped by route and status class")
+ .build(),
+ in_flight: meter
+ .i64_up_down_counter("http.server.active_requests")
+ .with_unit("{request}")
+ .with_description("Number of active HTTP server requests")
+ .build(),
+ duration: meter
+ .f64_histogram("http.server.request.duration")
+ .with_unit("s")
+ .with_description("Duration of HTTP server requests")
+ .build(),
+ }
+ })
+}
+
+fn http_base_labels(method: String, route: String) -> Vec {
+ vec![
+ KeyValue::new("http.request.method", method),
+ KeyValue::new("http.route", route),
+ ]
+}
+
+fn http_response_labels(mut labels: Vec, status: u16) -> Vec {
+ labels.push(KeyValue::new("status_class", status_class(status)));
+ labels
+}
+
+fn status_class(status: u16) -> &'static str {
+ match status {
+ 100..=199 => "1xx",
+ 200..=299 => "2xx",
+ 300..=399 => "3xx",
+ 400..=499 => "4xx",
+ 500..=599 => "5xx",
+ _ => "unknown",
+ }
+}
+
+pub(crate) fn observability_route(path: &str) -> String {
+ if path.starts_with("/api/runtime/puzzle/gallery") {
+ "/api/runtime/puzzle/gallery".to_string()
+ } else if path.starts_with("/api/runtime/custom-world-gallery") {
+ "/api/runtime/custom-world-gallery".to_string()
+ } else if path.starts_with("/admin/api/") {
+ "/admin/api/*".to_string()
+ } else if path.starts_with("/api/") {
+ "/api/*".to_string()
+ } else {
+ "other".to_string()
+ }
+}
+
+pub(crate) fn resolve_request_scheme(headers: &HeaderMap) -> String {
+ headers
+ .get("x-forwarded-proto")
+ .and_then(|value| value.to_str().ok())
+ .and_then(|value| value.split(',').next())
+ .map(str::trim)
+ .filter(|value| !value.is_empty())
+ .unwrap_or("http")
+ .to_string()
+}
+
+#[cfg(test)]
+mod tests {
+ use axum::http::{HeaderMap, HeaderValue};
+
+ use super::{observability_route, resolve_request_scheme};
+
+ #[test]
+ fn observability_route_keeps_metrics_labels_low_cardinality() {
+ assert_eq!(
+ observability_route("/api/runtime/puzzle/gallery?cursor=abc"),
+ "/api/runtime/puzzle/gallery"
+ );
+ assert_eq!(
+ observability_route("/api/runtime/puzzle/runs/run-123/history"),
+ "/api/*"
+ );
+ assert_eq!(
+ observability_route("/admin/api/debug/http"),
+ "/admin/api/*"
+ );
+ }
+
+ #[test]
+ fn resolve_request_scheme_uses_forwarded_proto_first_value() {
+ let mut headers = HeaderMap::new();
+ headers.insert(
+ "x-forwarded-proto",
+ HeaderValue::from_static("https, http"),
+ );
+
+ assert_eq!(resolve_request_scheme(&headers), "https");
+ }
+}
diff --git a/server-rs/crates/shared-logging/Cargo.toml b/server-rs/crates/shared-logging/Cargo.toml
index 75235916..e91655e9 100644
--- a/server-rs/crates/shared-logging/Cargo.toml
+++ b/server-rs/crates/shared-logging/Cargo.toml
@@ -5,4 +5,10 @@ version.workspace = true
license.workspace = true
[dependencies]
-tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] }
+opentelemetry = { workspace = true }
+opentelemetry-otlp = { workspace = true }
+opentelemetry-appender-tracing = { workspace = true }
+opentelemetry_sdk = { workspace = true }
+tracing = { workspace = true }
+tracing-opentelemetry = { workspace = true }
+tracing-subscriber = { workspace = true, features = ["env-filter", "fmt", "registry"] }
diff --git a/server-rs/crates/shared-logging/src/lib.rs b/server-rs/crates/shared-logging/src/lib.rs
index a810340e..ad77a6fb 100644
--- a/server-rs/crates/shared-logging/src/lib.rs
+++ b/server-rs/crates/shared-logging/src/lib.rs
@@ -1,6 +1,23 @@
use std::io;
-use tracing_subscriber::{EnvFilter, fmt};
+use opentelemetry::{KeyValue, global, trace::TracerProvider};
+use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
+use opentelemetry_otlp::WithExportConfig;
+use opentelemetry_sdk::{
+ Resource,
+ logs::SdkLoggerProvider,
+ metrics::SdkMeterProvider,
+ trace::SdkTracerProvider,
+};
+use tracing::warn;
+use tracing_subscriber::{
+ EnvFilter, Layer, filter::LevelFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt,
+};
+
+#[derive(Clone, Copy, Debug, Default)]
+pub struct OtelConfig {
+ pub enabled: bool,
+}
// 统一解析工作区日志过滤器,优先环境变量,其次回落到调用方传入的默认值。
pub fn resolve_env_filter(default_filter: &str) -> EnvFilter {
@@ -10,14 +27,196 @@ pub fn resolve_env_filter(default_filter: &str) -> EnvFilter {
}
// 统一初始化 tracing subscriber,避免各入口重复散落相同配置。
-pub fn init_tracing(default_filter: &str) -> Result<(), io::Error> {
+pub fn init_tracing(default_filter: &str, otel_config: OtelConfig) -> Result<(), io::Error> {
let env_filter = resolve_env_filter(default_filter);
+ let fmt_layer = fmt::layer().with_target(true).with_ansi(false).compact();
- fmt()
- .with_env_filter(env_filter)
- .with_target(true)
- .with_ansi(false)
- .compact()
+ if !otel_config.enabled {
+ return tracing_subscriber::registry()
+ .with(env_filter)
+ .with(fmt_layer)
+ .try_init()
+ .map_err(|error| io::Error::other(format!("初始化 tracing subscriber 失败:{error}")));
+ }
+
+ let Some(otel) = build_otel_pipeline() else {
+ return tracing_subscriber::registry()
+ .with(env_filter)
+ .with(fmt_layer)
+ .try_init()
+ .map_err(|error| io::Error::other(format!("初始化 tracing subscriber 失败:{error}")));
+ };
+
+ tracing_subscriber::registry()
+ .with(env_filter)
+ .with(fmt_layer)
+ .with(
+ tracing_opentelemetry::layer()
+ .with_tracer(otel.tracer_provider.tracer("genarrative-api")),
+ )
+ .with(
+ OpenTelemetryTracingBridge::new(&otel.logger_provider).with_filter(LevelFilter::INFO),
+ )
.try_init()
.map_err(|error| io::Error::other(format!("初始化 tracing subscriber 失败:{error}")))
}
+
+struct OtelPipeline {
+ tracer_provider: SdkTracerProvider,
+ _meter_provider: SdkMeterProvider,
+ logger_provider: SdkLoggerProvider,
+}
+
+fn build_otel_pipeline() -> Option {
+ let resource = Resource::builder()
+ .with_service_name(read_env_or_default("OTEL_SERVICE_NAME", "genarrative-api"))
+ .with_attribute(KeyValue::new("service.namespace", "genarrative"))
+ .build();
+
+ let span_exporter = match opentelemetry_otlp::SpanExporter::builder()
+ .with_http()
+ .with_endpoint(resolve_otlp_http_signal_endpoint(
+ "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT",
+ "/v1/traces",
+ ))
+ .build()
+ {
+ Ok(exporter) => exporter,
+ Err(error) => {
+ warn!(%error, "OpenTelemetry span exporter 初始化失败,已回退为本地日志");
+ return None;
+ }
+ };
+
+ let metric_exporter = match opentelemetry_otlp::MetricExporter::builder()
+ .with_http()
+ .with_endpoint(resolve_otlp_http_signal_endpoint(
+ "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT",
+ "/v1/metrics",
+ ))
+ .build()
+ {
+ Ok(exporter) => exporter,
+ Err(error) => {
+ warn!(%error, "OpenTelemetry metric exporter 初始化失败,已回退为本地日志");
+ return None;
+ }
+ };
+
+ let log_exporter = match opentelemetry_otlp::LogExporter::builder()
+ .with_http()
+ .with_endpoint(resolve_otlp_http_signal_endpoint(
+ "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT",
+ "/v1/logs",
+ ))
+ .build()
+ {
+ Ok(exporter) => exporter,
+ Err(error) => {
+ warn!(%error, "OpenTelemetry log exporter 初始化失败,已回退为本地日志");
+ return None;
+ }
+ };
+
+ let tracer_provider = SdkTracerProvider::builder()
+ .with_resource(resource.clone())
+ .with_batch_exporter(span_exporter)
+ .build();
+ let meter_provider = SdkMeterProvider::builder()
+ .with_resource(resource)
+ .with_periodic_exporter(metric_exporter)
+ .build();
+ let logger_provider = SdkLoggerProvider::builder()
+ .with_resource(Resource::builder()
+ .with_service_name(read_env_or_default("OTEL_SERVICE_NAME", "genarrative-api"))
+ .with_attribute(KeyValue::new("service.namespace", "genarrative"))
+ .build())
+ .with_batch_exporter(log_exporter)
+ .build();
+
+ global::set_tracer_provider(tracer_provider.clone());
+ global::set_meter_provider(meter_provider.clone());
+
+ Some(OtelPipeline {
+ tracer_provider,
+ _meter_provider: meter_provider,
+ logger_provider,
+ })
+}
+
+fn read_env_or_default(key: &str, default_value: &str) -> String {
+ std::env::var(key)
+ .ok()
+ .filter(|value| !value.trim().is_empty())
+ .unwrap_or_else(|| default_value.to_string())
+}
+
+fn resolve_otlp_http_signal_endpoint(signal_key: &str, signal_path: &str) -> String {
+ if let Ok(value) = std::env::var(signal_key)
+ && !value.trim().is_empty()
+ {
+ return value;
+ }
+
+ append_otlp_signal_path(
+ &read_env_or_default("OTEL_EXPORTER_OTLP_ENDPOINT", "http://127.0.0.1:4318"),
+ signal_path,
+ )
+}
+
+fn append_otlp_signal_path(base_endpoint: &str, signal_path: &str) -> String {
+ let base_endpoint = base_endpoint.trim_end_matches('/');
+ let signal_path = signal_path.trim_start_matches('/');
+ format!("{base_endpoint}/{signal_path}")
+}
+
+#[cfg(test)]
+mod tests {
+ use std::sync::{Mutex, OnceLock};
+
+ use super::resolve_otlp_http_signal_endpoint;
+
+ const OTEL_ENDPOINT_ENV_KEYS: [&str; 4] = [
+ "OTEL_EXPORTER_OTLP_ENDPOINT",
+ "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT",
+ "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT",
+ "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT",
+ ];
+
+ fn env_lock() -> std::sync::MutexGuard<'static, ()> {
+ static LOCK: OnceLock> = OnceLock::new();
+ LOCK.get_or_init(|| Mutex::new(())).lock().unwrap()
+ }
+
+ fn clear_otel_endpoint_env() {
+ unsafe {
+ for key in OTEL_ENDPOINT_ENV_KEYS {
+ std::env::remove_var(key);
+ }
+ }
+ }
+
+ #[test]
+ fn generic_otlp_http_endpoint_expands_to_signal_paths() {
+ let _guard = env_lock();
+ clear_otel_endpoint_env();
+ unsafe {
+ std::env::set_var("OTEL_EXPORTER_OTLP_ENDPOINT", "http://127.0.0.1:4318");
+ }
+
+ assert_eq!(
+ resolve_otlp_http_signal_endpoint("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", "/v1/traces"),
+ "http://127.0.0.1:4318/v1/traces"
+ );
+ assert_eq!(
+ resolve_otlp_http_signal_endpoint("OTEL_EXPORTER_OTLP_METRICS_ENDPOINT", "/v1/metrics"),
+ "http://127.0.0.1:4318/v1/metrics"
+ );
+ assert_eq!(
+ resolve_otlp_http_signal_endpoint("OTEL_EXPORTER_OTLP_LOGS_ENDPOINT", "/v1/logs"),
+ "http://127.0.0.1:4318/v1/logs"
+ );
+
+ clear_otel_endpoint_env();
+ }
+}
diff --git a/server-rs/crates/spacetime-client/Cargo.toml b/server-rs/crates/spacetime-client/Cargo.toml
index 4499f545..734c0df9 100644
--- a/server-rs/crates/spacetime-client/Cargo.toml
+++ b/server-rs/crates/spacetime-client/Cargo.toml
@@ -27,3 +27,5 @@ shared-kernel = { workspace = true }
spacetimedb-sdk = { workspace = true }
time = { workspace = true }
tokio = { workspace = true, features = ["rt", "sync", "time"] }
+opentelemetry = { workspace = true }
+tracing = { workspace = true }
diff --git a/server-rs/crates/spacetime-client/src/ai.rs b/server-rs/crates/spacetime-client/src/ai.rs
index 84a8041c..1ecbfd9d 100644
--- a/server-rs/crates/spacetime-client/src/ai.rs
+++ b/server-rs/crates/spacetime-client/src/ai.rs
@@ -8,7 +8,7 @@ impl SpacetimeClient {
) -> Result {
let procedure_input = input.into();
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("create_ai_task_and_return", move |connection, sender| {
connection.procedures().create_ai_task_and_return_then(
procedure_input,
move |_, result| {
@@ -28,7 +28,7 @@ impl SpacetimeClient {
) -> Result<(), SpacetimeClientError> {
let reducer_input = input.into();
- self.call_reducer_after_connect(move |connection, sender| {
+ self.call_reducer_after_connect("start_ai_task", move |connection, sender| {
let callback_sender = sender.clone();
if let Err(error) =
connection
@@ -52,7 +52,7 @@ impl SpacetimeClient {
) -> Result<(), SpacetimeClientError> {
let reducer_input = input.into();
- self.call_reducer_after_connect(move |connection, sender| {
+ self.call_reducer_after_connect("start_ai_task_stage", move |connection, sender| {
let callback_sender = sender.clone();
if let Err(error) =
connection
@@ -76,16 +76,19 @@ impl SpacetimeClient {
) -> Result {
let procedure_input = input.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .append_ai_text_chunk_and_return_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_ai_task_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "append_ai_text_chunk_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .append_ai_text_chunk_and_return_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_ai_task_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -95,7 +98,7 @@ impl SpacetimeClient {
) -> Result {
let procedure_input = input.into();
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("complete_ai_stage_and_return", move |connection, sender| {
connection.procedures().complete_ai_stage_and_return_then(
procedure_input,
move |_, result| {
@@ -115,16 +118,22 @@ impl SpacetimeClient {
) -> Result {
let procedure_input = input.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .attach_ai_result_reference_and_return_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_ai_task_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "attach_ai_result_reference_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .attach_ai_result_reference_and_return_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_ai_task_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -134,7 +143,7 @@ impl SpacetimeClient {
) -> Result {
let procedure_input = input.into();
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("complete_ai_task_and_return", move |connection, sender| {
connection.procedures().complete_ai_task_and_return_then(
procedure_input,
move |_, result| {
@@ -154,7 +163,7 @@ impl SpacetimeClient {
) -> Result {
let procedure_input = input.into();
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("fail_ai_task_and_return", move |connection, sender| {
connection.procedures().fail_ai_task_and_return_then(
procedure_input,
move |_, result| {
@@ -174,7 +183,7 @@ impl SpacetimeClient {
) -> Result {
let procedure_input = input.into();
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("cancel_ai_task_and_return", move |connection, sender| {
connection.procedures().cancel_ai_task_and_return_then(
procedure_input,
move |_, result| {
diff --git a/server-rs/crates/spacetime-client/src/assets.rs b/server-rs/crates/spacetime-client/src/assets.rs
index ef0a910e..4cb2c2b7 100644
--- a/server-rs/crates/spacetime-client/src/assets.rs
+++ b/server-rs/crates/spacetime-client/src/assets.rs
@@ -7,17 +7,20 @@ impl SpacetimeClient {
) -> Result, SpacetimeClientError> {
let procedure_input = input.into();
- self.call_after_connect(move |connection, sender| {
- connection.procedures().list_asset_history_and_return_then(
- procedure_input,
- move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_asset_history_list_result);
- send_once(&sender, mapped);
- },
- );
- })
+ self.call_after_connect(
+ "list_asset_history_and_return",
+ move |connection, sender| {
+ connection.procedures().list_asset_history_and_return_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_asset_history_list_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -27,16 +30,19 @@ impl SpacetimeClient {
) -> Result {
let procedure_input = input.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .confirm_asset_object_and_return_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "confirm_asset_object_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .confirm_asset_object_and_return_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -46,16 +52,22 @@ impl SpacetimeClient {
) -> Result {
let procedure_input = input.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .bind_asset_object_to_entity_and_return_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_entity_binding_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "bind_asset_object_to_entity_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .bind_asset_object_to_entity_and_return_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_entity_binding_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
}
diff --git a/server-rs/crates/spacetime-client/src/auth.rs b/server-rs/crates/spacetime-client/src/auth.rs
index 438a2d69..e0f8faa1 100644
--- a/server-rs/crates/spacetime-client/src/auth.rs
+++ b/server-rs/crates/spacetime-client/src/auth.rs
@@ -4,23 +4,26 @@ impl SpacetimeClient {
pub async fn export_auth_store_snapshot_from_tables(
&self,
) -> Result {
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .export_auth_store_snapshot_from_tables_then(move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_auth_store_snapshot_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "export_auth_store_snapshot_from_tables",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .export_auth_store_snapshot_from_tables_then(move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_auth_store_snapshot_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
pub async fn get_auth_store_snapshot(
&self,
) -> Result {
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_auth_store_snapshot", move |connection, sender| {
connection
.procedures()
.get_auth_store_snapshot_then(move |_, result| {
@@ -43,7 +46,7 @@ impl SpacetimeClient {
updated_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("upsert_auth_store_snapshot", move |connection, sender| {
connection.procedures().upsert_auth_store_snapshot_then(
procedure_input,
move |_, result| {
@@ -67,23 +70,26 @@ impl SpacetimeClient {
updated_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .import_auth_store_snapshot_json_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_auth_store_snapshot_import_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "import_auth_store_snapshot_json",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .import_auth_store_snapshot_json_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_auth_store_snapshot_import_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
pub async fn import_auth_store_snapshot(
&self,
) -> Result {
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("import_auth_store_snapshot", move |connection, sender| {
connection
.procedures()
.import_auth_store_snapshot_then(move |_, result| {
diff --git a/server-rs/crates/spacetime-client/src/bark_battle.rs b/server-rs/crates/spacetime-client/src/bark_battle.rs
index 18985b15..19af1cb5 100644
--- a/server-rs/crates/spacetime-client/src/bark_battle.rs
+++ b/server-rs/crates/spacetime-client/src/bark_battle.rs
@@ -11,7 +11,7 @@ impl SpacetimeClient {
&self,
input: BarkBattleDraftCreateRecordInput,
) -> Result {
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("create_bark_battle_draft", move |connection, sender| {
connection
.procedures()
.create_bark_battle_draft_then(input, move |_, result| {
@@ -28,16 +28,19 @@ impl SpacetimeClient {
&self,
input: BarkBattleDraftConfigUpsertRecordInput,
) -> Result {
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .update_bark_battle_draft_config_then(input, move |_, result| {
- let mapped = result
- .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
- .and_then(map_bark_battle_draft_config_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "update_bark_battle_draft_config",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .update_bark_battle_draft_config_then(input, move |_, result| {
+ let mapped = result
+ .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
+ .and_then(map_bark_battle_draft_config_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -45,7 +48,7 @@ impl SpacetimeClient {
&self,
input: BarkBattleWorkPublishRecordInput,
) -> Result {
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("publish_bark_battle_work", move |connection, sender| {
connection
.procedures()
.publish_bark_battle_work_then(input, move |_, result| {
@@ -67,16 +70,20 @@ impl SpacetimeClient {
work_id,
owner_user_id,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .get_bark_battle_runtime_config_then(input, move |_, result| {
- let mapped = result
- .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
- .and_then(map_bark_battle_runtime_config_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "get_bark_battle_runtime_config",
+ move |connection, sender| {
+ connection.procedures().get_bark_battle_runtime_config_then(
+ input,
+ move |_, result| {
+ let mapped = result
+ .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
+ .and_then(map_bark_battle_runtime_config_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -84,7 +91,7 @@ impl SpacetimeClient {
&self,
input: BarkBattleRunStartRecordInput,
) -> Result {
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("start_bark_battle_run", move |connection, sender| {
connection
.procedures()
.start_bark_battle_run_then(input, move |_, result| {
@@ -101,7 +108,7 @@ impl SpacetimeClient {
&self,
input: BarkBattleRunFinishRecordInput,
) -> Result {
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("finish_bark_battle_run", move |connection, sender| {
connection
.procedures()
.finish_bark_battle_run_then(input, move |_, result| {
@@ -123,7 +130,7 @@ impl SpacetimeClient {
run_id,
owner_user_id,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_bark_battle_run", move |connection, sender| {
connection
.procedures()
.get_bark_battle_run_then(input, move |_, result| {
diff --git a/server-rs/crates/spacetime-client/src/big_fish.rs b/server-rs/crates/spacetime-client/src/big_fish.rs
index 699cb5a6..38c976b4 100644
--- a/server-rs/crates/spacetime-client/src/big_fish.rs
+++ b/server-rs/crates/spacetime-client/src/big_fish.rs
@@ -23,7 +23,7 @@ impl SpacetimeClient {
created_at_micros: input.created_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("create_big_fish_session", move |connection, sender| {
connection.procedures().create_big_fish_session_then(
procedure_input,
move |_, result| {
@@ -47,7 +47,7 @@ impl SpacetimeClient {
owner_user_id,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_big_fish_session", move |connection, sender| {
connection
.procedures()
.get_big_fish_session_then(procedure_input, move |_, result| {
@@ -87,7 +87,7 @@ impl SpacetimeClient {
&self,
procedure_input: BigFishWorksListInput,
) -> Result, SpacetimeClientError> {
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("list_big_fish_works", move |connection, sender| {
let fallback_owner_user_id = if procedure_input.published_only {
None
} else {
@@ -120,7 +120,7 @@ impl SpacetimeClient {
owner_user_id,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("delete_big_fish_work", move |connection, sender| {
let fallback_owner_user_id = Some(procedure_input.owner_user_id.clone());
connection
.procedures()
@@ -152,7 +152,7 @@ impl SpacetimeClient {
submitted_at_micros: input.submitted_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("submit_big_fish_message", move |connection, sender| {
connection.procedures().submit_big_fish_message_then(
procedure_input,
move |_, result| {
@@ -182,16 +182,22 @@ impl SpacetimeClient {
updated_at_micros: input.updated_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .finalize_big_fish_agent_message_turn_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_big_fish_session_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "finalize_big_fish_agent_message_turn",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .finalize_big_fish_agent_message_turn_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_big_fish_session_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -206,7 +212,7 @@ impl SpacetimeClient {
compiled_at_micros: input.compiled_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("compile_big_fish_draft", move |connection, sender| {
connection.procedures().compile_big_fish_draft_then(
procedure_input,
move |_, result| {
@@ -234,7 +240,7 @@ impl SpacetimeClient {
generated_at_micros: input.generated_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("generate_big_fish_asset", move |connection, sender| {
connection.procedures().generate_big_fish_asset_then(
procedure_input,
move |_, result| {
@@ -260,7 +266,7 @@ impl SpacetimeClient {
published_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("publish_big_fish_game", move |connection, sender| {
connection.procedures().publish_big_fish_game_then(
procedure_input,
move |_, result| {
@@ -285,7 +291,7 @@ impl SpacetimeClient {
played_at_micros: input.reported_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("record_big_fish_play", move |connection, sender| {
connection
.procedures()
.record_big_fish_play_then(procedure_input, move |_, result| {
@@ -309,7 +315,7 @@ impl SpacetimeClient {
started_at_micros: input.started_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("start_big_fish_run", move |connection, sender| {
connection
.procedures()
.start_big_fish_run_then(procedure_input, move |_, result| {
@@ -332,7 +338,7 @@ impl SpacetimeClient {
liked_at_micros: input.liked_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("record_big_fish_like", move |connection, sender| {
connection
.procedures()
.record_big_fish_like_then(procedure_input, move |_, result| {
@@ -355,7 +361,7 @@ impl SpacetimeClient {
owner_user_id,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_big_fish_run", move |connection, sender| {
connection
.procedures()
.get_big_fish_run_then(procedure_input, move |_, result| {
@@ -380,7 +386,7 @@ impl SpacetimeClient {
remixed_at_micros: input.remixed_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("remix_big_fish_work", move |connection, sender| {
connection
.procedures()
.remix_big_fish_work_then(procedure_input, move |_, result| {
@@ -405,7 +411,7 @@ impl SpacetimeClient {
submitted_at_micros: input.submitted_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("submit_big_fish_input", move |connection, sender| {
connection.procedures().submit_big_fish_input_then(
procedure_input,
move |_, result| {
diff --git a/server-rs/crates/spacetime-client/src/combat.rs b/server-rs/crates/spacetime-client/src/combat.rs
index 80ae53ae..e8b4814e 100644
--- a/server-rs/crates/spacetime-client/src/combat.rs
+++ b/server-rs/crates/spacetime-client/src/combat.rs
@@ -9,17 +9,20 @@ impl SpacetimeClient {
validate_battle_state_input(&input).map_err(SpacetimeClientError::validation_failed)?;
let procedure_input = input.into();
- self.call_after_connect(move |connection, sender| {
- connection.procedures().create_battle_state_and_return_then(
- procedure_input,
- move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_battle_state_procedure_result);
- send_once(&sender, mapped);
- },
- );
- })
+ self.call_after_connect(
+ "create_battle_state_and_return",
+ move |connection, sender| {
+ connection.procedures().create_battle_state_and_return_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_battle_state_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -31,7 +34,7 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_battle_state", move |connection, sender| {
connection
.procedures()
.get_battle_state_then(procedure_input, move |_, result| {
@@ -52,16 +55,19 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?;
let procedure_input = input.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .resolve_combat_action_and_return_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_resolve_combat_action_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "resolve_combat_action_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .resolve_combat_action_and_return_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_resolve_combat_action_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
}
diff --git a/server-rs/crates/spacetime-client/src/custom_world.rs b/server-rs/crates/spacetime-client/src/custom_world.rs
index 3cbeb56f..62b7be19 100644
--- a/server-rs/crates/spacetime-client/src/custom_world.rs
+++ b/server-rs/crates/spacetime-client/src/custom_world.rs
@@ -12,7 +12,7 @@ impl SpacetimeClient {
) -> Result, SpacetimeClientError> {
let procedure_input = CustomWorldProfileListInput { owner_user_id };
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("list_custom_world_profiles", move |connection, sender| {
connection.procedures().list_custom_world_profiles_then(
procedure_input,
move |_, result| {
@@ -36,16 +36,19 @@ impl SpacetimeClient {
profile_id,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .get_custom_world_library_detail_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_custom_world_library_detail_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "get_custom_world_library_detail",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .get_custom_world_library_detail_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_custom_world_library_detail_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -55,16 +58,22 @@ impl SpacetimeClient {
) -> Result {
let procedure_input = input.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .upsert_custom_world_profile_and_return_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_custom_world_library_mutation_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "upsert_custom_world_profile_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .upsert_custom_world_profile_and_return_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_custom_world_library_mutation_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -86,16 +95,22 @@ impl SpacetimeClient {
published_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .publish_custom_world_profile_and_return_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_custom_world_library_mutation_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "publish_custom_world_profile_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .publish_custom_world_profile_and_return_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_custom_world_library_mutation_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -113,19 +128,22 @@ impl SpacetimeClient {
updated_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .unpublish_custom_world_profile_and_return_then(
- procedure_input,
- move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_custom_world_library_mutation_result);
- send_once(&sender, mapped);
- },
- );
- })
+ self.call_after_connect(
+ "unpublish_custom_world_profile_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .unpublish_custom_world_profile_and_return_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_custom_world_library_mutation_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -141,32 +159,41 @@ impl SpacetimeClient {
deleted_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .delete_custom_world_profile_and_return_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_custom_world_profile_list_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "delete_custom_world_profile_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .delete_custom_world_profile_and_return_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_custom_world_profile_list_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
pub async fn list_custom_world_gallery_entries(
&self,
) -> Result, SpacetimeClientError> {
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .list_custom_world_gallery_entries_then(move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_custom_world_gallery_list_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "list_custom_world_gallery_entries",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .list_custom_world_gallery_entries_then(move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_custom_world_gallery_list_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -180,16 +207,19 @@ impl SpacetimeClient {
profile_id,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .get_custom_world_gallery_detail_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_custom_world_library_mutation_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "get_custom_world_gallery_detail",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .get_custom_world_gallery_detail_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_custom_world_library_mutation_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -199,16 +229,22 @@ impl SpacetimeClient {
) -> Result {
let procedure_input = CustomWorldGalleryDetailByCodeInput { public_work_code };
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .get_custom_world_gallery_detail_by_code_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_custom_world_library_mutation_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "get_custom_world_gallery_detail_by_code",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .get_custom_world_gallery_detail_by_code_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_custom_world_library_mutation_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -225,7 +261,7 @@ impl SpacetimeClient {
remixed_at_micros: input.remixed_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("remix_custom_world_profile", move |connection, sender| {
connection.procedures().remix_custom_world_profile_then(
procedure_input,
move |_, result| {
@@ -249,16 +285,19 @@ impl SpacetimeClient {
played_at_micros: input.played_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .record_custom_world_profile_play_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
- .and_then(map_custom_world_library_mutation_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "record_custom_world_profile_play",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .record_custom_world_profile_play_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
+ .and_then(map_custom_world_library_mutation_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -273,16 +312,19 @@ impl SpacetimeClient {
liked_at_micros: input.liked_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .record_custom_world_profile_like_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
- .and_then(map_custom_world_library_mutation_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "record_custom_world_profile_like",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .record_custom_world_profile_like_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
+ .and_then(map_custom_world_library_mutation_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -292,7 +334,7 @@ impl SpacetimeClient {
) -> Result {
let procedure_input = input.into();
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("publish_custom_world_world", move |connection, sender| {
connection.procedures().publish_custom_world_world_then(
procedure_input,
move |_, result| {
@@ -331,16 +373,19 @@ impl SpacetimeClient {
created_at_micros: input.created_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .create_custom_world_agent_session_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_custom_world_agent_session_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "create_custom_world_agent_session",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .create_custom_world_agent_session_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_custom_world_agent_session_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -354,17 +399,20 @@ impl SpacetimeClient {
owner_user_id,
};
- self.call_after_connect(move |connection, sender| {
- connection.procedures().get_custom_world_agent_session_then(
- procedure_input,
- move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_custom_world_agent_session_procedure_result);
- send_once(&sender, mapped);
- },
- );
- })
+ self.call_after_connect(
+ "get_custom_world_agent_session",
+ move |connection, sender| {
+ connection.procedures().get_custom_world_agent_session_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_custom_world_agent_session_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -374,7 +422,7 @@ impl SpacetimeClient {
) -> Result, SpacetimeClientError> {
let procedure_input = CustomWorldWorksListInput { owner_user_id };
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("list_custom_world_works", move |connection, sender| {
connection.procedures().list_custom_world_works_then(
procedure_input,
move |_, result| {
@@ -398,16 +446,19 @@ impl SpacetimeClient {
owner_user_id,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .delete_custom_world_agent_session_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_custom_world_works_list_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "delete_custom_world_agent_session",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .delete_custom_world_agent_session_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_custom_world_works_list_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -423,16 +474,19 @@ impl SpacetimeClient {
card_id,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .get_custom_world_agent_card_detail_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_custom_world_draft_card_detail_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "get_custom_world_agent_card_detail",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .get_custom_world_agent_card_detail_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_custom_world_draft_card_detail_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -449,16 +503,19 @@ impl SpacetimeClient {
submitted_at_micros: input.submitted_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .execute_custom_world_agent_action_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_custom_world_agent_action_execute_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "execute_custom_world_agent_action",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .execute_custom_world_agent_action_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_custom_world_agent_action_execute_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -475,16 +532,19 @@ impl SpacetimeClient {
submitted_at_micros: input.submitted_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .submit_custom_world_agent_message_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_custom_world_agent_operation_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "submit_custom_world_agent_message",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .submit_custom_world_agent_message_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_custom_world_agent_operation_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -521,19 +581,22 @@ impl SpacetimeClient {
updated_at_micros: input.updated_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .finalize_custom_world_agent_message_turn_then(
- procedure_input,
- move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_custom_world_agent_operation_procedure_result);
- send_once(&sender, mapped);
- },
- );
- })
+ self.call_after_connect(
+ "finalize_custom_world_agent_message_turn",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .finalize_custom_world_agent_message_turn_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_custom_world_agent_operation_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -556,19 +619,22 @@ impl SpacetimeClient {
updated_at_micros: input.updated_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .upsert_custom_world_agent_operation_progress_then(
- procedure_input,
- move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_custom_world_agent_operation_procedure_result);
- send_once(&sender, mapped);
- },
- );
- })
+ self.call_after_connect(
+ "upsert_custom_world_agent_operation_progress",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .upsert_custom_world_agent_operation_progress_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_custom_world_agent_operation_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -584,16 +650,19 @@ impl SpacetimeClient {
operation_id,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .get_custom_world_agent_operation_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_custom_world_agent_operation_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "get_custom_world_agent_operation",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .get_custom_world_agent_operation_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_custom_world_agent_operation_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
}
diff --git a/server-rs/crates/spacetime-client/src/inventory.rs b/server-rs/crates/spacetime-client/src/inventory.rs
index 470c8c45..490a9a4b 100644
--- a/server-rs/crates/spacetime-client/src/inventory.rs
+++ b/server-rs/crates/spacetime-client/src/inventory.rs
@@ -11,7 +11,7 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_runtime_inventory_state", move |connection, sender| {
connection.procedures().get_runtime_inventory_state_then(
procedure_input,
move |_, result| {
diff --git a/server-rs/crates/spacetime-client/src/lib.rs b/server-rs/crates/spacetime-client/src/lib.rs
index e222f6b2..cd09a684 100644
--- a/server-rs/crates/spacetime-client/src/lib.rs
+++ b/server-rs/crates/spacetime-client/src/lib.rs
@@ -3,6 +3,7 @@
pub mod module_bindings;
mod mapper;
+mod telemetry;
use mapper::*;
pub use mapper::{
AiResultReferenceRecord, AiTaskMutationRecord, AiTaskRecord, AiTaskStageRecord,
@@ -142,6 +143,24 @@ use module_npc::{
NpcStanceProfile as DomainNpcStanceProfile, NpcStateSnapshot as DomainNpcStateSnapshot,
ResolveNpcInteractionInput as DomainResolveNpcInteractionInput,
};
+use module_puzzle::{
+ PuzzleAgentMessageSnapshot as DomainPuzzleAgentMessageSnapshot,
+ PuzzleAgentSessionSnapshot as DomainPuzzleAgentSessionSnapshot,
+ PuzzleAgentSuggestedAction as DomainPuzzleAgentSuggestedAction,
+ PuzzleAnchorItem as DomainPuzzleAnchorItem, PuzzleAnchorPack as DomainPuzzleAnchorPack,
+ PuzzleBoardSnapshot as DomainPuzzleBoardSnapshot,
+ PuzzleCellPosition as DomainPuzzleCellPosition,
+ PuzzleCreatorIntent as DomainPuzzleCreatorIntent, PuzzleDraftLevel as DomainPuzzleDraftLevel,
+ PuzzleGeneratedImageCandidate as DomainPuzzleGeneratedImageCandidate,
+ PuzzleMergedGroupState as DomainPuzzleMergedGroupState,
+ PuzzlePieceState as DomainPuzzlePieceState, PuzzleResultDraft as DomainPuzzleResultDraft,
+ PuzzleResultPreviewBlocker as DomainPuzzleResultPreviewBlocker,
+ PuzzleResultPreviewEnvelope as DomainPuzzleResultPreviewEnvelope,
+ PuzzleResultPreviewFinding as DomainPuzzleResultPreviewFinding,
+ PuzzleRunSnapshot as DomainPuzzleRunSnapshot,
+ PuzzleRuntimeLevelSnapshot as DomainPuzzleRuntimeLevelSnapshot,
+ PuzzleWorkProfile as DomainPuzzleWorkProfile,
+};
use module_runtime::{
AnalyticsMetricQueryResponse as DomainAnalyticsMetricQueryResponse, RuntimeBrowseHistoryRecord,
RuntimePlatformTheme as DomainRuntimePlatformTheme, RuntimeProfileDashboardRecord,
@@ -307,56 +326,72 @@ impl SpacetimeClient {
async fn call_after_connect(
&self,
+ procedure: &'static str,
call: impl FnOnce(&DbConnection, ProcedureResultSender) + Send + 'static,
) -> Result
where
T: Send + 'static,
{
+ let metrics_guard = telemetry::begin_procedure(procedure);
let (sender, receiver) = oneshot::channel();
let result_sender = Arc::new(Mutex::new(Some(sender)));
- let lease = self.acquire_connection().await?;
- let final_result = if let Some(connection) = lease.connection.as_ref() {
- call(&connection.connection, result_sender.clone());
- match timeout(self.config.procedure_timeout, receiver).await {
- Ok(inner) => match inner {
- Ok(value) => value,
- Err(_) => Err(SpacetimeClientError::ConnectDropped),
- },
- Err(_) => Err(Self::resolve_timeout_error(Some(connection))),
+ let final_result = match self.acquire_connection().await {
+ Ok(lease) => {
+ let result = if let Some(connection) = lease.connection.as_ref() {
+ call(&connection.connection, result_sender.clone());
+ match timeout(self.config.procedure_timeout, receiver).await {
+ Ok(inner) => match inner {
+ Ok(value) => value,
+ Err(_) => Err(SpacetimeClientError::ConnectDropped),
+ },
+ Err(_) => Err(Self::resolve_timeout_error(Some(connection))),
+ }
+ } else {
+ Err(SpacetimeClientError::Runtime(
+ "SpacetimeDB 连接租约缺少连接".to_string(),
+ ))
+ };
+ self.release_connection(lease).await;
+ result
}
- } else {
- Err(SpacetimeClientError::Runtime(
- "SpacetimeDB 连接租约缺少连接".to_string(),
- ))
+ Err(error) => Err(error),
};
- self.release_connection(lease).await;
+ metrics_guard.finish(&final_result);
final_result
}
async fn call_reducer_after_connect(
&self,
+ procedure: &'static str,
call: impl FnOnce(&DbConnection, ReducerResultSender) + Send + 'static,
) -> Result<(), SpacetimeClientError> {
+ let metrics_guard = telemetry::begin_procedure(procedure);
let (sender, receiver) = oneshot::channel();
let result_sender = Arc::new(Mutex::new(Some(sender)));
- let lease = self.acquire_connection().await?;
- let final_result = if let Some(connection) = lease.connection.as_ref() {
- call(&connection.connection, result_sender.clone());
- match timeout(self.config.procedure_timeout, receiver).await {
- Ok(inner) => match inner {
- Ok(value) => value,
- Err(_) => Err(SpacetimeClientError::ConnectDropped),
- },
- Err(_) => Err(Self::resolve_timeout_error(Some(connection))),
+ let final_result = match self.acquire_connection().await {
+ Ok(lease) => {
+ let result = if let Some(connection) = lease.connection.as_ref() {
+ call(&connection.connection, result_sender.clone());
+ match timeout(self.config.procedure_timeout, receiver).await {
+ Ok(inner) => match inner {
+ Ok(value) => value,
+ Err(_) => Err(SpacetimeClientError::ConnectDropped),
+ },
+ Err(_) => Err(Self::resolve_timeout_error(Some(connection))),
+ }
+ } else {
+ Err(SpacetimeClientError::Runtime(
+ "SpacetimeDB 连接租约缺少连接".to_string(),
+ ))
+ };
+ self.release_connection(lease).await;
+ result
}
- } else {
- Err(SpacetimeClientError::Runtime(
- "SpacetimeDB 连接租约缺少连接".to_string(),
- ))
+ Err(error) => Err(error),
};
- self.release_connection(lease).await;
+ metrics_guard.finish(&final_result);
final_result
}
@@ -488,7 +523,6 @@ impl SpacetimeClient {
let mut subscriptions = Vec::new();
for query in [
"SELECT * FROM puzzle_gallery_view",
- "SELECT * FROM public_work_play_daily_stat WHERE source_type = 'puzzle'",
] {
let (sender, receiver) = oneshot::channel::>();
let applied_sender = Arc::new(Mutex::new(Some(sender)));
diff --git a/server-rs/crates/spacetime-client/src/match3d.rs b/server-rs/crates/spacetime-client/src/match3d.rs
index eb9efa7e..baaf7bb9 100644
--- a/server-rs/crates/spacetime-client/src/match3d.rs
+++ b/server-rs/crates/spacetime-client/src/match3d.rs
@@ -16,17 +16,20 @@ impl SpacetimeClient {
created_at_micros: input.created_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection.procedures().create_match_3_d_agent_session_then(
- procedure_input,
- move |_, result| {
- let mapped = result
- .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
- .and_then(map_match3d_agent_session_procedure_result);
- send_once(&sender, mapped);
- },
- );
- })
+ self.call_after_connect(
+ "create_match_3_d_agent_session",
+ move |connection, sender| {
+ connection.procedures().create_match_3_d_agent_session_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
+ .and_then(map_match3d_agent_session_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -40,7 +43,7 @@ impl SpacetimeClient {
owner_user_id,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_match_3_d_agent_session", move |connection, sender| {
connection.procedures().get_match_3_d_agent_session_then(
procedure_input,
move |_, result| {
@@ -66,17 +69,20 @@ impl SpacetimeClient {
submitted_at_micros: input.submitted_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection.procedures().submit_match_3_d_agent_message_then(
- procedure_input,
- move |_, result| {
- let mapped = result
- .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
- .and_then(map_match3d_agent_session_procedure_result);
- send_once(&sender, mapped);
- },
- );
- })
+ self.call_after_connect(
+ "submit_match_3_d_agent_message",
+ move |connection, sender| {
+ connection.procedures().submit_match_3_d_agent_message_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
+ .and_then(map_match3d_agent_session_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -96,16 +102,22 @@ impl SpacetimeClient {
error_message: input.error_message,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .finalize_match_3_d_agent_message_turn_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
- .and_then(map_match3d_agent_session_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "finalize_match_3_d_agent_message_turn",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .finalize_match_3_d_agent_message_turn_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
+ .and_then(map_match3d_agent_session_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -127,7 +139,7 @@ impl SpacetimeClient {
generated_item_assets_json: input.generated_item_assets_json,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("compile_match_3_d_draft", move |connection, sender| {
connection.procedures().compile_match_3_d_draft_then(
procedure_input,
move |_, result| {
@@ -159,7 +171,7 @@ impl SpacetimeClient {
updated_at_micros: input.updated_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("update_match_3_d_work", move |connection, sender| {
connection.procedures().update_match_3_d_work_then(
procedure_input,
move |_, result| {
@@ -185,7 +197,7 @@ impl SpacetimeClient {
published_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("publish_match_3_d_work", move |connection, sender| {
connection.procedures().publish_match_3_d_work_then(
procedure_input,
move |_, result| {
@@ -225,7 +237,7 @@ impl SpacetimeClient {
&self,
procedure_input: Match3DWorksListInput,
) -> Result, SpacetimeClientError> {
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("list_match_3_d_works", move |connection, sender| {
connection
.procedures()
.list_match_3_d_works_then(procedure_input, move |_, result| {
@@ -248,7 +260,7 @@ impl SpacetimeClient {
owner_user_id,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_match_3_d_work_detail", move |connection, sender| {
connection.procedures().get_match_3_d_work_detail_then(
procedure_input,
move |_, result| {
@@ -272,7 +284,7 @@ impl SpacetimeClient {
owner_user_id,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("delete_match_3_d_work", move |connection, sender| {
connection.procedures().delete_match_3_d_work_then(
procedure_input,
move |_, result| {
@@ -299,7 +311,7 @@ impl SpacetimeClient {
item_type_count_override: input.item_type_count_override,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("start_match_3_d_run", move |connection, sender| {
connection
.procedures()
.start_match_3_d_run_then(procedure_input, move |_, result| {
@@ -327,7 +339,7 @@ impl SpacetimeClient {
owner_user_id,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_match_3_d_run", move |connection, sender| {
connection
.procedures()
.get_match_3_d_run_then(procedure_input, move |_, result| {
@@ -359,7 +371,7 @@ impl SpacetimeClient {
clicked_at_ms: input.clicked_at_ms,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("click_match_3_d_item", move |connection, sender| {
connection
.procedures()
.click_match_3_d_item_then(procedure_input, move |_, result| {
@@ -390,7 +402,7 @@ impl SpacetimeClient {
stopped_at_ms: input.stopped_at_ms,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("stop_match_3_d_run", move |connection, sender| {
connection
.procedures()
.stop_match_3_d_run_then(procedure_input, move |_, result| {
@@ -419,7 +431,7 @@ impl SpacetimeClient {
restarted_at_ms: input.restarted_at_ms,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("restart_match_3_d_run", move |connection, sender| {
connection.procedures().restart_match_3_d_run_then(
procedure_input,
move |_, result| {
@@ -448,7 +460,7 @@ impl SpacetimeClient {
finished_at_ms: input.finished_at_ms,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("finish_match_3_d_time_up", move |connection, sender| {
connection.procedures().finish_match_3_d_time_up_then(
procedure_input,
move |_, result| {
diff --git a/server-rs/crates/spacetime-client/src/module_bindings/mod.rs b/server-rs/crates/spacetime-client/src/module_bindings/mod.rs
index 26fa2455..8e38203c 100644
--- a/server-rs/crates/spacetime-client/src/module_bindings/mod.rs
+++ b/server-rs/crates/spacetime-client/src/module_bindings/mod.rs
@@ -95,7 +95,6 @@ pub mod auth_store_snapshot_type;
pub mod auth_store_snapshot_upsert_input_type;
pub mod authorize_database_migration_operator_procedure;
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_upsert_input_type;
pub mod bark_battle_draft_create_input_type;
@@ -108,10 +107,8 @@ pub mod bark_battle_published_config_row_type;
pub mod bark_battle_published_config_table;
pub mod bark_battle_run_finish_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_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_table;
pub mod bark_battle_score_record_row_type;
@@ -163,20 +160,16 @@ pub mod big_fish_run_get_input_type;
pub mod big_fish_run_procedure_result_type;
pub mod big_fish_run_start_input_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_run_table;
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_get_input_type;
pub mod big_fish_session_procedure_result_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_like_record_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_procedure_result_type;
pub mod bind_asset_object_to_entity_and_return_procedure;
@@ -409,38 +402,30 @@ pub mod list_visual_novel_works_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_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_table;
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_procedure_result_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_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_snapshot_type;
-pub mod match_3_d_item_snapshot_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_procedure_result_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_stop_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_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_get_input_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_table;
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_works_list_input_type;
pub mod match_3_d_works_procedure_result_type;
@@ -514,58 +499,34 @@ pub mod puzzle_agent_message_finalize_input_type;
pub mod puzzle_agent_message_kind_type;
pub mod puzzle_agent_message_role_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_table;
pub mod puzzle_agent_session_create_input_type;
pub mod puzzle_agent_session_get_input_type;
pub mod puzzle_agent_session_procedure_result_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_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_level_type;
pub mod puzzle_event_kind_type;
pub mod puzzle_event_table;
pub mod puzzle_event_type;
pub mod puzzle_form_draft_save_input_type;
-pub mod puzzle_form_draft_type;
pub mod puzzle_gallery_view_table;
-pub mod puzzle_generated_image_candidate_type;
pub mod puzzle_generated_images_save_input_type;
pub mod puzzle_leaderboard_entry_row_type;
pub mod puzzle_leaderboard_entry_table;
-pub mod puzzle_leaderboard_entry_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_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_get_input_type;
pub mod puzzle_run_next_level_input_type;
pub mod puzzle_run_pause_input_type;
pub mod puzzle_run_procedure_result_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_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_table;
pub mod puzzle_select_cover_image_input_type;
@@ -577,7 +538,6 @@ pub mod puzzle_work_point_incentive_claim_input_type;
pub mod puzzle_work_procedure_result_type;
pub mod puzzle_work_profile_row_type;
pub mod puzzle_work_profile_table;
-pub mod puzzle_work_profile_type;
pub mod puzzle_work_remix_input_type;
pub mod puzzle_work_upsert_input_type;
pub mod puzzle_works_list_input_type;
@@ -769,41 +729,30 @@ pub mod seed_analytics_date_dimensions_reducer;
pub mod select_puzzle_cover_image_procedure;
pub mod square_hole_agent_message_finalize_input_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_table;
pub mod square_hole_agent_session_create_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_row_type;
-pub mod square_hole_agent_session_snapshot_type;
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_snapshot_type;
-pub mod square_hole_drop_feedback_snapshot_type;
pub mod square_hole_drop_shape_procedure_result_type;
-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_get_input_type;
pub mod square_hole_run_procedure_result_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_stop_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_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_get_input_type;
pub mod square_hole_work_procedure_result_type;
pub mod square_hole_work_profile_row_type;
pub mod square_hole_work_profile_table;
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_works_list_input_type;
pub mod square_hole_works_procedure_result_type;
@@ -880,31 +829,24 @@ pub mod user_browse_history_table;
pub mod user_browse_history_type;
pub mod visual_novel_agent_message_finalize_input_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_table;
pub mod visual_novel_agent_session_create_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_row_type;
-pub mod visual_novel_agent_session_snapshot_type;
pub mod visual_novel_agent_session_table;
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_procedure_result_type;
-pub mod visual_novel_run_snapshot_type;
pub mod visual_novel_run_snapshot_upsert_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_record_input_type;
-pub mod visual_novel_runtime_event_snapshot_type;
pub mod visual_novel_runtime_event_table;
pub mod visual_novel_runtime_event_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_snapshot_type;
pub mod visual_novel_runtime_history_entry_table;
pub mod visual_novel_runtime_history_list_input_type;
pub mod visual_novel_runtime_run_row_type;
@@ -916,7 +858,6 @@ pub mod visual_novel_work_procedure_result_type;
pub mod visual_novel_work_profile_row_type;
pub mod visual_novel_work_profile_table;
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_works_list_input_type;
pub mod visual_novel_works_procedure_result_type;
@@ -1010,7 +951,6 @@ pub use auth_store_snapshot_type::AuthStoreSnapshot;
pub use auth_store_snapshot_upsert_input_type::AuthStoreSnapshotUpsertInput;
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_snapshot_type::BarkBattleDraftConfigSnapshot;
pub use bark_battle_draft_config_table::*;
pub use bark_battle_draft_config_upsert_input_type::BarkBattleDraftConfigUpsertInput;
pub use bark_battle_draft_create_input_type::BarkBattleDraftCreateInput;
@@ -1023,10 +963,8 @@ pub use bark_battle_published_config_row_type::BarkBattlePublishedConfigRow;
pub use bark_battle_published_config_table::*;
pub use bark_battle_run_finish_input_type::BarkBattleRunFinishInput;
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_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_table::*;
pub use bark_battle_score_record_row_type::BarkBattleScoreRecordRow;
@@ -1078,20 +1016,16 @@ pub use big_fish_run_get_input_type::BigFishRunGetInput;
pub use big_fish_run_procedure_result_type::BigFishRunProcedureResult;
pub use big_fish_run_start_input_type::BigFishRunStartInput;
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_run_table::*;
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_get_input_type::BigFishSessionGetInput;
pub use big_fish_session_procedure_result_type::BigFishSessionProcedureResult;
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_like_record_input_type::BigFishWorkLikeRecordInput;
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_procedure_result_type::BigFishWorksProcedureResult;
pub use bind_asset_object_to_entity_and_return_procedure::bind_asset_object_to_entity_and_return;
@@ -1324,38 +1258,30 @@ 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 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_snapshot_type::Match3DAgentMessageSnapshot;
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_session_create_input_type::Match3DAgentSessionCreateInput;
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_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_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_snapshot_type::Match3DDraftSnapshot;
-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_get_input_type::Match3DRunGetInput;
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_snapshot_type::Match3DRunSnapshot;
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_time_up_input_type::Match3DRunTimeUpInput;
pub use match_3_d_runtime_run_row_type::Match3DRuntimeRunRow;
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_get_input_type::Match3DWorkGetInput;
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_table::*;
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_works_list_input_type::Match3DWorksListInput;
pub use match_3_d_works_procedure_result_type::Match3DWorksProcedureResult;
@@ -1429,58 +1355,34 @@ pub use puzzle_agent_message_finalize_input_type::PuzzleAgentMessageFinalizeInpu
pub use puzzle_agent_message_kind_type::PuzzleAgentMessageKind;
pub use puzzle_agent_message_role_type::PuzzleAgentMessageRole;
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_table::*;
pub use puzzle_agent_session_create_input_type::PuzzleAgentSessionCreateInput;
pub use puzzle_agent_session_get_input_type::PuzzleAgentSessionGetInput;
pub use puzzle_agent_session_procedure_result_type::PuzzleAgentSessionProcedureResult;
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_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_level_type::PuzzleDraftLevel;
pub use puzzle_event_kind_type::PuzzleEventKind;
pub use puzzle_event_table::*;
pub use puzzle_event_type::PuzzleEvent;
pub use puzzle_form_draft_save_input_type::PuzzleFormDraftSaveInput;
-pub use puzzle_form_draft_type::PuzzleFormDraft;
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_leaderboard_entry_row_type::PuzzleLeaderboardEntryRow;
pub use puzzle_leaderboard_entry_table::*;
-pub use puzzle_leaderboard_entry_type::PuzzleLeaderboardEntry;
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_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_get_input_type::PuzzleRunGetInput;
pub use puzzle_run_next_level_input_type::PuzzleRunNextLevelInput;
pub use puzzle_run_pause_input_type::PuzzleRunPauseInput;
pub use puzzle_run_procedure_result_type::PuzzleRunProcedureResult;
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_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_table::*;
pub use puzzle_select_cover_image_input_type::PuzzleSelectCoverImageInput;
@@ -1492,7 +1394,6 @@ pub use puzzle_work_point_incentive_claim_input_type::PuzzleWorkPointIncentiveCl
pub use puzzle_work_procedure_result_type::PuzzleWorkProcedureResult;
pub use puzzle_work_profile_row_type::PuzzleWorkProfileRow;
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_upsert_input_type::PuzzleWorkUpsertInput;
pub use puzzle_works_list_input_type::PuzzleWorksListInput;
@@ -1684,41 +1585,30 @@ pub use seed_analytics_date_dimensions_reducer::seed_analytics_date_dimensions;
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_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_table::*;
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_procedure_result_type::SquareHoleAgentSessionProcedureResult;
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_creator_config_snapshot_type::SquareHoleCreatorConfigSnapshot;
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_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_get_input_type::SquareHoleRunGetInput;
pub use square_hole_run_procedure_result_type::SquareHoleRunProcedureResult;
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_stop_input_type::SquareHoleRunStopInput;
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_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_get_input_type::SquareHoleWorkGetInput;
pub use square_hole_work_procedure_result_type::SquareHoleWorkProcedureResult;
pub use square_hole_work_profile_row_type::SquareHoleWorkProfileRow;
pub use square_hole_work_profile_table::*;
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_works_list_input_type::SquareHoleWorksListInput;
pub use square_hole_works_procedure_result_type::SquareHoleWorksProcedureResult;
@@ -1795,31 +1685,24 @@ pub use user_browse_history_table::*;
pub use user_browse_history_type::UserBrowseHistory;
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_snapshot_type::VisualNovelAgentMessageSnapshot;
pub use visual_novel_agent_message_submit_input_type::VisualNovelAgentMessageSubmitInput;
pub use visual_novel_agent_message_table::*;
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_procedure_result_type::VisualNovelAgentSessionProcedureResult;
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_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_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_start_input_type::VisualNovelRunStartInput;
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_snapshot_type::VisualNovelRuntimeEventSnapshot;
pub use visual_novel_runtime_event_table::*;
pub use visual_novel_runtime_event_type::VisualNovelRuntimeEvent;
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_snapshot_type::VisualNovelRuntimeHistoryEntrySnapshot;
pub use visual_novel_runtime_history_entry_table::*;
pub use visual_novel_runtime_history_list_input_type::VisualNovelRuntimeHistoryListInput;
pub use visual_novel_runtime_run_row_type::VisualNovelRuntimeRunRow;
@@ -1831,7 +1714,6 @@ pub use visual_novel_work_procedure_result_type::VisualNovelWorkProcedureResult;
pub use visual_novel_work_profile_row_type::VisualNovelWorkProfileRow;
pub use visual_novel_work_profile_table::*;
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_works_list_input_type::VisualNovelWorksListInput;
pub use visual_novel_works_procedure_result_type::VisualNovelWorksProcedureResult;
@@ -2172,7 +2054,7 @@ pub struct DbUpdate {
puzzle_agent_message: __sdk::TableUpdate,
puzzle_agent_session: __sdk::TableUpdate,
puzzle_event: __sdk::TableUpdate,
- puzzle_gallery_view: __sdk::TableUpdate,
+ puzzle_gallery_view: __sdk::TableUpdate,
puzzle_leaderboard_entry: __sdk::TableUpdate,
puzzle_runtime_run: __sdk::TableUpdate,
puzzle_work_profile: __sdk::TableUpdate,
@@ -2966,7 +2848,7 @@ impl __sdk::DbUpdate for DbUpdate {
&self.visual_novel_work_profile,
)
.with_updates_by_pk(|row| &row.profile_id);
- diff.puzzle_gallery_view = cache.apply_diff_to_table::(
+ diff.puzzle_gallery_view = cache.apply_diff_to_table::(
"puzzle_gallery_view",
&self.puzzle_gallery_view,
);
@@ -3611,7 +3493,7 @@ pub struct AppliedDiff<'r> {
puzzle_agent_message: __sdk::TableAppliedDiff<'r, PuzzleAgentMessageRow>,
puzzle_agent_session: __sdk::TableAppliedDiff<'r, PuzzleAgentSessionRow>,
puzzle_event: __sdk::TableAppliedDiff<'r, PuzzleEvent>,
- puzzle_gallery_view: __sdk::TableAppliedDiff<'r, PuzzleWorkProfile>,
+ puzzle_gallery_view: __sdk::TableAppliedDiff<'r, PuzzleGalleryViewRow>,
puzzle_leaderboard_entry: __sdk::TableAppliedDiff<'r, PuzzleLeaderboardEntryRow>,
puzzle_runtime_run: __sdk::TableAppliedDiff<'r, PuzzleRuntimeRunRow>,
puzzle_work_profile: __sdk::TableAppliedDiff<'r, PuzzleWorkProfileRow>,
@@ -3959,7 +3841,7 @@ impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> {
&self.puzzle_event,
event,
);
- callbacks.invoke_table_row_callbacks::(
+ callbacks.invoke_table_row_callbacks::(
"puzzle_gallery_view",
&self.puzzle_gallery_view,
event,
diff --git a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_gallery_view_table.rs b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_gallery_view_table.rs
index 24857cee..229fa37a 100644
--- a/server-rs/crates/spacetime-client/src/module_bindings/puzzle_gallery_view_table.rs
+++ b/server-rs/crates/spacetime-client/src/module_bindings/puzzle_gallery_view_table.rs
@@ -2,12 +2,246 @@
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![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};
+#[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,
+ 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,
+ pub asset_kind: Option,
+ pub audio_src: String,
+ pub prompt: Option,
+ pub title: Option,
+ pub updated_at: Option,
+}
+
+#[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,
+ pub ui_background_prompt: Option,
+ pub ui_background_image_src: Option,
+ pub ui_background_image_object_key: Option,
+ pub background_music: Option,
+ pub candidates: Vec,
+ pub selected_candidate_id: Option,
+ pub cover_image_src: Option,
+ pub cover_asset_id: Option,
+ 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,
+ pub author_display_name: String,
+ pub work_title: String,
+ pub work_description: String,
+ pub level_name: String,
+ pub summary: String,
+ pub theme_tags: Vec,
+ pub cover_image_src: Option,
+ pub cover_asset_id: Option,
+ pub levels: Vec,
+ pub publication_status: PuzzleGalleryPublicationStatus,
+ pub updated_at_micros: i64,
+ pub published_at_micros: Option,
+ 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 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 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 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
+ 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 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 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 for module_puzzle::PuzzlePublicationStatus {
+ fn from(status: PuzzleGalleryPublicationStatus) -> Self {
+ match status {
+ PuzzleGalleryPublicationStatus::Draft => Self::Draft,
+ PuzzleGalleryPublicationStatus::Published => Self::Published,
+ }
+ }
+}
+
+impl From 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`.
///
/// Obtain a handle from the [`PuzzleGalleryViewTableAccess::puzzle_gallery_view`] method on [`super::RemoteTables`],
@@ -17,7 +251,7 @@ use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
/// but to directly chain method calls,
/// like `ctx.db.puzzle_gallery_view().on_insert(...)`.
pub struct PuzzleGalleryViewTableHandle<'ctx> {
- imp: __sdk::TableHandle,
+ imp: __sdk::TableHandle,
ctx: std::marker::PhantomData<&'ctx super::RemoteTables>,
}
@@ -36,7 +270,7 @@ impl PuzzleGalleryViewTableAccess for super::RemoteTables {
PuzzleGalleryViewTableHandle {
imp: self
.imp
- .get_table::("puzzle_gallery_view"),
+ .get_table::("puzzle_gallery_view"),
ctx: std::marker::PhantomData,
}
}
@@ -46,13 +280,13 @@ pub struct PuzzleGalleryViewInsertCallbackId(__sdk::CallbackId);
pub struct PuzzleGalleryViewDeleteCallbackId(__sdk::CallbackId);
impl<'ctx> __sdk::Table for PuzzleGalleryViewTableHandle<'ctx> {
- type Row = PuzzleWorkProfile;
+ type Row = PuzzleGalleryViewRow;
type EventContext = super::EventContext;
fn count(&self) -> u64 {
self.imp.count()
}
- fn iter(&self) -> impl Iterator- + '_ {
+ fn iter(&self) -> impl Iterator
- + '_ {
self.imp.iter()
}
@@ -85,32 +319,32 @@ impl<'ctx> __sdk::Table for PuzzleGalleryViewTableHandle<'ctx> {
#[doc(hidden)]
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) {
- let _table = client_cache.get_or_make_table::("puzzle_gallery_view");
+ let _table = client_cache.get_or_make_table::("puzzle_gallery_view");
}
#[doc(hidden)]
pub(super) fn parse_table_update(
raw_updates: __ws::v2::TableUpdate,
-) -> __sdk::Result<__sdk::TableUpdate> {
+) -> __sdk::Result<__sdk::TableUpdate> {
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
- __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate")
+ __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate")
.with_cause(e)
.into()
})
}
#[allow(non_camel_case_types)]
-/// Extension trait for query builder access to the table `PuzzleWorkProfile`.
+/// Extension trait for query builder access to the table `PuzzleGalleryViewRow`.
///
/// Implemented for [`__sdk::QueryTableAccessor`].
pub trait puzzle_gallery_viewQueryTableAccess {
#[allow(non_snake_case)]
- /// Get a query builder for the table `PuzzleWorkProfile`.
- fn puzzle_gallery_view(&self) -> __sdk::__query_builder::Table;
+ /// Get a query builder for the table `PuzzleGalleryViewRow`.
+ fn puzzle_gallery_view(&self) -> __sdk::__query_builder::Table;
}
impl puzzle_gallery_viewQueryTableAccess for __sdk::QueryTableAccessor {
- fn puzzle_gallery_view(&self) -> __sdk::__query_builder::Table {
+ fn puzzle_gallery_view(&self) -> __sdk::__query_builder::Table {
__sdk::__query_builder::Table::new("puzzle_gallery_view")
}
}
diff --git a/server-rs/crates/spacetime-client/src/npc.rs b/server-rs/crates/spacetime-client/src/npc.rs
index 77635c7b..61d33d2b 100644
--- a/server-rs/crates/spacetime-client/src/npc.rs
+++ b/server-rs/crates/spacetime-client/src/npc.rs
@@ -9,19 +9,22 @@ impl SpacetimeClient {
validate_npc_battle_interaction_input(&input)?;
let procedure_input = input.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .resolve_npc_battle_interaction_and_return_then(
- procedure_input,
- move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_npc_battle_interaction_procedure_result);
- send_once(&sender, mapped);
- },
- );
- })
+ self.call_after_connect(
+ "resolve_npc_battle_interaction_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .resolve_npc_battle_interaction_and_return_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_npc_battle_interaction_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
}
diff --git a/server-rs/crates/spacetime-client/src/puzzle.rs b/server-rs/crates/spacetime-client/src/puzzle.rs
index 5426e756..1006e6f1 100644
--- a/server-rs/crates/spacetime-client/src/puzzle.rs
+++ b/server-rs/crates/spacetime-client/src/puzzle.rs
@@ -59,7 +59,7 @@ impl SpacetimeClient {
created_at_micros: input.created_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("create_puzzle_agent_session", move |connection, sender| {
connection.procedures().create_puzzle_agent_session_then(
procedure_input,
move |_, result| {
@@ -83,7 +83,7 @@ impl SpacetimeClient {
owner_user_id,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_puzzle_agent_session", move |connection, sender| {
connection.procedures().get_puzzle_agent_session_then(
procedure_input,
move |_, result| {
@@ -108,7 +108,7 @@ impl SpacetimeClient {
saved_at_micros: input.saved_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("save_puzzle_form_draft", move |connection, sender| {
connection.procedures().save_puzzle_form_draft_then(
procedure_input,
move |_, result| {
@@ -134,7 +134,7 @@ impl SpacetimeClient {
submitted_at_micros: input.submitted_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("submit_puzzle_agent_message", move |connection, sender| {
connection.procedures().submit_puzzle_agent_message_then(
procedure_input,
move |_, result| {
@@ -164,16 +164,19 @@ impl SpacetimeClient {
updated_at_micros: input.updated_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .finalize_puzzle_agent_message_turn_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_puzzle_agent_session_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "finalize_puzzle_agent_message_turn",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .finalize_puzzle_agent_message_turn_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_puzzle_agent_session_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -189,7 +192,7 @@ impl SpacetimeClient {
compiled_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("compile_puzzle_agent_draft", move |connection, sender| {
connection.procedures().compile_puzzle_agent_draft_then(
procedure_input,
move |_, result| {
@@ -216,7 +219,7 @@ impl SpacetimeClient {
saved_at_micros: input.saved_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("save_puzzle_generated_images", move |connection, sender| {
connection.procedures().save_puzzle_generated_images_then(
procedure_input,
move |_, result| {
@@ -245,7 +248,7 @@ impl SpacetimeClient {
saved_at_micros: input.saved_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("save_puzzle_ui_background", move |connection, sender| {
connection.procedures().save_puzzle_ui_background_then(
procedure_input,
move |_, result| {
@@ -271,7 +274,7 @@ impl SpacetimeClient {
selected_at_micros: input.selected_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("select_puzzle_cover_image", move |connection, sender| {
connection.procedures().select_puzzle_cover_image_then(
procedure_input,
move |_, result| {
@@ -304,7 +307,7 @@ impl SpacetimeClient {
published_at_micros: input.published_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("publish_puzzle_work", move |connection, sender| {
connection
.procedures()
.publish_puzzle_work_then(procedure_input, move |_, result| {
@@ -323,7 +326,7 @@ impl SpacetimeClient {
) -> Result, SpacetimeClientError> {
let procedure_input = PuzzleWorksListInput { owner_user_id };
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("list_puzzle_works", move |connection, sender| {
connection
.procedures()
.list_puzzle_works_then(procedure_input, move |_, result| {
@@ -342,7 +345,7 @@ impl SpacetimeClient {
) -> Result {
let procedure_input = PuzzleWorkGetInput { profile_id };
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_puzzle_work_detail", move |connection, sender| {
connection.procedures().get_puzzle_work_detail_then(
procedure_input,
move |_, result| {
@@ -374,7 +377,7 @@ impl SpacetimeClient {
updated_at_micros: input.updated_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("update_puzzle_work", move |connection, sender| {
connection
.procedures()
.update_puzzle_work_then(procedure_input, move |_, result| {
@@ -397,7 +400,7 @@ impl SpacetimeClient {
owner_user_id,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("delete_puzzle_work", move |connection, sender| {
connection
.procedures()
.delete_puzzle_work_then(procedure_input, move |_, result| {
@@ -420,16 +423,19 @@ impl SpacetimeClient {
claimed_at_micros: input.claimed_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .claim_puzzle_work_point_incentive_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
- .and_then(map_puzzle_work_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "claim_puzzle_work_point_incentive",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .claim_puzzle_work_point_incentive_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
+ .and_then(map_puzzle_work_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -443,7 +449,7 @@ impl SpacetimeClient {
Ok(items
.into_iter()
.map(|item| {
- let mut record = map_puzzle_work_profile(item);
+ let mut record = map_puzzle_work_profile(item.into());
record.recent_play_count_7d = recent_play_counts
.get(&record.profile_id)
.copied()
@@ -461,7 +467,7 @@ impl SpacetimeClient {
) -> Result {
let procedure_input = PuzzleWorkGetInput { profile_id };
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_puzzle_gallery_detail", move |connection, sender| {
connection.procedures().get_puzzle_gallery_detail_then(
procedure_input,
move |_, result| {
@@ -485,7 +491,7 @@ impl SpacetimeClient {
liked_at_micros: input.liked_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("record_puzzle_work_like", move |connection, sender| {
connection.procedures().record_puzzle_work_like_then(
procedure_input,
move |_, result| {
@@ -514,7 +520,7 @@ impl SpacetimeClient {
remixed_at_micros: input.remixed_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("remix_puzzle_work", move |connection, sender| {
connection
.procedures()
.remix_puzzle_work_then(procedure_input, move |_, result| {
@@ -539,7 +545,7 @@ impl SpacetimeClient {
started_at_micros: input.started_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("start_puzzle_run", move |connection, sender| {
connection
.procedures()
.start_puzzle_run_then(procedure_input, move |_, result| {
@@ -562,7 +568,7 @@ impl SpacetimeClient {
owner_user_id,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_puzzle_run", move |connection, sender| {
connection
.procedures()
.get_puzzle_run_then(procedure_input, move |_, result| {
@@ -587,7 +593,7 @@ impl SpacetimeClient {
swapped_at_micros: input.swapped_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("swap_puzzle_pieces", move |connection, sender| {
connection
.procedures()
.swap_puzzle_pieces_then(procedure_input, move |_, result| {
@@ -613,7 +619,7 @@ impl SpacetimeClient {
dragged_at_micros: input.dragged_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("drag_puzzle_piece_or_group", move |connection, sender| {
connection.procedures().drag_puzzle_piece_or_group_then(
procedure_input,
move |_, result| {
@@ -638,7 +644,7 @@ impl SpacetimeClient {
advanced_at_micros: input.advanced_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("advance_puzzle_next_level", move |connection, sender| {
connection.procedures().advance_puzzle_next_level_then(
procedure_input,
move |_, result| {
@@ -663,7 +669,7 @@ impl SpacetimeClient {
updated_at_micros: input.updated_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("update_puzzle_run_pause", move |connection, sender| {
connection.procedures().update_puzzle_run_pause_then(
procedure_input,
move |_, result| {
@@ -689,7 +695,7 @@ impl SpacetimeClient {
spent_points: input.spent_points,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("use_puzzle_runtime_prop", move |connection, sender| {
connection.procedures().use_puzzle_runtime_prop_then(
procedure_input,
move |_, result| {
@@ -717,16 +723,19 @@ impl SpacetimeClient {
submitted_at_micros: input.submitted_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .submit_puzzle_leaderboard_entry_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_puzzle_run_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "submit_puzzle_leaderboard_entry",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .submit_puzzle_leaderboard_entry_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_puzzle_run_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
}
diff --git a/server-rs/crates/spacetime-client/src/runtime.rs b/server-rs/crates/spacetime-client/src/runtime.rs
index 3ecd0d1f..a4b3aa29 100644
--- a/server-rs/crates/spacetime-client/src/runtime.rs
+++ b/server-rs/crates/spacetime-client/src/runtime.rs
@@ -4,7 +4,7 @@ impl SpacetimeClient {
pub async fn get_creation_entry_config(
&self,
) -> Result {
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_creation_entry_config", move |connection, sender| {
connection
.procedures()
.get_creation_entry_config_then(move |_, result| {
@@ -22,16 +22,19 @@ impl SpacetimeClient {
input: module_runtime::CreationEntryTypeAdminUpsertInput,
) -> Result {
let procedure_input: CreationEntryTypeAdminUpsertInput = input.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .upsert_creation_entry_type_config_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_creation_entry_config_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "upsert_creation_entry_type_config",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .upsert_creation_entry_type_config_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_creation_entry_config_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -43,17 +46,20 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection.procedures().get_runtime_setting_or_default_then(
- procedure_input,
- move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_setting_procedure_result);
- send_once(&sender, mapped);
- },
- );
- })
+ self.call_after_connect(
+ "get_runtime_setting_or_default",
+ move |connection, sender| {
+ connection.procedures().get_runtime_setting_or_default_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_setting_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -65,7 +71,7 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("list_platform_browse_history", move |connection, sender| {
connection.procedures().list_platform_browse_history_then(
procedure_input,
move |_, result| {
@@ -87,7 +93,7 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_profile_dashboard", move |connection, sender| {
connection.procedures().get_profile_dashboard_then(
procedure_input,
move |_, result| {
@@ -109,7 +115,7 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("list_profile_wallet_ledger", move |connection, sender| {
connection.procedures().list_profile_wallet_ledger_then(
procedure_input,
move |_, result| {
@@ -131,19 +137,22 @@ impl SpacetimeClient {
.map_err(|error| SpacetimeClientError::Runtime(error.to_string()))?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .grant_new_user_registration_wallet_reward_then(
- procedure_input,
- move |_, result| {
- let mapped = result
- .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
- .and_then(map_runtime_profile_wallet_adjustment_procedure_result);
- send_once(&sender, mapped);
- },
- );
- })
+ self.call_after_connect(
+ "grant_new_user_registration_wallet_reward",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .grant_new_user_registration_wallet_reward_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
+ .and_then(map_runtime_profile_wallet_adjustment_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -163,19 +172,22 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .consume_profile_wallet_points_and_return_then(
- procedure_input,
- move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_profile_wallet_adjustment_procedure_result);
- send_once(&sender, mapped);
- },
- );
- })
+ self.call_after_connect(
+ "consume_profile_wallet_points_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .consume_profile_wallet_points_and_return_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_profile_wallet_adjustment_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -195,16 +207,22 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .refund_profile_wallet_points_and_return_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_profile_wallet_adjustment_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "refund_profile_wallet_points_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .refund_profile_wallet_points_and_return_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_profile_wallet_adjustment_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -216,7 +234,7 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_profile_recharge_center", move |connection, sender| {
connection.procedures().get_profile_recharge_center_then(
procedure_input,
move |_, result| {
@@ -252,19 +270,22 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .create_profile_recharge_order_and_return_then(
- procedure_input,
- move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_profile_recharge_order_procedure_result);
- send_once(&sender, mapped);
- },
- );
- })
+ self.call_after_connect(
+ "create_profile_recharge_order_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .create_profile_recharge_order_and_return_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_profile_recharge_order_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -282,16 +303,22 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .get_profile_recharge_order_and_return_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_profile_recharge_order_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "get_profile_recharge_order_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .get_profile_recharge_order_and_return_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_profile_recharge_order_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -315,19 +342,22 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .mark_profile_recharge_order_paid_and_return_then(
- procedure_input,
- move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_profile_recharge_order_procedure_result);
- send_once(&sender, mapped);
- },
- );
- })
+ self.call_after_connect(
+ "mark_profile_recharge_order_paid_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .mark_profile_recharge_order_paid_and_return_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_profile_recharge_order_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -349,16 +379,19 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .submit_profile_feedback_and_return_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_profile_feedback_submission_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "submit_profile_feedback_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .submit_profile_feedback_and_return_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_profile_feedback_submission_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -370,16 +403,19 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .get_profile_referral_invite_center_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_referral_invite_center_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "get_profile_referral_invite_center",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .get_profile_referral_invite_center_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_referral_invite_center_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -394,16 +430,19 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .redeem_profile_referral_invite_code_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_referral_redeem_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "redeem_profile_referral_invite_code",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .redeem_profile_referral_invite_code_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_referral_redeem_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -418,7 +457,7 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("redeem_profile_reward_code", move |connection, sender| {
connection.procedures().redeem_profile_reward_code_then(
procedure_input,
move |_, result| {
@@ -481,16 +520,19 @@ impl SpacetimeClient {
occurred_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .record_tracking_event_and_return_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_tracking_event_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "record_tracking_event_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .record_tracking_event_and_return_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_tracking_event_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -502,7 +544,7 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_profile_task_center", move |connection, sender| {
connection.procedures().get_profile_task_center_then(
procedure_input,
move |_, result| {
@@ -525,16 +567,22 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .claim_profile_task_reward_and_return_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_profile_task_claim_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "claim_profile_task_reward_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .claim_profile_task_reward_and_return_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_profile_task_claim_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -550,7 +598,7 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("query_analytics_metric", move |connection, sender| {
connection.procedures().query_analytics_metric_then(
procedure_input,
move |_, result| {
@@ -572,16 +620,19 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .admin_list_profile_task_configs_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_profile_task_config_admin_list_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "admin_list_profile_task_configs",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .admin_list_profile_task_configs_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_profile_task_config_admin_list_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -617,16 +668,19 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .admin_upsert_profile_task_config_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_profile_task_config_admin_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "admin_upsert_profile_task_config",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .admin_upsert_profile_task_config_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_profile_task_config_admin_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -644,16 +698,19 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .admin_disable_profile_task_config_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_profile_task_config_admin_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "admin_disable_profile_task_config",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .admin_disable_profile_task_config_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_profile_task_config_admin_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -666,16 +723,24 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .admin_list_profile_recharge_products_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_profile_recharge_product_admin_list_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "admin_list_profile_recharge_products",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .admin_list_profile_recharge_products_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(
+ map_runtime_profile_recharge_product_admin_list_procedure_result,
+ );
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -716,16 +781,24 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .admin_upsert_profile_recharge_product_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_profile_recharge_product_admin_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "admin_upsert_profile_recharge_product",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .admin_upsert_profile_recharge_product_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(
+ map_runtime_profile_recharge_product_admin_procedure_result,
+ );
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -755,16 +828,19 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .admin_upsert_profile_redeem_code_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_profile_redeem_code_admin_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "admin_upsert_profile_redeem_code",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .admin_upsert_profile_redeem_code_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_profile_redeem_code_admin_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -776,16 +852,19 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .admin_list_profile_redeem_codes_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_profile_redeem_code_admin_list_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "admin_list_profile_redeem_codes",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .admin_list_profile_redeem_codes_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_profile_redeem_code_admin_list_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -803,16 +882,19 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .admin_disable_profile_redeem_code_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_profile_redeem_code_admin_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "admin_disable_profile_redeem_code",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .admin_disable_profile_redeem_code_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_profile_redeem_code_admin_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -836,16 +918,19 @@ impl SpacetimeClient {
.map_err(|error| SpacetimeClientError::Runtime(error.to_string()))?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .admin_upsert_profile_invite_code_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
- .and_then(map_runtime_profile_invite_code_admin_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "admin_upsert_profile_invite_code",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .admin_upsert_profile_invite_code_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
+ .and_then(map_runtime_profile_invite_code_admin_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -857,16 +942,19 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .admin_list_profile_invite_codes_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_profile_invite_code_admin_list_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "admin_list_profile_invite_codes",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .admin_list_profile_invite_codes_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_profile_invite_code_admin_list_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -878,7 +966,7 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_profile_play_stats", move |connection, sender| {
connection.procedures().get_profile_play_stats_then(
procedure_input,
move |_, result| {
@@ -900,7 +988,7 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_runtime_snapshot", move |connection, sender| {
connection
.procedures()
.get_runtime_snapshot_then(procedure_input, move |_, result| {
@@ -933,16 +1021,19 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .upsert_runtime_snapshot_and_return_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_snapshot_required_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "upsert_runtime_snapshot_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .upsert_runtime_snapshot_and_return_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_snapshot_required_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -954,16 +1045,19 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .delete_runtime_snapshot_and_return_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_snapshot_delete_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "delete_runtime_snapshot_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .delete_runtime_snapshot_and_return_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_snapshot_delete_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -975,7 +1069,7 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("list_profile_save_archives", move |connection, sender| {
connection.procedures().list_profile_save_archives_then(
procedure_input,
move |_, result| {
@@ -999,16 +1093,22 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .resume_profile_save_archive_and_return_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_profile_save_archive_resume_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "resume_profile_save_archive_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .resume_profile_save_archive_and_return_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_profile_save_archive_resume_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -1028,16 +1128,19 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .upsert_runtime_setting_and_return_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_setting_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "upsert_runtime_setting_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .upsert_runtime_setting_and_return_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_setting_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -1052,19 +1155,22 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .upsert_platform_browse_history_and_return_then(
- procedure_input,
- move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_browse_history_procedure_result);
- send_once(&sender, mapped);
- },
- );
- })
+ self.call_after_connect(
+ "upsert_platform_browse_history_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .upsert_platform_browse_history_and_return_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_browse_history_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -1076,19 +1182,22 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .clear_platform_browse_history_and_return_then(
- procedure_input,
- move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_runtime_browse_history_procedure_result);
- send_once(&sender, mapped);
- },
- );
- })
+ self.call_after_connect(
+ "clear_platform_browse_history_and_return",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .clear_platform_browse_history_and_return_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_runtime_browse_history_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
}
diff --git a/server-rs/crates/spacetime-client/src/square_hole.rs b/server-rs/crates/spacetime-client/src/square_hole.rs
index f0ade205..ffeb616f 100644
--- a/server-rs/crates/spacetime-client/src/square_hole.rs
+++ b/server-rs/crates/spacetime-client/src/square_hole.rs
@@ -16,16 +16,19 @@ impl SpacetimeClient {
created_at_micros: input.created_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .create_square_hole_agent_session_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
- .and_then(map_square_hole_agent_session_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "create_square_hole_agent_session",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .create_square_hole_agent_session_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
+ .and_then(map_square_hole_agent_session_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -39,17 +42,20 @@ impl SpacetimeClient {
owner_user_id,
};
- self.call_after_connect(move |connection, sender| {
- connection.procedures().get_square_hole_agent_session_then(
- procedure_input,
- move |_, result| {
- let mapped = result
- .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
- .and_then(map_square_hole_agent_session_procedure_result);
- send_once(&sender, mapped);
- },
- );
- })
+ self.call_after_connect(
+ "get_square_hole_agent_session",
+ move |connection, sender| {
+ connection.procedures().get_square_hole_agent_session_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
+ .and_then(map_square_hole_agent_session_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -65,16 +71,19 @@ impl SpacetimeClient {
submitted_at_micros: input.submitted_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .submit_square_hole_agent_message_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
- .and_then(map_square_hole_agent_session_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "submit_square_hole_agent_message",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .submit_square_hole_agent_message_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
+ .and_then(map_square_hole_agent_session_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -94,16 +103,22 @@ impl SpacetimeClient {
error_message: input.error_message,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .finalize_square_hole_agent_message_turn_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
- .and_then(map_square_hole_agent_session_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "finalize_square_hole_agent_message_turn",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .finalize_square_hole_agent_message_turn_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
+ .and_then(map_square_hole_agent_session_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -123,7 +138,7 @@ impl SpacetimeClient {
compiled_at_micros: input.compiled_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("compile_square_hole_draft", move |connection, sender| {
connection.procedures().compile_square_hole_draft_then(
procedure_input,
move |_, result| {
@@ -159,7 +174,7 @@ impl SpacetimeClient {
updated_at_micros: input.updated_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("update_square_hole_work", move |connection, sender| {
connection.procedures().update_square_hole_work_then(
procedure_input,
move |_, result| {
@@ -185,7 +200,7 @@ impl SpacetimeClient {
published_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("publish_square_hole_work", move |connection, sender| {
connection.procedures().publish_square_hole_work_then(
procedure_input,
move |_, result| {
@@ -225,7 +240,7 @@ impl SpacetimeClient {
&self,
procedure_input: SquareHoleWorksListInput,
) -> Result, SpacetimeClientError> {
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("list_square_hole_works", move |connection, sender| {
connection.procedures().list_square_hole_works_then(
procedure_input,
move |_, result| {
@@ -249,7 +264,7 @@ impl SpacetimeClient {
owner_user_id,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_square_hole_work_detail", move |connection, sender| {
connection.procedures().get_square_hole_work_detail_then(
procedure_input,
move |_, result| {
@@ -273,7 +288,7 @@ impl SpacetimeClient {
owner_user_id,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("delete_square_hole_work", move |connection, sender| {
connection.procedures().delete_square_hole_work_then(
procedure_input,
move |_, result| {
@@ -298,7 +313,7 @@ impl SpacetimeClient {
started_at_ms: input.started_at_ms,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("start_square_hole_run", move |connection, sender| {
connection.procedures().start_square_hole_run_then(
procedure_input,
move |_, result| {
@@ -322,7 +337,7 @@ impl SpacetimeClient {
owner_user_id,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_square_hole_run", move |connection, sender| {
connection
.procedures()
.get_square_hole_run_then(procedure_input, move |_, result| {
@@ -349,7 +364,7 @@ impl SpacetimeClient {
dropped_at_ms: input.dropped_at_ms,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("drop_square_hole_shape", move |connection, sender| {
connection.procedures().drop_square_hole_shape_then(
procedure_input,
move |_, result| {
@@ -379,7 +394,7 @@ impl SpacetimeClient {
stopped_at_ms: input.stopped_at_ms,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("stop_square_hole_run", move |connection, sender| {
connection
.procedures()
.stop_square_hole_run_then(procedure_input, move |_, result| {
@@ -403,7 +418,7 @@ impl SpacetimeClient {
restarted_at_ms: input.restarted_at_ms,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("restart_square_hole_run", move |connection, sender| {
connection.procedures().restart_square_hole_run_then(
procedure_input,
move |_, result| {
@@ -427,7 +442,7 @@ impl SpacetimeClient {
finished_at_ms: input.finished_at_ms,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("finish_square_hole_time_up", move |connection, sender| {
connection.procedures().finish_square_hole_time_up_then(
procedure_input,
move |_, result| {
diff --git a/server-rs/crates/spacetime-client/src/story.rs b/server-rs/crates/spacetime-client/src/story.rs
index c04d02d1..d341385f 100644
--- a/server-rs/crates/spacetime-client/src/story.rs
+++ b/server-rs/crates/spacetime-client/src/story.rs
@@ -23,17 +23,20 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
- connection.procedures().begin_story_session_and_return_then(
- procedure_input,
- move |_, result| {
- let mapped = result
- .map_err(SpacetimeClientError::from_sdk_error)
- .and_then(map_story_session_procedure_result);
- send_once(&sender, mapped);
- },
- );
- })
+ self.call_after_connect(
+ "begin_story_session_and_return",
+ move |connection, sender| {
+ connection.procedures().begin_story_session_and_return_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(SpacetimeClientError::from_sdk_error)
+ .and_then(map_story_session_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -55,7 +58,7 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("continue_story_and_return", move |connection, sender| {
connection.procedures().continue_story_and_return_then(
procedure_input,
move |_, result| {
@@ -77,7 +80,7 @@ impl SpacetimeClient {
.map_err(SpacetimeClientError::validation_failed)?
.into();
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_story_session_state", move |connection, sender| {
connection.procedures().get_story_session_state_then(
procedure_input,
move |_, result| {
diff --git a/server-rs/crates/spacetime-client/src/telemetry.rs b/server-rs/crates/spacetime-client/src/telemetry.rs
new file mode 100644
index 00000000..9fdd9885
--- /dev/null
+++ b/server-rs/crates/spacetime-client/src/telemetry.rs
@@ -0,0 +1,75 @@
+use std::time::Duration;
+
+use opentelemetry::{KeyValue, global, metrics::Counter};
+
+use crate::SpacetimeClientError;
+
+// SpacetimeDB procedure 指标只使用 procedure / status_class 等低基数字段,避免把用户或作品 ID 写进指标标签。
+pub(crate) struct ProcedureMetricsGuard {
+ procedure: &'static str,
+ started_at: std::time::Instant,
+}
+
+pub(crate) fn begin_procedure(procedure: &'static str) -> ProcedureMetricsGuard {
+ ProcedureMetricsGuard {
+ procedure,
+ started_at: std::time::Instant::now(),
+ }
+}
+
+impl ProcedureMetricsGuard {
+ pub(crate) fn finish(&self, result: &Result) {
+ let duration = self.started_at.elapsed();
+ record_procedure(self.procedure, duration, result.is_err());
+ }
+}
+
+struct SpacetimeMetrics {
+ calls: Counter,
+ errors: Counter,
+ duration_ms: opentelemetry::metrics::Histogram,
+}
+
+fn spacetime_metrics() -> &'static SpacetimeMetrics {
+ static METRICS: std::sync::OnceLock = std::sync::OnceLock::new();
+ METRICS.get_or_init(|| {
+ let meter = global::meter("genarrative-spacetime-client");
+ SpacetimeMetrics {
+ calls: meter
+ .u64_counter("genarrative.spacetime.procedure.calls")
+ .with_description("SpacetimeDB procedure call count")
+ .build(),
+ errors: meter
+ .u64_counter("genarrative.spacetime.procedure.errors")
+ .with_description("SpacetimeDB procedure error count")
+ .build(),
+ duration_ms: meter
+ .f64_histogram("genarrative.spacetime.procedure.duration_ms")
+ .with_unit("ms")
+ .with_description("SpacetimeDB procedure duration in milliseconds")
+ .build(),
+ }
+ })
+}
+
+fn record_procedure(
+ procedure: &'static str,
+ duration: Duration,
+ failed: bool,
+) {
+ let labels = vec![
+ KeyValue::new("procedure", procedure),
+ KeyValue::new(
+ "status_class",
+ if failed { "error" } else { "ok" },
+ ),
+ ];
+ let metrics = spacetime_metrics();
+ metrics.calls.add(1, &labels);
+ metrics
+ .duration_ms
+ .record(duration.as_secs_f64() * 1000.0, &labels);
+ if failed {
+ metrics.errors.add(1, &labels);
+ }
+}
diff --git a/server-rs/crates/spacetime-client/src/visual_novel.rs b/server-rs/crates/spacetime-client/src/visual_novel.rs
index bbc8226a..bbf7a00f 100644
--- a/server-rs/crates/spacetime-client/src/visual_novel.rs
+++ b/server-rs/crates/spacetime-client/src/visual_novel.rs
@@ -28,16 +28,19 @@ impl SpacetimeClient {
created_at_micros: input.created_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .create_visual_novel_agent_session_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
- .and_then(map_visual_novel_agent_session_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "create_visual_novel_agent_session",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .create_visual_novel_agent_session_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
+ .and_then(map_visual_novel_agent_session_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -51,17 +54,20 @@ impl SpacetimeClient {
owner_user_id,
};
- self.call_after_connect(move |connection, sender| {
- connection.procedures().get_visual_novel_agent_session_then(
- procedure_input,
- move |_, result| {
- let mapped = result
- .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
- .and_then(map_visual_novel_agent_session_procedure_result);
- send_once(&sender, mapped);
- },
- );
- })
+ self.call_after_connect(
+ "get_visual_novel_agent_session",
+ move |connection, sender| {
+ connection.procedures().get_visual_novel_agent_session_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
+ .and_then(map_visual_novel_agent_session_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -77,16 +83,19 @@ impl SpacetimeClient {
submitted_at_micros: input.submitted_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .submit_visual_novel_agent_message_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
- .and_then(map_visual_novel_agent_session_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "submit_visual_novel_agent_message",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .submit_visual_novel_agent_message_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
+ .and_then(map_visual_novel_agent_session_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -107,19 +116,22 @@ impl SpacetimeClient {
error_message: input.error_message,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .finalize_visual_novel_agent_message_turn_then(
- procedure_input,
- move |_, result| {
- let mapped = result
- .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
- .and_then(map_visual_novel_agent_session_procedure_result);
- send_once(&sender, mapped);
- },
- );
- })
+ self.call_after_connect(
+ "finalize_visual_novel_agent_message_turn",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .finalize_visual_novel_agent_message_turn_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
+ .and_then(map_visual_novel_agent_session_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -140,16 +152,19 @@ impl SpacetimeClient {
compiled_at_micros: input.compiled_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .compile_visual_novel_work_profile_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
- .and_then(map_visual_novel_agent_session_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "compile_visual_novel_work_profile",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .compile_visual_novel_work_profile_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
+ .and_then(map_visual_novel_agent_session_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -170,7 +185,7 @@ impl SpacetimeClient {
updated_at_micros: input.updated_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("update_visual_novel_work", move |connection, sender| {
connection.procedures().update_visual_novel_work_then(
procedure_input,
move |_, result| {
@@ -196,7 +211,7 @@ impl SpacetimeClient {
published_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("publish_visual_novel_work", move |connection, sender| {
connection.procedures().publish_visual_novel_work_then(
procedure_input,
move |_, result| {
@@ -236,7 +251,7 @@ impl SpacetimeClient {
&self,
procedure_input: VisualNovelWorksListInput,
) -> Result, SpacetimeClientError> {
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("list_visual_novel_works", move |connection, sender| {
connection.procedures().list_visual_novel_works_then(
procedure_input,
move |_, result| {
@@ -260,7 +275,7 @@ impl SpacetimeClient {
owner_user_id,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_visual_novel_work_detail", move |connection, sender| {
connection.procedures().get_visual_novel_work_detail_then(
procedure_input,
move |_, result| {
@@ -284,7 +299,7 @@ impl SpacetimeClient {
owner_user_id,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("delete_visual_novel_work", move |connection, sender| {
connection.procedures().delete_visual_novel_work_then(
procedure_input,
move |_, result| {
@@ -311,7 +326,7 @@ impl SpacetimeClient {
started_at_micros: input.started_at_micros,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("start_visual_novel_run", move |connection, sender| {
connection.procedures().start_visual_novel_run_then(
procedure_input,
move |_, result| {
@@ -335,7 +350,7 @@ impl SpacetimeClient {
owner_user_id,
};
- self.call_after_connect(move |connection, sender| {
+ self.call_after_connect("get_visual_novel_run", move |connection, sender| {
connection
.procedures()
.get_visual_novel_run_then(procedure_input, move |_, result| {
@@ -367,16 +382,19 @@ impl SpacetimeClient {
updated_at_micros: input.updated_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .upsert_visual_novel_run_snapshot_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
- .and_then(map_visual_novel_run_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "upsert_visual_novel_run_snapshot",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .upsert_visual_novel_run_snapshot_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
+ .and_then(map_visual_novel_run_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -397,19 +415,22 @@ impl SpacetimeClient {
created_at_micros: input.created_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .append_visual_novel_runtime_history_entry_then(
- procedure_input,
- move |_, result| {
- let mapped = result
- .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
- .and_then(map_visual_novel_history_procedure_result);
- send_once(&sender, mapped);
- },
- );
- })
+ self.call_after_connect(
+ "append_visual_novel_runtime_history_entry",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .append_visual_novel_runtime_history_entry_then(
+ procedure_input,
+ move |_, result| {
+ let mapped = result
+ .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
+ .and_then(map_visual_novel_history_procedure_result);
+ send_once(&sender, mapped);
+ },
+ );
+ },
+ )
.await
}
@@ -423,16 +444,19 @@ impl SpacetimeClient {
owner_user_id,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .list_visual_novel_runtime_history_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
- .and_then(map_visual_novel_history_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "list_visual_novel_runtime_history",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .list_visual_novel_runtime_history_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
+ .and_then(map_visual_novel_history_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
@@ -452,16 +476,19 @@ impl SpacetimeClient {
occurred_at_micros: input.occurred_at_micros,
};
- self.call_after_connect(move |connection, sender| {
- connection
- .procedures()
- .record_visual_novel_runtime_event_then(procedure_input, move |_, result| {
- let mapped = result
- .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
- .and_then(map_visual_novel_runtime_event_procedure_result);
- send_once(&sender, mapped);
- });
- })
+ self.call_after_connect(
+ "record_visual_novel_runtime_event",
+ move |connection, sender| {
+ connection
+ .procedures()
+ .record_visual_novel_runtime_event_then(procedure_input, move |_, result| {
+ let mapped = result
+ .map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
+ .and_then(map_visual_novel_runtime_event_procedure_result);
+ send_once(&sender, mapped);
+ });
+ },
+ )
.await
}
}
diff --git a/server-rs/crates/spacetime-module/src/puzzle.rs b/server-rs/crates/spacetime-module/src/puzzle.rs
index 28f75c1e..512c3c6c 100644
--- a/server-rs/crates/spacetime-module/src/puzzle.rs
+++ b/server-rs/crates/spacetime-module/src/puzzle.rs
@@ -216,12 +216,12 @@ pub fn create_puzzle_agent_session(
match ctx.try_with_tx(|tx| create_puzzle_agent_session_tx(tx, input.clone())) {
Ok(session) => PuzzleAgentSessionProcedureResult {
ok: true,
- session: Some(session),
+ session_json: Some(serialize_json(&session)),
error_message: None,
},
Err(message) => PuzzleAgentSessionProcedureResult {
ok: false,
- session: None,
+ session_json: None,
error_message: Some(message),
},
}
@@ -235,12 +235,12 @@ pub fn get_puzzle_agent_session(
match ctx.try_with_tx(|tx| get_puzzle_agent_session_tx(tx, input.clone())) {
Ok(session) => PuzzleAgentSessionProcedureResult {
ok: true,
- session: Some(session),
+ session_json: Some(serialize_json(&session)),
error_message: None,
},
Err(message) => PuzzleAgentSessionProcedureResult {
ok: false,
- session: None,
+ session_json: None,
error_message: Some(message),
},
}
@@ -254,12 +254,12 @@ pub fn submit_puzzle_agent_message(
match ctx.try_with_tx(|tx| submit_puzzle_agent_message_tx(tx, input.clone())) {
Ok(session) => PuzzleAgentSessionProcedureResult {
ok: true,
- session: Some(session),
+ session_json: Some(serialize_json(&session)),
error_message: None,
},
Err(message) => PuzzleAgentSessionProcedureResult {
ok: false,
- session: None,
+ session_json: None,
error_message: Some(message),
},
}
@@ -273,12 +273,12 @@ pub fn finalize_puzzle_agent_message_turn(
match ctx.try_with_tx(|tx| finalize_puzzle_agent_message_turn_tx(tx, input.clone())) {
Ok(session) => PuzzleAgentSessionProcedureResult {
ok: true,
- session: Some(session),
+ session_json: Some(serialize_json(&session)),
error_message: None,
},
Err(message) => PuzzleAgentSessionProcedureResult {
ok: false,
- session: None,
+ session_json: None,
error_message: Some(message),
},
}
@@ -292,12 +292,12 @@ pub fn compile_puzzle_agent_draft(
match ctx.try_with_tx(|tx| compile_puzzle_agent_draft_tx(tx, input.clone())) {
Ok(session) => PuzzleAgentSessionProcedureResult {
ok: true,
- session: Some(session),
+ session_json: Some(serialize_json(&session)),
error_message: None,
},
Err(message) => PuzzleAgentSessionProcedureResult {
ok: false,
- session: None,
+ session_json: None,
error_message: Some(message),
},
}
@@ -313,12 +313,12 @@ pub fn save_puzzle_form_draft(
match ctx.try_with_tx(|tx| save_puzzle_form_draft_tx(tx, input.clone())) {
Ok(session) => PuzzleAgentSessionProcedureResult {
ok: true,
- session: Some(session),
+ session_json: Some(serialize_json(&session)),
error_message: None,
},
Err(message) => PuzzleAgentSessionProcedureResult {
ok: false,
- session: None,
+ session_json: None,
error_message: Some(message),
},
}
@@ -332,12 +332,12 @@ pub fn save_puzzle_generated_images(
match ctx.try_with_tx(|tx| save_puzzle_generated_images_tx(tx, input.clone())) {
Ok(session) => PuzzleAgentSessionProcedureResult {
ok: true,
- session: Some(session),
+ session_json: Some(serialize_json(&session)),
error_message: None,
},
Err(message) => PuzzleAgentSessionProcedureResult {
ok: false,
- session: None,
+ session_json: None,
error_message: Some(message),
},
}
@@ -351,12 +351,12 @@ pub fn save_puzzle_ui_background(
match ctx.try_with_tx(|tx| save_puzzle_ui_background_tx(tx, input.clone())) {
Ok(session) => PuzzleAgentSessionProcedureResult {
ok: true,
- session: Some(session),
+ session_json: Some(serialize_json(&session)),
error_message: None,
},
Err(message) => PuzzleAgentSessionProcedureResult {
ok: false,
- session: None,
+ session_json: None,
error_message: Some(message),
},
}
@@ -370,12 +370,12 @@ pub fn select_puzzle_cover_image(
match ctx.try_with_tx(|tx| select_puzzle_cover_image_tx(tx, input.clone())) {
Ok(session) => PuzzleAgentSessionProcedureResult {
ok: true,
- session: Some(session),
+ session_json: Some(serialize_json(&session)),
error_message: None,
},
Err(message) => PuzzleAgentSessionProcedureResult {
ok: false,
- session: None,
+ session_json: None,
error_message: Some(message),
},
}
@@ -389,12 +389,12 @@ pub fn publish_puzzle_work(
match ctx.try_with_tx(|tx| publish_puzzle_work_tx(tx, input.clone())) {
Ok(item) => PuzzleWorkProcedureResult {
ok: true,
- item: Some(item),
+ item_json: Some(serialize_json(&item)),
error_message: None,
},
Err(message) => PuzzleWorkProcedureResult {
ok: false,
- item: None,
+ item_json: None,
error_message: Some(message),
},
}
@@ -408,12 +408,12 @@ pub fn list_puzzle_works(
match ctx.try_with_tx(|tx| list_puzzle_works_tx(tx, input.clone())) {
Ok(items) => PuzzleWorksProcedureResult {
ok: true,
- items,
+ items_json: Some(serialize_json(&items)),
error_message: None,
},
Err(message) => PuzzleWorksProcedureResult {
ok: false,
- items: Vec::new(),
+ items_json: None,
error_message: Some(message),
},
}
@@ -427,12 +427,12 @@ pub fn get_puzzle_work_detail(
match ctx.try_with_tx(|tx| get_puzzle_work_detail_tx(tx, input.clone())) {
Ok(item) => PuzzleWorkProcedureResult {
ok: true,
- item: Some(item),
+ item_json: Some(serialize_json(&item)),
error_message: None,
},
Err(message) => PuzzleWorkProcedureResult {
ok: false,
- item: None,
+ item_json: None,
error_message: Some(message),
},
}
@@ -446,12 +446,12 @@ pub fn update_puzzle_work(
match ctx.try_with_tx(|tx| update_puzzle_work_tx(tx, input.clone())) {
Ok(item) => PuzzleWorkProcedureResult {
ok: true,
- item: Some(item),
+ item_json: Some(serialize_json(&item)),
error_message: None,
},
Err(message) => PuzzleWorkProcedureResult {
ok: false,
- item: None,
+ item_json: None,
error_message: Some(message),
},
}
@@ -465,12 +465,12 @@ pub fn delete_puzzle_work(
match ctx.try_with_tx(|tx| delete_puzzle_work_tx(tx, input.clone())) {
Ok(items) => PuzzleWorksProcedureResult {
ok: true,
- items,
+ items_json: Some(serialize_json(&items)),
error_message: None,
},
Err(message) => PuzzleWorksProcedureResult {
ok: false,
- items: Vec::new(),
+ items_json: None,
error_message: Some(message),
},
}
@@ -481,12 +481,12 @@ pub fn list_puzzle_gallery(ctx: &mut ProcedureContext) -> PuzzleWorksProcedureRe
match ctx.try_with_tx(|tx| list_puzzle_gallery_tx(tx)) {
Ok(items) => PuzzleWorksProcedureResult {
ok: true,
- items,
+ items_json: Some(serialize_json(&items)),
error_message: None,
},
Err(message) => PuzzleWorksProcedureResult {
ok: false,
- items: Vec::new(),
+ items_json: None,
error_message: Some(message),
},
}
@@ -500,12 +500,12 @@ pub fn get_puzzle_gallery_detail(
match ctx.try_with_tx(|tx| get_puzzle_gallery_detail_tx(tx, input.clone())) {
Ok(item) => PuzzleWorkProcedureResult {
ok: true,
- item: Some(item),
+ item_json: Some(serialize_json(&item)),
error_message: None,
},
Err(message) => PuzzleWorkProcedureResult {
ok: false,
- item: None,
+ item_json: None,
error_message: Some(message),
},
}
@@ -519,12 +519,12 @@ pub fn record_puzzle_work_like(
match ctx.try_with_tx(|tx| record_puzzle_work_like_tx(tx, input.clone())) {
Ok(item) => PuzzleWorkProcedureResult {
ok: true,
- item: Some(item),
+ item_json: Some(serialize_json(&item)),
error_message: None,
},
Err(message) => PuzzleWorkProcedureResult {
ok: false,
- item: None,
+ item_json: None,
error_message: Some(message),
},
}
@@ -538,12 +538,12 @@ pub fn remix_puzzle_work(
match ctx.try_with_tx(|tx| remix_puzzle_work_tx(tx, input.clone())) {
Ok(session) => PuzzleAgentSessionProcedureResult {
ok: true,
- session: Some(session),
+ session_json: Some(serialize_json(&session)),
error_message: None,
},
Err(message) => PuzzleAgentSessionProcedureResult {
ok: false,
- session: None,
+ session_json: None,
error_message: Some(message),
},
}
@@ -557,12 +557,12 @@ pub fn start_puzzle_run(
match ctx.try_with_tx(|tx| start_puzzle_run_tx(tx, input.clone())) {
Ok(run) => PuzzleRunProcedureResult {
ok: true,
- run: Some(run),
+ run_json: Some(serialize_json(&run)),
error_message: None,
},
Err(message) => PuzzleRunProcedureResult {
ok: false,
- run: None,
+ run_json: None,
error_message: Some(message),
},
}
@@ -576,12 +576,12 @@ pub fn get_puzzle_run(
match ctx.try_with_tx(|tx| get_puzzle_run_tx(tx, input.clone())) {
Ok(run) => PuzzleRunProcedureResult {
ok: true,
- run: Some(run),
+ run_json: Some(serialize_json(&run)),
error_message: None,
},
Err(message) => PuzzleRunProcedureResult {
ok: false,
- run: None,
+ run_json: None,
error_message: Some(message),
},
}
@@ -595,12 +595,12 @@ pub fn swap_puzzle_pieces(
match ctx.try_with_tx(|tx| swap_puzzle_pieces_tx(tx, input.clone())) {
Ok(run) => PuzzleRunProcedureResult {
ok: true,
- run: Some(run),
+ run_json: Some(serialize_json(&run)),
error_message: None,
},
Err(message) => PuzzleRunProcedureResult {
ok: false,
- run: None,
+ run_json: None,
error_message: Some(message),
},
}
@@ -614,12 +614,12 @@ pub fn drag_puzzle_piece_or_group(
match ctx.try_with_tx(|tx| drag_puzzle_piece_or_group_tx(tx, input.clone())) {
Ok(run) => PuzzleRunProcedureResult {
ok: true,
- run: Some(run),
+ run_json: Some(serialize_json(&run)),
error_message: None,
},
Err(message) => PuzzleRunProcedureResult {
ok: false,
- run: None,
+ run_json: None,
error_message: Some(message),
},
}
@@ -633,12 +633,12 @@ pub fn advance_puzzle_next_level(
match ctx.try_with_tx(|tx| advance_puzzle_next_level_tx(tx, input.clone())) {
Ok(run) => PuzzleRunProcedureResult {
ok: true,
- run: Some(run),
+ run_json: Some(serialize_json(&run)),
error_message: None,
},
Err(message) => PuzzleRunProcedureResult {
ok: false,
- run: None,
+ run_json: None,
error_message: Some(message),
},
}
@@ -652,12 +652,12 @@ pub fn update_puzzle_run_pause(
match ctx.try_with_tx(|tx| update_puzzle_run_pause_tx(tx, input.clone())) {
Ok(run) => PuzzleRunProcedureResult {
ok: true,
- run: Some(run),
+ run_json: Some(serialize_json(&run)),
error_message: None,
},
Err(message) => PuzzleRunProcedureResult {
ok: false,
- run: None,
+ run_json: None,
error_message: Some(message),
},
}
@@ -671,12 +671,12 @@ pub fn use_puzzle_runtime_prop(
match ctx.try_with_tx(|tx| use_puzzle_runtime_prop_tx(tx, input.clone())) {
Ok(run) => PuzzleRunProcedureResult {
ok: true,
- run: Some(run),
+ run_json: Some(serialize_json(&run)),
error_message: None,
},
Err(message) => PuzzleRunProcedureResult {
ok: false,
- run: None,
+ run_json: None,
error_message: Some(message),
},
}
@@ -690,12 +690,12 @@ pub fn claim_puzzle_work_point_incentive(
match ctx.try_with_tx(|tx| claim_puzzle_work_point_incentive_tx(tx, input.clone())) {
Ok(item) => PuzzleWorkProcedureResult {
ok: true,
- item: Some(item),
+ item_json: Some(serialize_json(&item)),
error_message: None,
},
Err(message) => PuzzleWorkProcedureResult {
ok: false,
- item: None,
+ item_json: None,
error_message: Some(message),
},
}
@@ -709,12 +709,12 @@ pub fn submit_puzzle_leaderboard_entry(
match ctx.try_with_tx(|tx| submit_puzzle_leaderboard_entry_tx(tx, input.clone())) {
Ok(run) => PuzzleRunProcedureResult {
ok: true,
- run: Some(run),
+ run_json: Some(serialize_json(&run)),
error_message: None,
},
Err(message) => PuzzleRunProcedureResult {
ok: false,
- run: None,
+ run_json: None,
error_message: Some(message),
},
}
diff --git a/vite.config.ts b/vite.config.ts
index c55a9803..ac0b1e69 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -58,7 +58,7 @@ export default defineConfig(({mode}) => {
entries: ['index.html'],
},
build: {
- chunkSizeWarningLimit: 800,
+ chunkSizeWarningLimit: 1000,
},
server: {
// HMR is disabled in AI Studio via DISABLE_HMR env var.