Persist auth store into formal tables
This commit is contained in:
@@ -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),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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])),
|
||||
|
||||
@@ -158,6 +158,7 @@ macro_rules! migration_tables {
|
||||
$macro_name! {
|
||||
$($arg,)*
|
||||
auth_store_snapshot,
|
||||
auth_store_projection_meta,
|
||||
user_account,
|
||||
auth_identity,
|
||||
refresh_session,
|
||||
|
||||
Reference in New Issue
Block a user