切断认证快照同步路径
删除 auth_store_snapshot 表和旧 JSON 快照 procedure。 改用 AuthStoreProjectionView 同步 user_account、auth_identity 和 refresh_session。 让 auth 服务从 user_account 投影恢复内存工作集。 同步生成绑定、schema guard 和后端架构文档。
This commit is contained in:
@@ -16,6 +16,14 @@
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-01 认证工作集只经 typed projection 同步正式表
|
||||
|
||||
- 背景:同手机号重复账号、兑换码白名单错配和微信资料不回写暴露出 `module-auth` 内存工作集、`auth_store_snapshot` 和正式认证表之间仍有历史互刷路径;旧 JSON 快照会把过期手机号索引或用户资料重新带回运行态。
|
||||
- 决策:删除 `auth_store_snapshot` 表和旧 `import_auth_store_snapshot_json` / `export_auth_store_snapshot_from_tables` procedure;`module-auth` 只保留内存工作集和 typed `AuthStoreProjectionView` 导入 / 导出。运行中认证写操作通过 `sync_auth_store_projection` 同步 `user_account` / `auth_identity` / `refresh_session`,启动恢复通过 `export_auth_store_projection_from_tables` 从正式表恢复内存。账号资料真相只在 `user_account`,`auth_identity` 只保存登录入口身份键。
|
||||
- 影响范围:`module-auth` projection API、`spacetime-module` auth schema/procedure、`spacetime-client` bindings/facade、`api-server` 启动恢复和认证同步、后端架构文档与认证排障记忆。
|
||||
- 验证方式:`npm run spacetime:generate`、`SPACETIME_SCHEMA_GUARD_ALLOW_BREAKING=1 npm run check:spacetime-schema`、`cargo test -p module-auth --manifest-path server-rs/Cargo.toml -- --nocapture`、`cargo check -p spacetime-client --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:encoding`、`git diff --check`。
|
||||
- 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
|
||||
|
||||
## 2026-06-29 图片画布手动抠图走远端 BiRefNet BFF
|
||||
|
||||
- 背景:用户手动“去除背景”面对任意图片,前端 `chromaKey` 和标准绿幕后处理不适合复杂人物、自然背景或非纯色背景;远端 image host 已部署 BiRefNet 服务,需要让手动抠图走高质量模型,同时避免把服务令牌暴露到浏览器。
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
|
||||
- 现象:后台把私有兑换码配给某个陶泥号或手机号后,用户用同一手机号登录兑换仍提示 `该兑换码不适用于当前账号`。
|
||||
- 原因:认证表里可能存在同一手机号的多条 `user_account`。如果认证工作集重建 `phone_to_user_id` 时让 `user_account.phone_number_e164` 后写覆盖前写,当前登录态会漂到没有 `auth_identity` 的重复账号,而兑换码白名单仍指向另一个内部 `user_id`。
|
||||
- 处理:重建认证工作集时以 `auth_identity(provider="phone")` 指向的账号作为手机号索引权威,`user_account.phone_number_e164` 只补没有 identity 的手机号;旧 `auth_store_snapshot` 只允许在正式认证表为空时一次性转移到正式表,随后清空,不再作为运行期回灌来源;Bearer / refresh session 本进程未命中时不要再从 SpacetimeDB 导出整包快照刷新内存。线上止血先核对失败请求附近的 current session `user_id` 与兑换码 `allowed_user_ids`,不要只看手机号展示值。
|
||||
- 处理:重建认证工作集时以 typed `AuthStoreProjectionView` 从 `user_account` / `auth_identity` / `refresh_session` 恢复;手机号索引以 `auth_identity(provider="phone")` 指向的账号为权威,`user_account.phone_number_e164` 只补没有 identity 的手机号;`auth_store_snapshot` 表和旧 JSON procedure 已删除,Bearer / refresh session 本进程未命中时不要再从 SpacetimeDB 导出整包状态刷新内存。线上止血先核对失败请求附近的 current session `user_id` 与兑换码 `allowed_user_ids`,不要只看手机号展示值。
|
||||
- 约束:`auth_identity` 只保存登录入口身份键;手机号、昵称和头像的正式资料真相在 `user_account.phone_number_e164` / `display_name` / `avatar_url`。旧 `auth_identity.phone_e164` / `display_name` / `avatar_url` 只能作为历史回填来源,不能继续让新写入依赖这些列。
|
||||
- 验证:`cargo test -p spacetime-module auth_export -- --nocapture` 应覆盖同手机号重复账号时手机号索引优先指向有 phone identity 的账号;`api-server` 中不应再存在运行期 `refresh_auth_store_from_spacetime` 调用。
|
||||
- 关联:`server-rs/crates/spacetime-module/src/auth/procedures.rs`、`server-rs/crates/spacetime-module/src/auth/tables.rs`、`server-rs/crates/module-auth/src/lib.rs`。
|
||||
@@ -400,12 +400,12 @@
|
||||
- 验证:`systemctl status genarrative-external-generation-controller.service 'genarrative-external-generation-worker@*.service'` 能看到 controller 和 worker 实例;queue 模式下任务被 claim 后 `worker_id` 与 `lease_expires_at` 会更新,完成后 session 进入 ready 或 failed;inline 模式下不应产生新的 `external_generation_job`。
|
||||
- 关联:`deploy/systemd/genarrative-external-generation-worker@.service`、`deploy/systemd/genarrative-external-generation-controller.service`、`deploy/env/external-generation-controller.env.example`、`server-rs/crates/spacetime-module/src/external_generation.rs`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
|
||||
|
||||
## 外部生成 worker 不应等待 HTTP 认证快照恢复
|
||||
## 外部生成 worker 不应等待 HTTP 认证投影恢复
|
||||
|
||||
- 现象:`genarrative-external-generation-worker@1.service` 在 systemd 中显示 active,但 `external_generation_job` 长时间保持 `pending`;worker 日志每 5 秒出现 `export_auth_store_snapshot_from_tables` 订阅失败,例如缺少 `public_work_gallery_entry` 公开 read model 表。
|
||||
- 原因:独立 worker / controller 是非 HTTP 角色,不承接用户登录态恢复;如果启动路径复用 HTTP `api-server` 的认证快照恢复,SpacetimeDB 认证投影或公开 read model 漂移会把 worker claim 循环挡在启动前。
|
||||
- 处理:`GENARRATIVE_PROCESS_ROLE=external-generation-worker` 和 `external-generation-controller` 启动时只构建空 auth store 的 `AppState`,不调用 SpacetimeDB 认证快照导出;只有 `api` / `all` 这类 HTTP 角色需要在启动时恢复认证快照并在依赖不可用时重试或进入 503 降级。
|
||||
- 验证:重启 worker 后日志应先出现“非 HTTP 进程跳过 SpacetimeDB 认证快照恢复”,随后出现 `external generation worker 已启动`;同一时间窗口不应再因为 `export_auth_store_snapshot_from_tables` 缺表而阻止 job claim。HTTP `api-server` 的认证恢复日志和 503 降级语义保持不变。
|
||||
- 现象:`genarrative-external-generation-worker@1.service` 在 systemd 中显示 active,但 `external_generation_job` 长时间保持 `pending`;worker 日志每 5 秒出现认证投影或公开 read model 订阅失败。
|
||||
- 原因:独立 worker / controller 是非 HTTP 角色,不承接用户登录态恢复;如果启动路径复用 HTTP `api-server` 的认证投影恢复,SpacetimeDB 认证投影或公开 read model 漂移会把 worker claim 循环挡在启动前。
|
||||
- 处理:`GENARRATIVE_PROCESS_ROLE=external-generation-worker` 和 `external-generation-controller` 启动时只构建空 auth store 的 `AppState`,不调用 SpacetimeDB 认证投影导出;只有 `api` / `all` 这类 HTTP 角色需要在启动时恢复认证投影并在依赖不可用时重试或进入 503 降级。
|
||||
- 验证:重启 worker 后日志应先出现“非 HTTP 进程跳过 SpacetimeDB 认证投影恢复”,随后出现 `external generation worker 已启动`;同一时间窗口不应再因为认证投影恢复失败而阻止 job claim。HTTP `api-server` 的认证恢复日志和 503 降级语义保持不变。
|
||||
- 关联:`server-rs/crates/api-server/src/main.rs`、`server-rs/crates/api-server/src/external_generation_worker.rs`、`server-rs/crates/api-server/src/external_generation_worker_controller.rs`、`docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`。
|
||||
|
||||
## 本地旧 external-generation-worker 会抢队列并暴露成 procedure 超时
|
||||
@@ -1197,27 +1197,27 @@
|
||||
- 验证:生成文件落在 `public/branding/taonier-logo-*/`,用 Pillow 检查图片尺寸和非空;执行 `node --check scripts/generate-taonier-logo-concepts.mjs`、`npm run check:encoding`、`git diff --check`。
|
||||
- 关联:`scripts/generate-taonier-logo-concepts.mjs`、`docs/design/TAONIER_BRAND_LOGO_CONCEPTS_2026-05-13.md`。
|
||||
|
||||
## 忘记密码后仍提示手机号或密码错误先查认证快照同步
|
||||
## 忘记密码后仍提示手机号或密码错误先查认证投影同步
|
||||
|
||||
- 现象:用户通过“忘记密码”重设密码后,接口返回成功或页面进入登录态,但再次使用新密码登录仍提示“手机号或密码错误”;重启后还可能出现 `Bearer JWT 版本已失效`,日志里的 token version 与本地快照不一致。
|
||||
- 原因:重置/修改密码会更新 `password_hash`、`password_login_enabled` 和 `token_version`,如果 API 层只更新本地 `InMemoryAuthStore`,没有调用 `sync_auth_store_tables_to_spacetime()`,`api-server` 重启时可能从旧的 SpacetimeDB 正式认证表恢复账号状态。
|
||||
- 处理:`POST /api/auth/password/change` 与 `POST /api/auth/password/reset` 成功后必须同步正式认证表。2026-06-30 起,`auth_store_snapshot` 不再保留行级备查,也不作为运行期回灌来源;只在正式认证表为空时把最新旧快照转移一次到 `user_account` / `auth_identity` / `refresh_session` 并立即清空旧表。认证创建、登录会话、刷新、退出、改密、重置密码、绑定和资料变更等写操作必须在返回客户端前成功同步 SpacetimeDB;同步失败时接口返回错误,不允许把只存在于当前进程内存的账号或会话当成成功结果。新用户注册奖励、邀请码绑定和登录埋点必须排在认证同步成功之后,避免认证没落库时先写出钱包或邀请关系。
|
||||
- 处理:`POST /api/auth/password/change` 与 `POST /api/auth/password/reset` 成功后必须同步正式认证表。2026-07-01 起,`auth_store_snapshot` 表和旧 JSON procedure 已删除;认证工作集只通过 typed projection 同步 `user_account` / `auth_identity` / `refresh_session`。认证创建、登录会话、刷新、退出、改密、重置密码、绑定和资料变更等写操作必须在返回客户端前成功同步 SpacetimeDB;同步失败时接口返回错误,不允许把只存在于当前进程内存的账号或会话当成成功结果。新用户注册奖励、邀请码绑定和登录埋点必须排在认证同步成功之后,避免认证没落库时先写出钱包或邀请关系。
|
||||
- 验证:执行 `cargo test -p module-auth password --manifest-path server-rs/Cargo.toml` 与 `cargo test -p api-server password --manifest-path server-rs/Cargo.toml`;手测时重设密码后旧密码应失败,新密码应成功,重启后仍应保持。
|
||||
- 关联:`server-rs/crates/api-server/src/password_management.rs`、`server-rs/crates/api-server/src/state.rs`、`docs/technical/PASSWORD_LOGIN_CHANGE_RESET_DESIGN_2026-04-24.md`。
|
||||
|
||||
## 密码登录失败且短信登录提示手机号已存在先查孤儿手机号索引
|
||||
|
||||
- 现象:老账号用密码登录提示“手机号或密码错误”,改用短信验证码登录又提示“手机号已存在 / 已注册”,用户卡在既不能登录也不能重新创建的状态。
|
||||
- 原因:历史版本或停服务时认证同步不完整,可能在 SpacetimeDB `auth_identity(provider=phone)` 或 `module-auth` 快照里留下 `phone_to_user_id` 映射,但对应 `user_account` / `users_by_username` 用户行已经不存在。密码登录按手机号索引找不到真实用户,短信登录尝试创建新用户时又被孤儿手机号索引挡住。
|
||||
- 处理:`export_auth_store_snapshot_from_tables` 导出时必须过滤没有 `user_account` 的 phone / wechat identity、union 索引和 refresh session;`module-auth` 从 JSON 快照恢复时也必须二次丢弃指向不存在用户的索引。运行时创建手机号用户前若发现手机号映射指向不存在的用户,应删除孤儿映射后继续创建,避免死锁态继续扩散。
|
||||
- 验证:`cargo test -p module-auth snapshot_json_drops_orphan_phone_index_before_phone_login --manifest-path server-rs/Cargo.toml`、`cargo test -p module-auth phone --manifest-path server-rs/Cargo.toml`、`cargo test -p spacetime-module auth --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server phone_login_reuses_existing_user_for_same_phone_number --manifest-path server-rs/Cargo.toml`。
|
||||
- 原因:历史版本或停服务时认证同步不完整,可能在 SpacetimeDB `auth_identity(provider=phone)` 或旧 `module-auth` 快照里留下 `phone_to_user_id` 映射,但对应 `user_account` / `users_by_username` 用户行已经不存在。密码登录按手机号索引找不到真实用户,短信登录尝试创建新用户时又被孤儿手机号索引挡住。
|
||||
- 处理:`export_auth_store_projection_from_tables` 只导出正式认证表 projection;`module-auth` 从 projection 恢复时必须丢弃指向不存在 `user_account` 的 identity、union 索引和 refresh session。运行时创建手机号用户前若发现手机号映射指向不存在的用户,应删除孤儿映射后继续创建,避免死锁态继续扩散。
|
||||
- 验证:`cargo test -p module-auth projection --manifest-path server-rs/Cargo.toml`、`cargo test -p module-auth phone --manifest-path server-rs/Cargo.toml`、`cargo test -p api-server phone_login_reuses_existing_user_for_same_phone_number --manifest-path server-rs/Cargo.toml`。
|
||||
- 关联:`server-rs/crates/module-auth/src/lib.rs`、`server-rs/crates/spacetime-module/src/auth/procedures.rs`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`。
|
||||
|
||||
## 认证本地文件快照已废弃,旧 procedure 也已删
|
||||
## 认证快照表和旧 procedure 已删除
|
||||
|
||||
- 现象:有些旧代码和生成 bindings 里还会残留 `get_auth_store_snapshot`、`upsert_auth_store_snapshot`、`import_auth_store_snapshot`,或者把 `auth-store.json` 误当成认证恢复源。
|
||||
- 原因:认证恢复已经彻底收口到 SpacetimeDB 正式表和 `module-auth` 的 JSON 导入 / 导出路径;本地文件持久化会和正式表投影打架,SpacetimeDB 不可用时还可能把旧快照回灌到用户表。
|
||||
- 处理:先用 `npm run spacetime:generate -- --rust-only` 刷新 bindings,确认 `server-rs/crates/spacetime-client/src/module_bindings.rs` 里已没有旧 procedure 导出;`module-auth` 只保留内存态,不再写本地快照文件。
|
||||
- 现象:有些旧代码和生成 bindings 里还会残留 `get_auth_store_snapshot`、`upsert_auth_store_snapshot`、`import_auth_store_snapshot`、`import_auth_store_snapshot_json`、`export_auth_store_snapshot_from_tables`,或者把 `auth-store.json` 误当成认证恢复源。
|
||||
- 原因:认证恢复已经彻底收口到 SpacetimeDB 正式表和 `module-auth` typed projection;本地文件持久化或 JSON 快照会和正式表投影打架,SpacetimeDB 不可用时还可能把旧快照回灌到用户表。
|
||||
- 处理:先用 `npm run spacetime:generate` 刷新 bindings,确认 `server-rs/crates/spacetime-client/src/module_bindings.rs` 里已没有旧 snapshot table / procedure 导出;`module-auth` 只保留内存态和 projection view,不再写本地快照文件。
|
||||
- 验证:`cargo check -p module-auth --manifest-path server-rs/Cargo.toml`、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run check:spacetime-schema`、`npm run check:encoding`。
|
||||
|
||||
## 抓大鹅生成页只显示服务暂不可用先查 reason 和外部服务配置
|
||||
@@ -2387,7 +2387,7 @@
|
||||
|
||||
## 本地 api-server 启动订阅 401 先查 Web identity token 注入
|
||||
|
||||
- 现象:`npm run dev` 启动到 api-server 恢复认证快照时,日志出现 `Failed to initiate WebSocket connection ... /v1/database/<db>/subscribe?compression=Brotli: HTTP error: 401 Unauthorized`。
|
||||
- 现象:`npm run dev` 启动到 api-server 恢复认证投影时,日志出现 `Failed to initiate WebSocket connection ... /v1/database/<db>/subscribe?compression=Brotli: HTTP error: 401 Unauthorized`。
|
||||
- 原因:SpacetimeDB SDK 订阅需要 Web API identity token;本地 `.env.local` 常把 `GENARRATIVE_SPACETIME_TOKEN` 留空,只靠 CLI 登录态 publish 成功并不能让 api-server 的 WebSocket subscribe 获得权限。
|
||||
- 处理:`scripts/dev.mjs` 在 SpacetimeDB 就绪后调用 `/v1/identity` 创建当前进程专用 Web API identity token,并只注入本次 `api-server` 环境;不要把临时 token 写进 `.env.local` 或日志。若仍报 401,先确认是否使用了项目脚本启动、日志是否出现 `已创建本地 Web identity`,以及 `GENARRATIVE_SPACETIME_SERVER_URL` / 数据库名是否指向本次启动的实例。
|
||||
- 验证:`npm run test -- scripts/dev.test.ts`;重新运行 `npm run dev` 后 api-server 启动日志不再出现上述 subscribe 401,`/healthz` 返回 200。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# server-rs 与 SpacetimeDB 数据契约
|
||||
|
||||
更新时间:`2026-06-24`
|
||||
更新时间:`2026-07-01`
|
||||
|
||||
## 后端主线
|
||||
|
||||
@@ -290,16 +290,9 @@ npm run check:server-rs-ddd
|
||||
- Rust 结构体:`AuthStoreProjectionMeta`
|
||||
- 源码:`server-rs/crates/spacetime-module/src/auth/tables.rs`
|
||||
|
||||
### `auth_store_snapshot`
|
||||
认证恢复策略:`api-server` 启动时只从 SpacetimeDB 正式认证表(`user_account` / `auth_identity` / `refresh_session`)导出 typed `AuthStoreProjectionView`,再恢复 `module-auth` 的进程内认证工作集;运行中 Bearer `sid` 或 refresh cookie 在本进程工作集内未命中时直接按失效处理,不再从 SpacetimeDB 导出整包认证状态刷新内存,避免旧投影把重复手机号或旧会话重新灌回进程。`module-auth` 只保留内存工作集和 projection 导入 / 导出能力,不再保留 JSON 快照导入 / 导出能力,也不写本地持久化文件;`auth-store.json` / `GENARRATIVE_AUTH_STORE_PATH` 不再是兼容恢复源。认证创建、登录会话、刷新、退出、改密、重置密码、绑定和资料变更等写操作必须在返回客户端前通过 `sync_auth_store_projection` 成功同步 SpacetimeDB 正式认证表;同步失败时接口返回错误,不允许把只存在于当前进程内存的账号或会话当成成功结果。新用户注册奖励、邀请码绑定和登录埋点必须排在认证同步成功之后,避免认证没落库时先写出钱包或邀请关系。若启动恢复阶段 SpacetimeDB 不可连接或超时,`api-server` 会按固定间隔持续重试认证工作集恢复,恢复成功后才开始监听 HTTP,避免一次短超时让进程永久停留在依赖不可用状态。
|
||||
|
||||
- Rust 结构体:`AuthStoreSnapshot`
|
||||
- 源码:`server-rs/crates/spacetime-module/src/auth/tables.rs`
|
||||
|
||||
认证恢复策略:`api-server` 启动时只从 SpacetimeDB 正式认证表(`user_account` / `auth_identity` / `refresh_session`)投影恢复进程内认证工作集;运行中 Bearer `sid` 或 refresh cookie 在本进程工作集内未命中时直接按失效处理,不再从 SpacetimeDB 导出整包认证状态刷新内存,避免旧投影把重复手机号或旧会话重新灌回进程。`module-auth` 只保留内存工作集和 JSON 导入 / 导出能力,不再写本地持久化文件;`auth-store.json` / `GENARRATIVE_AUTH_STORE_PATH` 不再是兼容恢复源,也不得在启动时回写覆盖 `auth_identity` / `user_account`。认证创建、登录会话、刷新、退出、改密、重置密码、绑定和资料变更等写操作必须在返回客户端前成功同步 SpacetimeDB 正式认证表;同步失败时接口返回错误,不允许把只存在于当前进程内存的账号或会话当成成功结果。新用户注册奖励、邀请码绑定和登录埋点必须排在认证同步成功之后,避免认证没落库时先写出钱包或邀请关系。若启动恢复阶段 SpacetimeDB 不可连接或超时,`api-server` 会按固定间隔持续重试认证工作集恢复,恢复成功后才开始监听 HTTP,避免一次短超时让进程永久停留在依赖不可用状态。
|
||||
|
||||
`auth_store_snapshot` 禁止再写单行 `snapshot_id = "default"` 聚合 JSON,也不再保留行级备查。SpacetimeDB 模块只保留 `import_auth_store_snapshot_json` 与 `export_auth_store_snapshot_from_tables` 两个兼容过程:前者把当前 `module-auth` 工作集导入正式认证表并清空旧快照表;后者只在正式认证表为空时把最新旧快照转移一次到正式认证表,然后清空 `auth_store_snapshot`。旧 `get_auth_store_snapshot`、`upsert_auth_store_snapshot`、`import_auth_store_snapshot` 兼容入口已删除。导入正式表时只按主键 upsert 本次快照包含的用户、身份和会话,避免过期快照把其他用户整表删除。
|
||||
|
||||
导出认证快照时,`auth_identity` 与 `refresh_session` 只能引用仍存在于 `user_account` 的用户;孤儿手机号 identity、微信 identity、union 索引或 refresh session 必须被过滤,不能恢复成 `module-auth` 内存态里的 `phone_to_user_id` 死索引。`module-auth` 从 JSON 快照恢复时也要二次清理这些孤儿索引,避免历史坏快照导致密码登录提示错误、短信登录又提示手机号已存在。
|
||||
`auth_store_snapshot` 表和旧 `import_auth_store_snapshot_json` / `export_auth_store_snapshot_from_tables` procedure 已删除。认证投影同步只读写 `user_account`、`auth_identity`、`refresh_session` 和 `auth_store_projection_meta`;`auth_identity` 不再写 `phone_e164`、`display_name`、`avatar_url`,这些账号资料只以 `user_account` 为准。
|
||||
|
||||
### `bark_battle_draft_config`
|
||||
|
||||
@@ -930,6 +923,7 @@ RPG 创作入口的配置 ID 是 `rpg`,当前 `visible=true`、`open=true`;
|
||||
|
||||
- Rust 结构体:`UserAccount`
|
||||
- 源码:`server-rs/crates/spacetime-module/src/auth/tables.rs`
|
||||
- 职责:账号资料真相源,保存 `phone_number_e164`、`display_name`、`avatar_url`、`created_at`、登录状态、密码 hash、token version 和账号标签。`module-auth` 进程内工作集必须通过 typed projection 与该表同步,不得再经 auth JSON 快照回灌。
|
||||
|
||||
### `user_browse_history`
|
||||
|
||||
|
||||
@@ -621,10 +621,12 @@ function main() {
|
||||
const compareResult = compareTables(baseResult.tables, currentResult.tables);
|
||||
const changedFiles = getChangedFiles(baseRef);
|
||||
const sidecarFailures = checkSchemaSidecars(changedFiles, compareResult.schemaChanged);
|
||||
const compareFailures =
|
||||
compareResult.breakingChanged && allowBreaking ? [] : compareResult.failures;
|
||||
const failures = [
|
||||
...currentResult.failures,
|
||||
...baseResult.failures,
|
||||
...compareResult.failures,
|
||||
...compareFailures,
|
||||
...sidecarFailures,
|
||||
];
|
||||
|
||||
|
||||
Generated
+1
@@ -5770,6 +5770,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"module-ai",
|
||||
"module-assets",
|
||||
"module-auth",
|
||||
"module-big-fish",
|
||||
"module-combat",
|
||||
"module-custom-world",
|
||||
|
||||
@@ -521,7 +521,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn spacetime_unavailable_router_returns_service_unavailable_for_requests() {
|
||||
let app =
|
||||
build_spacetime_unavailable_router("SpacetimeDB 启动恢复认证快照超时".to_string());
|
||||
build_spacetime_unavailable_router("SpacetimeDB 启动恢复认证投影超时".to_string());
|
||||
|
||||
let response = app
|
||||
.oneshot(
|
||||
@@ -2806,6 +2806,10 @@ mod tests {
|
||||
login_payload["user"]["wechatDisplayName"],
|
||||
Value::String("微信旅人".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
login_payload["user"]["displayName"],
|
||||
Value::String("微信旅人".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
login_payload["user"]["wechatAccount"],
|
||||
Value::String("wx-mini-code-001".to_string())
|
||||
@@ -2969,6 +2973,10 @@ mod tests {
|
||||
bind_payload["user"]["wechatDisplayName"],
|
||||
Value::String("微信旅人".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
bind_payload["user"]["displayName"],
|
||||
Value::String("微信旅人".to_string())
|
||||
);
|
||||
assert!(
|
||||
bind_payload["token"]
|
||||
.as_str()
|
||||
|
||||
@@ -219,7 +219,7 @@ fn build_non_http_app_state_for_startup(
|
||||
debug_assert!(!should_restore_auth_store_for_startup(process_role));
|
||||
info!(
|
||||
process_role = process_role.as_str(),
|
||||
"非 HTTP 进程跳过 SpacetimeDB 认证快照恢复"
|
||||
"非 HTTP 进程跳过 SpacetimeDB 认证投影恢复"
|
||||
);
|
||||
AppState::new_with_empty_auth_store(config)
|
||||
}
|
||||
@@ -436,7 +436,7 @@ async fn restore_app_state_for_startup(
|
||||
warn!(
|
||||
retry_after_seconds = AUTH_STORE_STARTUP_RETRY_INTERVAL.as_secs(),
|
||||
error = %message,
|
||||
"启动恢复 SpacetimeDB 认证快照暂不可用,api-server 将继续重试"
|
||||
"启动恢复 SpacetimeDB 认证投影暂不可用,api-server 将继续重试"
|
||||
);
|
||||
tokio::time::sleep(AUTH_STORE_STARTUP_RETRY_INTERVAL).await;
|
||||
}
|
||||
@@ -458,10 +458,10 @@ async fn try_restore_app_state_for_startup(
|
||||
Err(_) => {
|
||||
error!(
|
||||
timeout_seconds = AUTH_STORE_STARTUP_RESTORE_TIMEOUT.as_secs(),
|
||||
"启动等待 SpacetimeDB 恢复认证快照超时"
|
||||
"启动等待 SpacetimeDB 恢复认证投影超时"
|
||||
);
|
||||
Err(state::AppStateInitError::DependencyUnavailable(
|
||||
"SpacetimeDB 启动恢复认证快照超时".to_string(),
|
||||
"SpacetimeDB 启动恢复认证投影超时".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -782,27 +782,26 @@ impl AppState {
|
||||
#[cfg(test)]
|
||||
return Ok(());
|
||||
|
||||
#[cfg(not(test))]
|
||||
let snapshot_json = self
|
||||
.auth_store
|
||||
.export_snapshot_json()
|
||||
.map_err(SpacetimeClientError::Runtime)?;
|
||||
#[cfg(not(test))]
|
||||
let updated_at_micros = i64::try_from(
|
||||
OffsetDateTime::now_utc().unix_timestamp_nanos() / 1_000,
|
||||
)
|
||||
.map_err(|_| SpacetimeClientError::Runtime("认证状态更新时间超出 i64 范围".to_string()))?;
|
||||
// 当前仍由 module-auth 的进程内工作集执行业务规则;这里只同步到 SpacetimeDB 正式认证表,
|
||||
// 不再读写 auth_store_snapshot 行镜像。
|
||||
#[cfg(not(test))]
|
||||
let projection = self
|
||||
.auth_store
|
||||
.export_projection_view(updated_at_micros)
|
||||
.map_err(SpacetimeClientError::Runtime)?;
|
||||
// 当前仍由 module-auth 的进程内工作集执行业务规则;这里只用 typed projection 同步正式认证表。
|
||||
#[cfg(not(test))]
|
||||
if let Err(error) = self
|
||||
.spacetime_client
|
||||
.import_auth_store_snapshot_json(snapshot_json, updated_at_micros)
|
||||
.sync_auth_store_projection(projection)
|
||||
.await
|
||||
{
|
||||
warn!(
|
||||
error = %error,
|
||||
"认证状态导入 SpacetimeDB 正式表失败,当前认证流程中止"
|
||||
"认证投影同步 SpacetimeDB 正式表失败,当前认证流程中止"
|
||||
);
|
||||
return Err(error);
|
||||
}
|
||||
@@ -824,33 +823,33 @@ impl AppState {
|
||||
let mut restore_errors = Vec::new();
|
||||
|
||||
match spacetime_client
|
||||
.export_auth_store_snapshot_from_tables()
|
||||
.export_auth_store_projection_from_tables()
|
||||
.await
|
||||
{
|
||||
Ok(snapshot) => {
|
||||
Ok(projection) => {
|
||||
spacetime_restore_available = true;
|
||||
if let Some(candidate) = auth_store_candidate_from_snapshot_record(
|
||||
snapshot,
|
||||
if let Some(candidate) = auth_store_candidate_from_projection_view(
|
||||
projection,
|
||||
AuthStoreRestoreSource::SpacetimeTables,
|
||||
)? {
|
||||
let state = Self::new_with_auth_store(config, candidate.auth_store)?;
|
||||
info!(
|
||||
source = candidate.source.as_str(),
|
||||
updated_at_micros = candidate.updated_at_micros,
|
||||
"已恢复认证快照"
|
||||
"已恢复认证投影"
|
||||
);
|
||||
return Ok(state);
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
warn!(error = %error, "从 SpacetimeDB 表恢复认证快照失败");
|
||||
warn!(error = %error, "从 SpacetimeDB 表恢复认证投影失败");
|
||||
restore_errors.push(error.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
if !spacetime_restore_available {
|
||||
return Err(AppStateInitError::DependencyUnavailable(format!(
|
||||
"SpacetimeDB 认证恢复不可用:{}",
|
||||
"SpacetimeDB 认证投影恢复不可用:{}",
|
||||
restore_errors.join("; ")
|
||||
)));
|
||||
}
|
||||
@@ -1244,22 +1243,23 @@ struct AuthStoreRestoreCandidate {
|
||||
auth_store: InMemoryAuthStore,
|
||||
}
|
||||
|
||||
fn auth_store_candidate_from_snapshot_record(
|
||||
snapshot: spacetime_client::AuthStoreSnapshotRecord,
|
||||
fn auth_store_candidate_from_projection_view(
|
||||
projection: module_auth::AuthStoreProjectionView,
|
||||
source: AuthStoreRestoreSource,
|
||||
) -> Result<Option<AuthStoreRestoreCandidate>, AppStateInitError> {
|
||||
let Some(snapshot_json) = snapshot
|
||||
.snapshot_json
|
||||
.filter(|value| !value.trim().is_empty())
|
||||
else {
|
||||
if projection.users.is_empty()
|
||||
&& projection.identities.is_empty()
|
||||
&& projection.refresh_sessions.is_empty()
|
||||
{
|
||||
return Ok(None);
|
||||
};
|
||||
let auth_store = InMemoryAuthStore::from_snapshot_json(&snapshot_json)
|
||||
}
|
||||
let updated_at_micros = Some(projection.updated_at_micros);
|
||||
let auth_store = InMemoryAuthStore::from_projection_view(projection)
|
||||
.map_err(AppStateInitError::AuthStore)?;
|
||||
|
||||
Ok(Some(AuthStoreRestoreCandidate {
|
||||
source,
|
||||
updated_at_micros: snapshot.updated_at_micros,
|
||||
updated_at_micros,
|
||||
auth_store,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -2,11 +2,7 @@
|
||||
//!
|
||||
//! 这里只返回纯应用结果与领域事件;短信 provider、JWT 签发和持久化由外层 adapter 完成。
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::domain::{
|
||||
AuthStoreSnapshotRecord, AuthUser, RefreshSessionRecord, WechatAuthStateRecord,
|
||||
};
|
||||
use crate::domain::{AuthUser, RefreshSessionRecord, WechatAuthStateRecord};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct AuthMeResult {
|
||||
@@ -111,20 +107,7 @@ pub struct LogoutCurrentSessionResult {
|
||||
pub user: AuthUser,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct RefreshAuthStoreSnapshotResult {
|
||||
pub user_count: usize,
|
||||
pub session_count: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct LogoutAllSessionsResult {
|
||||
pub user: AuthUser,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct AuthStoreSnapshotProcedureResult {
|
||||
pub ok: bool,
|
||||
pub record: Option<AuthStoreSnapshotRecord>,
|
||||
pub error_message: Option<String>,
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
//!
|
||||
//! 用于表达密码入口、手机号验证码、微信登录、刷新会话签发和吊销等用例输入。
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::domain::{
|
||||
AuthLoginMethod, PhoneAuthScene, RefreshSessionClientInfo, WechatAuthScene,
|
||||
WechatIdentityProfile,
|
||||
@@ -106,9 +104,3 @@ pub struct LogoutCurrentSessionInput {
|
||||
pub struct LogoutAllSessionsInput {
|
||||
pub user_id: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct AuthStoreSnapshotUpsertInput {
|
||||
pub snapshot_json: String,
|
||||
pub updated_at_micros: i64,
|
||||
}
|
||||
|
||||
@@ -178,11 +178,54 @@ pub struct RefreshSessionRecord {
|
||||
pub last_seen_at: String,
|
||||
}
|
||||
|
||||
/// Auth store 持久化快照记录。
|
||||
/// module-auth 进程内工作集同步到数据库的 typed view。
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct AuthStoreSnapshotRecord {
|
||||
pub snapshot_json: Option<String>,
|
||||
pub updated_at_micros: Option<i64>,
|
||||
pub struct AuthStoreProjectionView {
|
||||
pub updated_at_micros: i64,
|
||||
pub users: Vec<AuthStoreProjectionUser>,
|
||||
pub identities: Vec<AuthStoreProjectionIdentity>,
|
||||
pub refresh_sessions: Vec<AuthStoreProjectionRefreshSession>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct AuthStoreProjectionUser {
|
||||
pub user_id: String,
|
||||
pub public_user_code: String,
|
||||
pub username: String,
|
||||
pub display_name: String,
|
||||
pub avatar_url: Option<String>,
|
||||
pub phone_number_masked: Option<String>,
|
||||
pub phone_number_e164: Option<String>,
|
||||
pub login_method: String,
|
||||
pub binding_status: String,
|
||||
pub wechat_bound: bool,
|
||||
pub password_hash: String,
|
||||
pub password_login_enabled: bool,
|
||||
pub token_version: u64,
|
||||
pub created_at: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct AuthStoreProjectionIdentity {
|
||||
pub identity_id: String,
|
||||
pub user_id: String,
|
||||
pub provider: String,
|
||||
pub provider_uid: String,
|
||||
pub provider_union_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct AuthStoreProjectionRefreshSession {
|
||||
pub session_id: String,
|
||||
pub user_id: String,
|
||||
pub refresh_token_hash: String,
|
||||
pub issued_by_provider: String,
|
||||
pub client_info_json: String,
|
||||
pub expires_at: String,
|
||||
pub revoked_at: Option<String>,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
pub last_seen_at: String,
|
||||
}
|
||||
|
||||
pub fn validate_password(password: &str) -> Result<(), PasswordEntryError> {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@ license.workspace = true
|
||||
[dependencies]
|
||||
module-ai = { workspace = true }
|
||||
module-assets = { workspace = true }
|
||||
module-auth = { workspace = true }
|
||||
module-big-fish = { workspace = true }
|
||||
module-combat = { workspace = true }
|
||||
module-custom-world = { workspace = true }
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
use super::*;
|
||||
|
||||
impl SpacetimeClient {
|
||||
pub async fn export_auth_store_snapshot_from_tables(
|
||||
pub async fn export_auth_store_projection_from_tables(
|
||||
&self,
|
||||
) -> Result<AuthStoreSnapshotRecord, SpacetimeClientError> {
|
||||
) -> Result<module_auth::AuthStoreProjectionView, SpacetimeClientError> {
|
||||
self.call_after_connect(
|
||||
"export_auth_store_snapshot_from_tables",
|
||||
"export_auth_store_projection_from_tables",
|
||||
move |connection, sender| {
|
||||
connection
|
||||
.procedures()
|
||||
.export_auth_store_snapshot_from_tables_then(move |_, result| {
|
||||
.export_auth_store_projection_from_tables_then(move |_, result| {
|
||||
let mapped = result
|
||||
.map_err(SpacetimeClientError::from_sdk_error)
|
||||
.and_then(map_auth_store_snapshot_procedure_result);
|
||||
.and_then(map_auth_store_projection_procedure_result);
|
||||
send_once(&sender, mapped);
|
||||
});
|
||||
},
|
||||
@@ -20,29 +20,23 @@ impl SpacetimeClient {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn import_auth_store_snapshot_json(
|
||||
pub async fn sync_auth_store_projection(
|
||||
&self,
|
||||
snapshot_json: String,
|
||||
updated_at_micros: i64,
|
||||
) -> Result<AuthStoreSnapshotImportRecord, SpacetimeClientError> {
|
||||
let procedure_input = AuthStoreSnapshotUpsertInput {
|
||||
snapshot_json,
|
||||
updated_at_micros,
|
||||
};
|
||||
view: module_auth::AuthStoreProjectionView,
|
||||
) -> Result<AuthStoreProjectionSyncRecord, SpacetimeClientError> {
|
||||
let procedure_input = map_auth_store_projection_view_input(view);
|
||||
|
||||
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);
|
||||
});
|
||||
},
|
||||
)
|
||||
self.call_after_connect("sync_auth_store_projection", move |connection, sender| {
|
||||
connection.procedures().sync_auth_store_projection_then(
|
||||
procedure_input,
|
||||
move |_, result| {
|
||||
let mapped = result
|
||||
.map_err(SpacetimeClientError::from_sdk_error)
|
||||
.and_then(map_auth_store_projection_sync_procedure_result);
|
||||
send_once(&sender, mapped);
|
||||
},
|
||||
);
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,13 +333,7 @@ impl SpacetimeClientHealthSnapshot {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct AuthStoreSnapshotRecord {
|
||||
pub snapshot_json: Option<String>,
|
||||
pub updated_at_micros: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct AuthStoreSnapshotImportRecord {
|
||||
pub struct AuthStoreProjectionSyncRecord {
|
||||
pub imported_user_count: u32,
|
||||
pub imported_identity_count: u32,
|
||||
pub imported_refresh_session_count: u32,
|
||||
|
||||
@@ -179,7 +179,8 @@ pub(crate) use self::assets::{
|
||||
map_asset_object_row, map_entity_binding_procedure_result, map_procedure_result,
|
||||
};
|
||||
pub(crate) use self::auth::{
|
||||
map_auth_store_snapshot_import_procedure_result, map_auth_store_snapshot_procedure_result,
|
||||
map_auth_store_projection_procedure_result, map_auth_store_projection_sync_procedure_result,
|
||||
map_auth_store_projection_view_input,
|
||||
};
|
||||
pub(crate) use self::bark_battle::{
|
||||
map_bark_battle_draft_config_procedure_result, map_bark_battle_draft_config_row,
|
||||
|
||||
@@ -1,42 +1,146 @@
|
||||
use super::*;
|
||||
|
||||
pub(crate) fn map_auth_store_snapshot_procedure_result(
|
||||
result: AuthStoreSnapshotProcedureResult,
|
||||
) -> Result<AuthStoreSnapshotRecord, SpacetimeClientError> {
|
||||
pub(crate) fn map_auth_store_projection_procedure_result(
|
||||
result: crate::module_bindings::AuthStoreProjectionProcedureResult,
|
||||
) -> Result<module_auth::AuthStoreProjectionView, SpacetimeClientError> {
|
||||
if !result.ok {
|
||||
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
||||
}
|
||||
|
||||
result
|
||||
.record
|
||||
.map(map_auth_store_projection_view)
|
||||
.ok_or_else(|| SpacetimeClientError::missing_snapshot("认证投影"))
|
||||
}
|
||||
|
||||
pub(crate) fn map_auth_store_projection_sync_procedure_result(
|
||||
result: crate::module_bindings::AuthStoreProjectionSyncProcedureResult,
|
||||
) -> Result<AuthStoreProjectionSyncRecord, SpacetimeClientError> {
|
||||
if !result.ok {
|
||||
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
||||
}
|
||||
|
||||
let record = result
|
||||
.record
|
||||
.ok_or_else(|| SpacetimeClientError::missing_snapshot("认证快照"))?;
|
||||
.ok_or_else(|| SpacetimeClientError::missing_snapshot("认证投影同步结果"))?;
|
||||
|
||||
Ok(map_auth_store_snapshot_record(record))
|
||||
}
|
||||
|
||||
pub(crate) fn map_auth_store_snapshot_record(
|
||||
record: crate::module_bindings::AuthStoreSnapshotRecord,
|
||||
) -> crate::AuthStoreSnapshotRecord {
|
||||
crate::AuthStoreSnapshotRecord {
|
||||
snapshot_json: record.snapshot_json,
|
||||
updated_at_micros: record.updated_at_micros,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn map_auth_store_snapshot_import_procedure_result(
|
||||
result: AuthStoreSnapshotImportProcedureResult,
|
||||
) -> Result<AuthStoreSnapshotImportRecord, SpacetimeClientError> {
|
||||
if !result.ok {
|
||||
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
||||
}
|
||||
|
||||
let record = result
|
||||
.record
|
||||
.ok_or_else(|| SpacetimeClientError::missing_snapshot("认证快照导入结果"))?;
|
||||
|
||||
Ok(AuthStoreSnapshotImportRecord {
|
||||
Ok(AuthStoreProjectionSyncRecord {
|
||||
imported_user_count: record.imported_user_count,
|
||||
imported_identity_count: record.imported_identity_count,
|
||||
imported_refresh_session_count: record.imported_refresh_session_count,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn map_auth_store_projection_view_input(
|
||||
view: module_auth::AuthStoreProjectionView,
|
||||
) -> crate::module_bindings::AuthStoreProjectionView {
|
||||
crate::module_bindings::AuthStoreProjectionView {
|
||||
updated_at_micros: view.updated_at_micros,
|
||||
users: view
|
||||
.users
|
||||
.into_iter()
|
||||
.map(|user| crate::module_bindings::AuthStoreProjectionUser {
|
||||
user_id: user.user_id,
|
||||
public_user_code: user.public_user_code,
|
||||
username: user.username,
|
||||
display_name: user.display_name,
|
||||
avatar_url: user.avatar_url,
|
||||
phone_number_masked: user.phone_number_masked,
|
||||
phone_number_e_164: user.phone_number_e164,
|
||||
login_method: user.login_method,
|
||||
binding_status: user.binding_status,
|
||||
wechat_bound: user.wechat_bound,
|
||||
password_hash: user.password_hash,
|
||||
password_login_enabled: user.password_login_enabled,
|
||||
token_version: user.token_version,
|
||||
created_at: user.created_at,
|
||||
})
|
||||
.collect(),
|
||||
identities: view
|
||||
.identities
|
||||
.into_iter()
|
||||
.map(
|
||||
|identity| crate::module_bindings::AuthStoreProjectionIdentity {
|
||||
identity_id: identity.identity_id,
|
||||
user_id: identity.user_id,
|
||||
provider: identity.provider,
|
||||
provider_uid: identity.provider_uid,
|
||||
provider_union_id: identity.provider_union_id,
|
||||
},
|
||||
)
|
||||
.collect(),
|
||||
refresh_sessions: view
|
||||
.refresh_sessions
|
||||
.into_iter()
|
||||
.map(
|
||||
|session| crate::module_bindings::AuthStoreProjectionRefreshSession {
|
||||
session_id: session.session_id,
|
||||
user_id: session.user_id,
|
||||
refresh_token_hash: session.refresh_token_hash,
|
||||
issued_by_provider: session.issued_by_provider,
|
||||
client_info_json: session.client_info_json,
|
||||
expires_at: session.expires_at,
|
||||
revoked_at: session.revoked_at,
|
||||
created_at: session.created_at,
|
||||
updated_at: session.updated_at,
|
||||
last_seen_at: session.last_seen_at,
|
||||
},
|
||||
)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn map_auth_store_projection_view(
|
||||
view: crate::module_bindings::AuthStoreProjectionView,
|
||||
) -> module_auth::AuthStoreProjectionView {
|
||||
module_auth::AuthStoreProjectionView {
|
||||
updated_at_micros: view.updated_at_micros,
|
||||
users: view
|
||||
.users
|
||||
.into_iter()
|
||||
.map(|user| module_auth::AuthStoreProjectionUser {
|
||||
user_id: user.user_id,
|
||||
public_user_code: user.public_user_code,
|
||||
username: user.username,
|
||||
display_name: user.display_name,
|
||||
avatar_url: user.avatar_url,
|
||||
phone_number_masked: user.phone_number_masked,
|
||||
phone_number_e164: user.phone_number_e_164,
|
||||
login_method: user.login_method,
|
||||
binding_status: user.binding_status,
|
||||
wechat_bound: user.wechat_bound,
|
||||
password_hash: user.password_hash,
|
||||
password_login_enabled: user.password_login_enabled,
|
||||
token_version: user.token_version,
|
||||
created_at: user.created_at,
|
||||
})
|
||||
.collect(),
|
||||
identities: view
|
||||
.identities
|
||||
.into_iter()
|
||||
.map(|identity| module_auth::AuthStoreProjectionIdentity {
|
||||
identity_id: identity.identity_id,
|
||||
user_id: identity.user_id,
|
||||
provider: identity.provider,
|
||||
provider_uid: identity.provider_uid,
|
||||
provider_union_id: identity.provider_union_id,
|
||||
})
|
||||
.collect(),
|
||||
refresh_sessions: view
|
||||
.refresh_sessions
|
||||
.into_iter()
|
||||
.map(|session| module_auth::AuthStoreProjectionRefreshSession {
|
||||
session_id: session.session_id,
|
||||
user_id: session.user_id,
|
||||
refresh_token_hash: session.refresh_token_hash,
|
||||
issued_by_provider: session.issued_by_provider,
|
||||
client_info_json: session.client_info_json,
|
||||
expires_at: session.expires_at,
|
||||
revoked_at: session.revoked_at,
|
||||
created_at: session.created_at,
|
||||
updated_at: session.updated_at,
|
||||
last_seen_at: session.last_seen_at,
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,15 +95,15 @@ pub mod asset_object_upsert_snapshot_type;
|
||||
pub mod attach_ai_result_reference_and_return_procedure;
|
||||
pub mod auth_identity_table;
|
||||
pub mod auth_identity_type;
|
||||
pub mod auth_store_projection_identity_type;
|
||||
pub mod auth_store_projection_meta_table;
|
||||
pub mod auth_store_projection_meta_type;
|
||||
pub mod auth_store_snapshot_import_procedure_result_type;
|
||||
pub mod auth_store_snapshot_import_record_type;
|
||||
pub mod auth_store_snapshot_procedure_result_type;
|
||||
pub mod auth_store_snapshot_record_type;
|
||||
pub mod auth_store_snapshot_table;
|
||||
pub mod auth_store_snapshot_type;
|
||||
pub mod auth_store_snapshot_upsert_input_type;
|
||||
pub mod auth_store_projection_procedure_result_type;
|
||||
pub mod auth_store_projection_refresh_session_type;
|
||||
pub mod auth_store_projection_sync_procedure_result_type;
|
||||
pub mod auth_store_projection_sync_record_type;
|
||||
pub mod auth_store_projection_user_type;
|
||||
pub mod auth_store_projection_view_type;
|
||||
pub mod authenticate_external_api_key_and_return_procedure;
|
||||
pub mod authorize_database_migration_operator_procedure;
|
||||
pub mod bark_battle_draft_config_row_type;
|
||||
@@ -407,7 +407,7 @@ pub mod enqueue_external_generation_job_and_return_procedure;
|
||||
pub mod ensure_analytics_date_dimension_for_date_reducer;
|
||||
pub mod equip_inventory_item_input_type;
|
||||
pub mod execute_custom_world_agent_action_procedure;
|
||||
pub mod export_auth_store_snapshot_from_tables_procedure;
|
||||
pub mod export_auth_store_projection_from_tables_procedure;
|
||||
pub mod export_database_migration_to_file_procedure;
|
||||
pub mod external_api_key_authenticate_input_type;
|
||||
pub mod external_api_key_create_input_type;
|
||||
@@ -502,7 +502,6 @@ pub mod grant_inventory_item_input_type;
|
||||
pub mod grant_new_user_registration_wallet_reward_procedure;
|
||||
pub mod grant_player_progression_experience_and_return_procedure;
|
||||
pub mod grant_player_progression_experience_reducer;
|
||||
pub mod import_auth_store_snapshot_json_procedure;
|
||||
pub mod import_database_migration_from_chunks_procedure;
|
||||
pub mod import_database_migration_from_file_procedure;
|
||||
pub mod import_database_migration_incremental_from_chunks_procedure;
|
||||
@@ -1107,6 +1106,7 @@ pub mod submit_square_hole_agent_message_procedure;
|
||||
pub mod submit_visual_novel_agent_message_procedure;
|
||||
pub mod swap_puzzle_clear_cards_procedure;
|
||||
pub mod swap_puzzle_pieces_procedure;
|
||||
pub mod sync_auth_store_projection_procedure;
|
||||
pub mod tracking_daily_stat_table;
|
||||
pub mod tracking_daily_stat_type;
|
||||
pub mod tracking_event_table;
|
||||
@@ -1323,15 +1323,15 @@ pub use asset_object_upsert_snapshot_type::AssetObjectUpsertSnapshot;
|
||||
pub use attach_ai_result_reference_and_return_procedure::attach_ai_result_reference_and_return;
|
||||
pub use auth_identity_table::*;
|
||||
pub use auth_identity_type::AuthIdentity;
|
||||
pub use auth_store_projection_identity_type::AuthStoreProjectionIdentity;
|
||||
pub use auth_store_projection_meta_table::*;
|
||||
pub use auth_store_projection_meta_type::AuthStoreProjectionMeta;
|
||||
pub use auth_store_snapshot_import_procedure_result_type::AuthStoreSnapshotImportProcedureResult;
|
||||
pub use auth_store_snapshot_import_record_type::AuthStoreSnapshotImportRecord;
|
||||
pub use auth_store_snapshot_procedure_result_type::AuthStoreSnapshotProcedureResult;
|
||||
pub use auth_store_snapshot_record_type::AuthStoreSnapshotRecord;
|
||||
pub use auth_store_snapshot_table::*;
|
||||
pub use auth_store_snapshot_type::AuthStoreSnapshot;
|
||||
pub use auth_store_snapshot_upsert_input_type::AuthStoreSnapshotUpsertInput;
|
||||
pub use auth_store_projection_procedure_result_type::AuthStoreProjectionProcedureResult;
|
||||
pub use auth_store_projection_refresh_session_type::AuthStoreProjectionRefreshSession;
|
||||
pub use auth_store_projection_sync_procedure_result_type::AuthStoreProjectionSyncProcedureResult;
|
||||
pub use auth_store_projection_sync_record_type::AuthStoreProjectionSyncRecord;
|
||||
pub use auth_store_projection_user_type::AuthStoreProjectionUser;
|
||||
pub use auth_store_projection_view_type::AuthStoreProjectionView;
|
||||
pub use authenticate_external_api_key_and_return_procedure::authenticate_external_api_key_and_return;
|
||||
pub use authorize_database_migration_operator_procedure::authorize_database_migration_operator;
|
||||
pub use bark_battle_draft_config_row_type::BarkBattleDraftConfigRow;
|
||||
@@ -1635,7 +1635,7 @@ pub use enqueue_external_generation_job_and_return_procedure::enqueue_external_g
|
||||
pub use ensure_analytics_date_dimension_for_date_reducer::ensure_analytics_date_dimension_for_date;
|
||||
pub use equip_inventory_item_input_type::EquipInventoryItemInput;
|
||||
pub use execute_custom_world_agent_action_procedure::execute_custom_world_agent_action;
|
||||
pub use export_auth_store_snapshot_from_tables_procedure::export_auth_store_snapshot_from_tables;
|
||||
pub use export_auth_store_projection_from_tables_procedure::export_auth_store_projection_from_tables;
|
||||
pub use export_database_migration_to_file_procedure::export_database_migration_to_file;
|
||||
pub use external_api_key_authenticate_input_type::ExternalApiKeyAuthenticateInput;
|
||||
pub use external_api_key_create_input_type::ExternalApiKeyCreateInput;
|
||||
@@ -1730,7 +1730,6 @@ pub use grant_inventory_item_input_type::GrantInventoryItemInput;
|
||||
pub use grant_new_user_registration_wallet_reward_procedure::grant_new_user_registration_wallet_reward;
|
||||
pub use grant_player_progression_experience_and_return_procedure::grant_player_progression_experience_and_return;
|
||||
pub use grant_player_progression_experience_reducer::grant_player_progression_experience;
|
||||
pub use import_auth_store_snapshot_json_procedure::import_auth_store_snapshot_json;
|
||||
pub use import_database_migration_from_chunks_procedure::import_database_migration_from_chunks;
|
||||
pub use import_database_migration_from_file_procedure::import_database_migration_from_file;
|
||||
pub use import_database_migration_incremental_from_chunks_procedure::import_database_migration_incremental_from_chunks;
|
||||
@@ -2335,6 +2334,7 @@ pub use submit_square_hole_agent_message_procedure::submit_square_hole_agent_mes
|
||||
pub use submit_visual_novel_agent_message_procedure::submit_visual_novel_agent_message;
|
||||
pub use swap_puzzle_clear_cards_procedure::swap_puzzle_clear_cards;
|
||||
pub use swap_puzzle_pieces_procedure::swap_puzzle_pieces;
|
||||
pub use sync_auth_store_projection_procedure::sync_auth_store_projection;
|
||||
pub use tracking_daily_stat_table::*;
|
||||
pub use tracking_daily_stat_type::TrackingDailyStat;
|
||||
pub use tracking_event_table::*;
|
||||
@@ -2745,7 +2745,6 @@ pub struct DbUpdate {
|
||||
asset_object: __sdk::TableUpdate<AssetObject>,
|
||||
auth_identity: __sdk::TableUpdate<AuthIdentity>,
|
||||
auth_store_projection_meta: __sdk::TableUpdate<AuthStoreProjectionMeta>,
|
||||
auth_store_snapshot: __sdk::TableUpdate<AuthStoreSnapshot>,
|
||||
bark_battle_draft_config: __sdk::TableUpdate<BarkBattleDraftConfigRow>,
|
||||
bark_battle_gallery_view: __sdk::TableUpdate<BarkBattleGalleryViewRow>,
|
||||
bark_battle_leaderboard_entry: __sdk::TableUpdate<BarkBattleLeaderboardEntryRow>,
|
||||
@@ -2902,9 +2901,6 @@ impl TryFrom<__ws::v2::TransactionUpdate> for DbUpdate {
|
||||
"auth_store_projection_meta" => db_update.auth_store_projection_meta.append(
|
||||
auth_store_projection_meta_table::parse_table_update(table_update)?,
|
||||
),
|
||||
"auth_store_snapshot" => db_update
|
||||
.auth_store_snapshot
|
||||
.append(auth_store_snapshot_table::parse_table_update(table_update)?),
|
||||
"bark_battle_draft_config" => db_update.bark_battle_draft_config.append(
|
||||
bark_battle_draft_config_table::parse_table_update(table_update)?,
|
||||
),
|
||||
@@ -3333,12 +3329,6 @@ impl __sdk::DbUpdate for DbUpdate {
|
||||
&self.auth_store_projection_meta,
|
||||
)
|
||||
.with_updates_by_pk(|row| &row.meta_id);
|
||||
diff.auth_store_snapshot = cache
|
||||
.apply_diff_to_table::<AuthStoreSnapshot>(
|
||||
"auth_store_snapshot",
|
||||
&self.auth_store_snapshot,
|
||||
)
|
||||
.with_updates_by_pk(|row| &row.snapshot_id);
|
||||
diff.bark_battle_draft_config = cache
|
||||
.apply_diff_to_table::<BarkBattleDraftConfigRow>(
|
||||
"bark_battle_draft_config",
|
||||
@@ -3966,9 +3956,6 @@ impl __sdk::DbUpdate for DbUpdate {
|
||||
"auth_store_projection_meta" => db_update
|
||||
.auth_store_projection_meta
|
||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||
"auth_store_snapshot" => db_update
|
||||
.auth_store_snapshot
|
||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||
"bark_battle_draft_config" => db_update
|
||||
.bark_battle_draft_config
|
||||
.append(__sdk::parse_row_list_as_inserts(table_rows.rows)?),
|
||||
@@ -4360,9 +4347,6 @@ impl __sdk::DbUpdate for DbUpdate {
|
||||
"auth_store_projection_meta" => db_update
|
||||
.auth_store_projection_meta
|
||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||
"auth_store_snapshot" => db_update
|
||||
.auth_store_snapshot
|
||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||
"bark_battle_draft_config" => db_update
|
||||
.bark_battle_draft_config
|
||||
.append(__sdk::parse_row_list_as_deletes(table_rows.rows)?),
|
||||
@@ -4734,7 +4718,6 @@ pub struct AppliedDiff<'r> {
|
||||
asset_object: __sdk::TableAppliedDiff<'r, AssetObject>,
|
||||
auth_identity: __sdk::TableAppliedDiff<'r, AuthIdentity>,
|
||||
auth_store_projection_meta: __sdk::TableAppliedDiff<'r, AuthStoreProjectionMeta>,
|
||||
auth_store_snapshot: __sdk::TableAppliedDiff<'r, AuthStoreSnapshot>,
|
||||
bark_battle_draft_config: __sdk::TableAppliedDiff<'r, BarkBattleDraftConfigRow>,
|
||||
bark_battle_gallery_view: __sdk::TableAppliedDiff<'r, BarkBattleGalleryViewRow>,
|
||||
bark_battle_leaderboard_entry: __sdk::TableAppliedDiff<'r, BarkBattleLeaderboardEntryRow>,
|
||||
@@ -4913,11 +4896,6 @@ impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> {
|
||||
&self.auth_store_projection_meta,
|
||||
event,
|
||||
);
|
||||
callbacks.invoke_table_row_callbacks::<AuthStoreSnapshot>(
|
||||
"auth_store_snapshot",
|
||||
&self.auth_store_snapshot,
|
||||
event,
|
||||
);
|
||||
callbacks.invoke_table_row_callbacks::<BarkBattleDraftConfigRow>(
|
||||
"bark_battle_draft_config",
|
||||
&self.bark_battle_draft_config,
|
||||
@@ -6152,7 +6130,6 @@ impl __sdk::SpacetimeModule for RemoteModule {
|
||||
asset_object_table::register_table(client_cache);
|
||||
auth_identity_table::register_table(client_cache);
|
||||
auth_store_projection_meta_table::register_table(client_cache);
|
||||
auth_store_snapshot_table::register_table(client_cache);
|
||||
bark_battle_draft_config_table::register_table(client_cache);
|
||||
bark_battle_gallery_view_table::register_table(client_cache);
|
||||
bark_battle_leaderboard_entry_table::register_table(client_cache);
|
||||
@@ -6281,7 +6258,6 @@ impl __sdk::SpacetimeModule for RemoteModule {
|
||||
"asset_object",
|
||||
"auth_identity",
|
||||
"auth_store_projection_meta",
|
||||
"auth_store_snapshot",
|
||||
"bark_battle_draft_config",
|
||||
"bark_battle_gallery_view",
|
||||
"bark_battle_leaderboard_entry",
|
||||
|
||||
+7
-4
@@ -6,11 +6,14 @@ use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct AuthStoreSnapshotUpsertInput {
|
||||
pub snapshot_json: String,
|
||||
pub updated_at_micros: i64,
|
||||
pub struct AuthStoreProjectionIdentity {
|
||||
pub identity_id: String,
|
||||
pub user_id: String,
|
||||
pub provider: String,
|
||||
pub provider_uid: String,
|
||||
pub provider_union_id: Option<String>,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for AuthStoreSnapshotUpsertInput {
|
||||
impl __sdk::InModule for AuthStoreProjectionIdentity {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
+4
-4
@@ -4,16 +4,16 @@
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::auth_store_snapshot_record_type::AuthStoreSnapshotRecord;
|
||||
use super::auth_store_projection_view_type::AuthStoreProjectionView;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct AuthStoreSnapshotProcedureResult {
|
||||
pub struct AuthStoreProjectionProcedureResult {
|
||||
pub ok: bool,
|
||||
pub record: Option<AuthStoreSnapshotRecord>,
|
||||
pub record: Option<AuthStoreProjectionView>,
|
||||
pub error_message: Option<String>,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for AuthStoreSnapshotProcedureResult {
|
||||
impl __sdk::InModule for AuthStoreProjectionProcedureResult {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct AuthStoreProjectionRefreshSession {
|
||||
pub session_id: String,
|
||||
pub user_id: String,
|
||||
pub refresh_token_hash: String,
|
||||
pub issued_by_provider: String,
|
||||
pub client_info_json: String,
|
||||
pub expires_at: String,
|
||||
pub revoked_at: Option<String>,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
pub last_seen_at: String,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for AuthStoreProjectionRefreshSession {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
+4
-4
@@ -4,16 +4,16 @@
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::auth_store_snapshot_import_record_type::AuthStoreSnapshotImportRecord;
|
||||
use super::auth_store_projection_sync_record_type::AuthStoreProjectionSyncRecord;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct AuthStoreSnapshotImportProcedureResult {
|
||||
pub struct AuthStoreProjectionSyncProcedureResult {
|
||||
pub ok: bool,
|
||||
pub record: Option<AuthStoreSnapshotImportRecord>,
|
||||
pub record: Option<AuthStoreProjectionSyncRecord>,
|
||||
pub error_message: Option<String>,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for AuthStoreSnapshotImportProcedureResult {
|
||||
impl __sdk::InModule for AuthStoreProjectionSyncProcedureResult {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
+2
-2
@@ -6,12 +6,12 @@ use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct AuthStoreSnapshotImportRecord {
|
||||
pub struct AuthStoreProjectionSyncRecord {
|
||||
pub imported_user_count: u32,
|
||||
pub imported_identity_count: u32,
|
||||
pub imported_refresh_session_count: u32,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for AuthStoreSnapshotImportRecord {
|
||||
impl __sdk::InModule for AuthStoreProjectionSyncRecord {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct AuthStoreProjectionUser {
|
||||
pub user_id: String,
|
||||
pub public_user_code: String,
|
||||
pub username: String,
|
||||
pub display_name: String,
|
||||
pub avatar_url: Option<String>,
|
||||
pub phone_number_masked: Option<String>,
|
||||
pub phone_number_e_164: Option<String>,
|
||||
pub login_method: String,
|
||||
pub binding_status: String,
|
||||
pub wechat_bound: bool,
|
||||
pub password_hash: String,
|
||||
pub password_login_enabled: bool,
|
||||
pub token_version: u64,
|
||||
pub created_at: String,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for AuthStoreProjectionUser {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::auth_store_projection_identity_type::AuthStoreProjectionIdentity;
|
||||
use super::auth_store_projection_refresh_session_type::AuthStoreProjectionRefreshSession;
|
||||
use super::auth_store_projection_user_type::AuthStoreProjectionUser;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
pub struct AuthStoreProjectionView {
|
||||
pub updated_at_micros: i64,
|
||||
pub users: Vec<AuthStoreProjectionUser>,
|
||||
pub identities: Vec<AuthStoreProjectionIdentity>,
|
||||
pub refresh_sessions: Vec<AuthStoreProjectionRefreshSession>,
|
||||
}
|
||||
|
||||
impl __sdk::InModule for AuthStoreProjectionView {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user