use crate::*; const AUTH_STORE_SNAPSHOT_ID: &str = "default"; #[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)] pub struct AuthStoreSnapshotRecord { pub snapshot_json: Option, pub updated_at_micros: Option, } #[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)] pub struct AuthStoreSnapshotUpsertInput { pub snapshot_json: String, pub updated_at_micros: i64, } #[derive(Clone, Debug, PartialEq, Eq, SpacetimeType)] pub struct AuthStoreSnapshotProcedureResult { pub ok: bool, pub record: Option, pub error_message: Option, } #[spacetimedb::table(accessor = auth_store_snapshot)] pub struct AuthStoreSnapshot { #[primary_key] pub(crate) snapshot_id: String, pub(crate) snapshot_json: String, pub(crate) updated_at: Timestamp, } // Axum 启动恢复认证状态时读取当前快照;记录不存在代表尚未产生登录态。 #[spacetimedb::procedure] pub fn get_auth_store_snapshot(ctx: &mut ProcedureContext) -> AuthStoreSnapshotProcedureResult { match ctx.try_with_tx(|tx| get_auth_store_snapshot_tx(tx)) { Ok(record) => AuthStoreSnapshotProcedureResult { ok: true, record: Some(record), error_message: None, }, Err(message) => AuthStoreSnapshotProcedureResult { ok: false, record: None, error_message: Some(message), }, } } // Axum 每次鉴权仓储变更后覆盖写入整份快照,后续拆表阶段再替换为细粒度 reducer。 #[spacetimedb::procedure] pub fn upsert_auth_store_snapshot( ctx: &mut ProcedureContext, input: AuthStoreSnapshotUpsertInput, ) -> AuthStoreSnapshotProcedureResult { match ctx.try_with_tx(|tx| upsert_auth_store_snapshot_tx(tx, input.clone())) { Ok(record) => AuthStoreSnapshotProcedureResult { ok: true, record: Some(record), error_message: None, }, Err(message) => AuthStoreSnapshotProcedureResult { ok: false, record: None, error_message: Some(message), }, } } fn get_auth_store_snapshot_tx(ctx: &ReducerContext) -> Result { Ok( match ctx .db .auth_store_snapshot() .snapshot_id() .find(&AUTH_STORE_SNAPSHOT_ID.to_string()) { Some(row) => AuthStoreSnapshotRecord { snapshot_json: Some(row.snapshot_json), updated_at_micros: Some(row.updated_at.to_micros_since_unix_epoch()), }, None => AuthStoreSnapshotRecord { snapshot_json: None, updated_at_micros: None, }, }, ) } fn upsert_auth_store_snapshot_tx( ctx: &ReducerContext, input: AuthStoreSnapshotUpsertInput, ) -> Result { let snapshot_json = input.snapshot_json.trim().to_string(); if snapshot_json.is_empty() { return Err("认证快照 JSON 不能为空".to_string()); } let updated_at = Timestamp::from_micros_since_unix_epoch(input.updated_at_micros); if ctx .db .auth_store_snapshot() .snapshot_id() .find(&AUTH_STORE_SNAPSHOT_ID.to_string()) .is_some() { ctx.db .auth_store_snapshot() .snapshot_id() .delete(&AUTH_STORE_SNAPSHOT_ID.to_string()); } ctx.db.auth_store_snapshot().insert(AuthStoreSnapshot { snapshot_id: AUTH_STORE_SNAPSHOT_ID.to_string(), snapshot_json: snapshot_json.clone(), updated_at, }); Ok(AuthStoreSnapshotRecord { snapshot_json: Some(snapshot_json), updated_at_micros: Some(input.updated_at_micros), }) }