Persist auth store into formal tables

This commit is contained in:
2026-05-13 23:24:32 +08:00
parent e8648e45fc
commit 166544fae6
14 changed files with 452 additions and 32 deletions

View File

@@ -7,12 +7,14 @@ use super::{
sanitize_identity_component,
},
tables::{
AuthIdentity, AuthStoreSnapshot, RefreshSession, UserAccount, auth_identity,
auth_store_snapshot, refresh_session, user_account,
AuthIdentity, AuthStoreProjectionMeta, AuthStoreSnapshot, RefreshSession, UserAccount,
auth_identity, auth_store_projection_meta, auth_store_snapshot, refresh_session,
user_account,
},
};
const AUTH_STORE_SNAPSHOT_ID: &str = "default";
const AUTH_STORE_PROJECTION_META_ID: &str = "default";
#[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)]
pub struct AuthStoreSnapshotRecord {
@@ -70,7 +72,7 @@ pub fn get_auth_store_snapshot(ctx: &mut ProcedureContext) -> AuthStoreSnapshotP
}
}
// Axum 每次鉴权仓储变更后覆盖写入整份快照,后续拆表阶段再替换为细粒度 reducer
// 历史迁移入口:覆盖写入整份快照,供旧库从 `auth_store_snapshot/default` 导入正式表
#[spacetimedb::procedure]
pub fn upsert_auth_store_snapshot(
ctx: &mut ProcedureContext,
@@ -90,6 +92,26 @@ pub fn upsert_auth_store_snapshot(
}
}
// Axum 运行期认证变更直接导入正式认证表,不再继续刷新 `auth_store_snapshot/default`。
#[spacetimedb::procedure]
pub fn import_auth_store_snapshot_json(
ctx: &mut ProcedureContext,
input: AuthStoreSnapshotUpsertInput,
) -> AuthStoreSnapshotImportProcedureResult {
match ctx.try_with_tx(|tx| import_auth_store_snapshot_json_tx(tx, input.clone())) {
Ok(record) => AuthStoreSnapshotImportProcedureResult {
ok: true,
record: Some(record),
error_message: None,
},
Err(message) => AuthStoreSnapshotImportProcedureResult {
ok: false,
record: None,
error_message: Some(message),
},
}
}
#[spacetimedb::procedure]
pub fn import_auth_store_snapshot(
ctx: &mut ProcedureContext,
@@ -191,10 +213,35 @@ fn import_auth_store_snapshot_tx(
.snapshot_id()
.find(&AUTH_STORE_SNAPSHOT_ID.to_string())
.ok_or_else(|| "认证快照不存在,无法导入正式表".to_string())?;
let parsed = serde_json::from_str::<PersistentAuthStoreSnapshot>(&snapshot.snapshot_json)
import_auth_store_snapshot_json_value_tx(
ctx,
&snapshot.snapshot_json,
snapshot.updated_at.to_micros_since_unix_epoch(),
)
}
fn import_auth_store_snapshot_json_tx(
ctx: &ReducerContext,
input: AuthStoreSnapshotUpsertInput,
) -> Result<AuthStoreSnapshotImportRecord, String> {
import_auth_store_snapshot_json_value_tx(ctx, &input.snapshot_json, input.updated_at_micros)
}
fn import_auth_store_snapshot_json_value_tx(
ctx: &ReducerContext,
snapshot_json: &str,
updated_at_micros: i64,
) -> Result<AuthStoreSnapshotImportRecord, String> {
let snapshot_json = snapshot_json.trim();
if snapshot_json.is_empty() {
return Err("认证快照 JSON 不能为空".to_string());
}
let parsed = serde_json::from_str::<PersistentAuthStoreSnapshot>(snapshot_json)
.map_err(|error| format!("认证快照 JSON 解析失败:{error}"))?;
clear_auth_target_tables(ctx);
upsert_auth_projection_meta(ctx, updated_at_micros);
let mut imported_user_count = 0_u32;
let mut imported_identity_count = 0_u32;
@@ -293,6 +340,12 @@ fn export_auth_store_snapshot_from_tables_tx(
updated_at_micros: None,
});
}
let updated_at_micros = ctx
.db
.auth_store_projection_meta()
.meta_id()
.find(&AUTH_STORE_PROJECTION_META_ID.to_string())
.map(|row| row.updated_at.to_micros_since_unix_epoch());
let mut phone_identity_by_user_id = std::collections::HashMap::new();
let mut phone_to_user_id = std::collections::HashMap::new();
@@ -407,7 +460,7 @@ fn export_auth_store_snapshot_from_tables_tx(
Ok(AuthStoreSnapshotRecord {
snapshot_json: Some(snapshot_json),
updated_at_micros: None,
updated_at_micros,
})
}
@@ -428,3 +481,25 @@ fn clear_auth_target_tables(ctx: &ReducerContext) {
ctx.db.user_account().user_id().delete(&row.user_id);
}
}
fn upsert_auth_projection_meta(ctx: &ReducerContext, updated_at_micros: i64) {
let meta_id = AUTH_STORE_PROJECTION_META_ID.to_string();
if ctx
.db
.auth_store_projection_meta()
.meta_id()
.find(&meta_id)
.is_some()
{
ctx.db
.auth_store_projection_meta()
.meta_id()
.delete(&meta_id);
}
ctx.db
.auth_store_projection_meta()
.insert(AuthStoreProjectionMeta {
meta_id,
updated_at: Timestamp::from_micros_since_unix_epoch(updated_at_micros),
});
}

View File

@@ -8,6 +8,13 @@ pub struct AuthStoreSnapshot {
pub(crate) updated_at: Timestamp,
}
#[spacetimedb::table(accessor = auth_store_projection_meta)]
pub struct AuthStoreProjectionMeta {
#[primary_key]
pub(crate) meta_id: String,
pub(crate) updated_at: Timestamp,
}
#[spacetimedb::table(
accessor = user_account,
index(accessor = by_user_account_username, btree(columns = [username])),

View File

@@ -158,6 +158,7 @@ macro_rules! migration_tables {
$macro_name! {
$($arg,)*
auth_store_snapshot,
auth_store_projection_meta,
user_account,
auth_identity,
refresh_session,