1
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
# Auth SpacetimeDB 正式表恢复 Stage 3
|
||||
|
||||
## 1. 阶段目标
|
||||
|
||||
本阶段把认证持久化从“只依赖整包快照恢复”推进到“正式认证表优先恢复”。
|
||||
|
||||
落地口径:
|
||||
- `user_account`、`auth_identity`、`refresh_session` 作为 SpacetimeDB 中的正式认证持久化表。
|
||||
- API 启动时优先从正式表导出兼容 `module-auth` 的认证快照,再恢复到内存认证服务。
|
||||
- 运行期认证变更仍先复用现有 `module-auth` 逻辑生成一致快照,随后同步快照并导入正式表,保证正式表与快照一致。
|
||||
- 本阶段不重写登录、刷新、登出内部业务规则,避免在 JWT、refresh rotation、微信绑定合并等复杂语义中引入行为漂移。
|
||||
|
||||
## 2. 非目标
|
||||
|
||||
- 不在本阶段把 `PasswordEntryService`、`PhoneAuthService`、`RefreshSessionService` 改造成直接调用 SpacetimeDB reducer。
|
||||
- 不在前端增加认证规则说明文案。
|
||||
- 不删除 Stage 1 快照表;快照表继续作为导入载体与回滚兜底。
|
||||
|
||||
## 3. 运行流程
|
||||
|
||||
### 3.1 启动恢复
|
||||
|
||||
1. API 调用 `export_auth_store_snapshot_from_tables`。
|
||||
2. 若正式表已有用户、身份或会话数据,则返回兼容 `module-auth` 的 JSON 快照。
|
||||
3. API 用 `InMemoryAuthStore::from_snapshot_json` 恢复认证服务。
|
||||
4. 若正式表为空或调用失败,则回退到 Stage 1 的 `auth_store_snapshot`。
|
||||
5. 若 Stage 1 也不可用,则回退本地 JSON 热修复文件。
|
||||
|
||||
### 3.2 运行期同步
|
||||
|
||||
1. 登录、刷新、登出等路径继续调用当前内存认证服务。
|
||||
2. 每次认证状态变更后调用 `upsert_auth_store_snapshot`。
|
||||
3. 快照写入成功后调用 `import_auth_store_snapshot`,覆盖导入正式表。
|
||||
4. 导入失败时返回错误,避免用户误以为状态已经持久化。
|
||||
|
||||
## 4. 数据重建规则
|
||||
|
||||
- `users_by_username` 由 `user_account.username` 作为 key 重建。
|
||||
- `phone_to_user_id` 由 provider 为 `phone` 的 `auth_identity` 重建。
|
||||
- `wechat_identity_by_provider_uid` 由 provider 为 `wechat` 的 `auth_identity` 重建。
|
||||
- `user_id_by_provider_union_id` 由微信身份中非空 `provider_union_id` 重建。
|
||||
- `sessions_by_id` 由 `refresh_session.session_id` 重建。
|
||||
- `session_id_by_refresh_token_hash` 由 `refresh_session.refresh_token_hash` 重建。
|
||||
- `next_user_id` 取现有 `user_id` 中 `user_数字` 的最大值加一,若不存在则为 1。
|
||||
|
||||
## 5. 完成定义
|
||||
|
||||
- SpacetimeDB 模块能 wasm 编译。
|
||||
- Rust bindings 已重新生成并包含正式表导出 procedure。
|
||||
- `spacetime-client` 暴露正式表导出 facade。
|
||||
- `api-server` 启动恢复优先正式表,认证变更同步后导入正式表。
|
||||
- `module-auth` 测试保持通过。
|
||||
@@ -0,0 +1,97 @@
|
||||
# Auth SpacetimeDB 拆表 Stage 2
|
||||
|
||||
日期:`2026-04-24`
|
||||
|
||||
## 1. 阶段目标
|
||||
|
||||
Stage 1 已把 Rust 鉴权快照同步到 SpacetimeDB 的 `auth_store_snapshot` 表。本阶段继续把该快照导入正式认证表,建立后续运行时细粒度读写的表结构基础。
|
||||
|
||||
本阶段落地范围:
|
||||
|
||||
1. 新增 `user_account` 表。
|
||||
2. 新增 `auth_identity` 表。
|
||||
3. 新增 `refresh_session` 表。
|
||||
4. 新增 `import_auth_store_snapshot` procedure,把当前 `auth_store_snapshot.snapshot_json` 拆入三张表。
|
||||
5. 保留 Stage 1 快照表作为导入来源与回滚备份。
|
||||
|
||||
## 2. 非目标
|
||||
|
||||
本阶段不立即把 `api-server` 的登录、refresh、logout 写入切换到细粒度 reducer。原因是要避免同时改动认证业务语义、导入逻辑和运行时写路径。
|
||||
|
||||
运行时切换放到 Stage 3:
|
||||
|
||||
1. `POST /api/auth/refresh` 改写 `refresh_session` 表。
|
||||
2. 登录成功写 `user_account/auth_identity/refresh_session`。
|
||||
3. `logout/logout-all/revoke-session` 改写细粒度表。
|
||||
4. `auth_store_snapshot` 退化为迁移备份。
|
||||
|
||||
## 3. 表设计落地口径
|
||||
|
||||
### 3.1 `user_account`
|
||||
|
||||
字段先覆盖当前 `module-auth` 快照可提供的账号主数据:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `user_id` | `String` | 主键。 |
|
||||
| `public_user_code` | `String` | 公开叙世号。 |
|
||||
| `username` | `String` | 当前账号用户名。 |
|
||||
| `display_name` | `String` | 展示名。 |
|
||||
| `phone_number_masked` | `Option<String>` | 脱敏手机号。 |
|
||||
| `phone_number_e164` | `Option<String>` | 内部手机号索引。 |
|
||||
| `login_method` | `String` | `password/phone/wechat`。 |
|
||||
| `binding_status` | `String` | `active/pending_bind_phone`。 |
|
||||
| `wechat_bound` | `bool` | 是否绑定微信身份。 |
|
||||
| `password_hash` | `String` | 密码哈希。 |
|
||||
| `password_login_enabled` | `bool` | 是否允许密码登录。 |
|
||||
| `token_version` | `u64` | access token 统一失效版本。 |
|
||||
|
||||
### 3.2 `auth_identity`
|
||||
|
||||
当前只导入已有快照中的微信身份与手机号身份:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `identity_id` | `String` | 主键。 |
|
||||
| `user_id` | `String` | 归属账号。 |
|
||||
| `provider` | `String` | `phone/wechat`。 |
|
||||
| `provider_uid` | `String` | provider 主体键。 |
|
||||
| `provider_union_id` | `Option<String>` | 微信 unionid。 |
|
||||
| `phone_e164` | `Option<String>` | 手机号身份。 |
|
||||
| `display_name` | `Option<String>` | provider 显示名。 |
|
||||
| `avatar_url` | `Option<String>` | provider 头像。 |
|
||||
|
||||
### 3.3 `refresh_session`
|
||||
|
||||
字段对齐现有 refresh session 记录:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `session_id` | `String` | 主键。 |
|
||||
| `user_id` | `String` | 归属账号。 |
|
||||
| `refresh_token_hash` | `String` | 当前 refresh token hash。 |
|
||||
| `issued_by_provider` | `String` | 创建来源。 |
|
||||
| `client_info_json` | `String` | 当前客户端身份 JSON。 |
|
||||
| `expires_at` | `String` | RFC3339。 |
|
||||
| `revoked_at` | `Option<String>` | RFC3339。 |
|
||||
| `created_at` | `String` | RFC3339。 |
|
||||
| `updated_at` | `String` | RFC3339。 |
|
||||
| `last_seen_at` | `String` | RFC3339。 |
|
||||
|
||||
## 4. 导入语义
|
||||
|
||||
`import_auth_store_snapshot` 固定行为:
|
||||
|
||||
1. 读取 `auth_store_snapshot/default`。
|
||||
2. JSON 解析失败返回 `ok=false` 和中文错误。
|
||||
3. 导入前清空三张正式 auth 表,避免重复导入产生脏数据。
|
||||
4. 按快照内容重建账号、身份、refresh session。
|
||||
5. 返回导入计数,便于本地验证。
|
||||
|
||||
## 5. 完成定义
|
||||
|
||||
1. `spacetime-module` wasm check 通过。
|
||||
2. Rust bindings 已刷新。
|
||||
3. `spacetime-client` 暴露导入 procedure facade。
|
||||
4. `api-server/spacetime-client/module-auth` 定向检查通过。
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Rust 本地联调与远端发布脚本方案
|
||||
# Rust 本地联调与远端发布脚本方案
|
||||
|
||||
日期:`2026-04-22`
|
||||
|
||||
@@ -32,12 +32,13 @@ npm run dev:rust
|
||||
默认流程:
|
||||
|
||||
1. 检查 `cargo`、`node` 与 `spacetime` CLI。
|
||||
2. 启动 `spacetime --root-dir=server-rs/.spacetimedb/local start --edition standalone --listen-addr 127.0.0.1:3101`,确保本地数据库与 SpacetimeDB 内部日志不会落到开发者全局目录。
|
||||
3. 等待 `spacetime server ping http://127.0.0.1:3101` 可用。
|
||||
4. 执行 `spacetime publish <本地数据库名> --server http://127.0.0.1:3101 --module-path server-rs/crates/spacetime-module --yes`。
|
||||
5. 注入 `GENARRATIVE_API_*` 与 `GENARRATIVE_SPACETIME_*` 后启动 `cargo run -p api-server`;直接运行 `api-server` 时,如未显式设置 `GENARRATIVE_SPACETIME_DATABASE`,服务端也会向上查找 `spacetime.local.json` 作为本地默认库名。
|
||||
6. 注入 `GENARRATIVE_BACKEND_STACK=rust`、`RUST_SERVER_TARGET`、`GENARRATIVE_RUNTIME_SERVER_TARGET` 后启动 Vite。
|
||||
7. 任一子进程退出时,脚本回收其余子进程。
|
||||
2. Windows Git Bash 下如 `server-rs/.spacetimedb/local/bin/current/spacetimedb-cli.exe` 不存在,先把本机 `spacetime` 所在安装目录的 `bin/` 与 `spacetime.exe` 同步到 `server-rs/.spacetimedb/local/`。
|
||||
3. 启动 `spacetime --root-dir=server-rs/.spacetimedb/local start --edition standalone --listen-addr 127.0.0.1:3101`,确保本地数据库与 SpacetimeDB 内部日志不会落到开发者全局目录。
|
||||
4. 等待 `spacetime --root-dir=server-rs/.spacetimedb/local server ping http://127.0.0.1:3101` 可用。
|
||||
5. 执行 `spacetime --root-dir=server-rs/.spacetimedb/local publish <本地数据库名> --server http://127.0.0.1:3101 --module-path server-rs/crates/spacetime-module -c=on-conflict --yes`,确保 publish 的签名身份与 standalone 的本地控制库一致,并在当前开发阶段允许新版模块表结构变化且发生 schema 冲突时清除旧模块数据。
|
||||
6. 注入 `GENARRATIVE_API_*` 与 `GENARRATIVE_SPACETIME_*` 后启动 `cargo run -p api-server`;直接运行 `api-server` 时,如未显式设置 `GENARRATIVE_SPACETIME_DATABASE`,服务端也会向上查找 `spacetime.local.json` 作为本地默认库名。
|
||||
7. 注入 `GENARRATIVE_BACKEND_STACK=rust`、`RUST_SERVER_TARGET`、`GENARRATIVE_RUNTIME_SERVER_TARGET` 后启动 Vite。
|
||||
8. 任一子进程退出时,脚本回收其余子进程。
|
||||
|
||||
Vite 代理覆盖范围:
|
||||
|
||||
@@ -47,10 +48,11 @@ Vite 代理覆盖范围:
|
||||
|
||||
安全边界:
|
||||
|
||||
1. 默认不执行 `--clear-database`。
|
||||
2. 只有显式传入 `--clear-database` 才允许清库重发。
|
||||
1. 当前开发阶段默认执行 `-c=on-conflict`,允许本地开发库在表结构变化时清除旧模块数据后重发。
|
||||
2. 只有显式传入 `--preserve-database` 时,才跳过 `-c=on-conflict` 并保留现有数据。
|
||||
3. 如需要复用已经启动的 SpacetimeDB,可传 `--skip-spacetime`。
|
||||
4. 如只想启动进程不发布模块,可传 `--skip-publish`。
|
||||
5. 后续进入正式版本前,涉及表结构变化时必须在开发阶段补齐迁移表与迁移函数,不能依赖清库发布作为正式升级策略。
|
||||
|
||||
常用示例:
|
||||
|
||||
@@ -59,7 +61,7 @@ npm run dev:rust
|
||||
./scripts/dev-rust-stack.sh
|
||||
./scripts/dev-rust-stack.sh --api-port 8090 --spacetime-port 3110 --database genarrative-dev
|
||||
./scripts/dev-rust-stack.sh --skip-spacetime --skip-publish
|
||||
./scripts/dev-rust-stack.sh --clear-database
|
||||
./scripts/dev-rust-stack.sh --preserve-database
|
||||
```
|
||||
|
||||
日志提取:
|
||||
@@ -72,7 +74,7 @@ npm run dev:rust:logs -- --follow
|
||||
|
||||
日志提取规则:
|
||||
|
||||
1. SpacetimeDB 模块日志以 `spacetime logs <database>` 为唯一提取入口,脚本不直接读取内部日志文件结构。
|
||||
1. SpacetimeDB 模块日志以 `spacetime --root-dir=server-rs/.spacetimedb/local logs <database>` 为唯一提取入口,脚本不直接读取内部日志文件结构。
|
||||
2. 默认读取 `spacetime.local.json` 的 `database` 字段,默认 server 为 `http://127.0.0.1:3101`。
|
||||
3. 默认输出到 `logs/spacetime/<database>-<timestamp>.log`,并通过 `tee` 同步显示在终端。
|
||||
4. `--follow` 仅用于本地追踪,会持续追加到同一个输出文件;停止时用 `Ctrl+C`。
|
||||
@@ -80,7 +82,7 @@ npm run dev:rust:logs -- --follow
|
||||
联调排错补充:
|
||||
|
||||
1. 如果首页公开广场出现 `上游服务请求失败`,优先检查 `api-server` 错误详情里的 `ws://.../v1/database/<database>/subscribe` 是否指向了未发布的库。
|
||||
2. `spacetime list --server http://127.0.0.1:3101` 应能看到 `spacetime.local.json` 中的库名;若没有,执行 `spacetime publish <本地数据库名> --server http://127.0.0.1:3101 --module-path server-rs/crates/spacetime-module --yes`。
|
||||
2. `spacetime --root-dir=server-rs/.spacetimedb/local list --server http://127.0.0.1:3101` 应能看到 `spacetime.local.json` 中的库名;若没有,执行 `spacetime --root-dir=server-rs/.spacetimedb/local publish <本地数据库名> --server http://127.0.0.1:3101 --module-path server-rs/crates/spacetime-module -c=on-conflict --yes`。
|
||||
3. 发布库名与 `GENARRATIVE_SPACETIME_DATABASE` 不一致时,`/api/runtime/custom-world-gallery` 会从 Rust `api-server` 返回 `502`,前端首页只能展示空态或错误提示,无法自行修复。
|
||||
|
||||
## 3. Ubuntu 发布包脚本
|
||||
@@ -108,7 +110,7 @@ npm run deploy:rust:remote
|
||||
5. 执行 `cargo build -p spacetime-module --release --target wasm32-unknown-unknown --manifest-path server-rs/Cargo.toml`,并把 `spacetime_module.wasm` 复制到目标目录。
|
||||
6. 把仓库根目录的 `.env` 与 `.env.local` 分别复制到目标目录根部和目标目录的 `web/` 下。
|
||||
7. 在目标目录写入 `web-server.mjs`,用于托管 `web/` 并把 `/api/*`、`/generated-*`、`/healthz` 反代到本包内的 `api-server`。
|
||||
8. 在目标目录写入 `start.sh` 与 `stop.sh`;`start.sh` 会先加载发布目录根部的 `.env`、`.env.local`,再回退到构建时通过 `--database`、`--api-port`、`--web-port`、`--spacetime-host`、`--spacetime-port` 写入的默认值,并默认导出 `NO_COLOR=1` 与 `CARGO_TERM_COLOR=never`,避免 ANSI 控制码写入日志文件;如果以 `--clear-database` 启动,则内部 `spacetime publish` 会追加 `-c always`。
|
||||
8. 在目标目录写入 `start.sh` 与 `stop.sh`;`start.sh` 会先加载发布目录根部的 `.env`、`.env.local`,再回退到构建时通过 `--database`、`--api-port`、`--web-port`、`--spacetime-host`、`--spacetime-port` 写入的默认值,并默认导出 `NO_COLOR=1` 与 `CARGO_TERM_COLOR=never`,避免 ANSI 控制码写入日志文件;如果以 `--clear-database` 启动,则内部 `spacetime publish` 会追加 `-c=on-conflict`,仅在 schema 冲突时删除旧模块数据。
|
||||
9. 默认执行 `scp -r -i ~\.ssh\dsk.pem build/<timestamp> ubuntu@82.157.175.59:/home/ubuntu/genarrative/` 上传发布包。
|
||||
|
||||
发布包结构:
|
||||
@@ -150,8 +152,8 @@ cd build/<timestamp>
|
||||
|
||||
1. 构建脚本会把仓库根目录已有的 `.env`、`.env.local` 一并复制进发布包,因此运行前必须确认这些文件内容适合被带入目标环境。
|
||||
2. 如果仓库根目录不存在 `.env` 或 `.env.local`,脚本会打印跳过日志,但不会因此失败;此时 `start.sh` 仅使用构建时写入的默认值与运行时显式传入的环境变量。
|
||||
3. `start.sh` 默认不清空 SpacetimeDB;只有显式执行 `./start.sh --clear-database` 才允许清库重发。
|
||||
4. `start.sh` 使用 `spacetime publish --bin-path spacetime_module.wasm --yes` 发布当前包内 wasm;清库模式下会追加 `-c always`。
|
||||
3. `start.sh` 默认不追加清理参数;只有显式执行 `./start.sh --clear-database` 才追加 `-c=on-conflict`,在 schema 冲突时清理旧模块数据后重发。
|
||||
4. `start.sh` 使用 `spacetime publish --bin-path spacetime_module.wasm --yes` 发布当前包内 wasm;清库模式下会追加 `-c=on-conflict`,仅在 schema 冲突时删除旧模块数据。
|
||||
5. 当前脚本是单目录进程启动方案,不替代生产 systemd、Nginx、TLS、日志轮转与守护进程配置。
|
||||
6. 如只需要本地生成发布包,可传 `--skip-upload` 跳过默认 scp 上传。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user