25cac367c6
新增 SpacetimeDB 灰度配置表与后端判定链路 新增后台灰度发布页面和两段式 Gate Key 选择器 将画布 Agent 入口接入灰度配置与运行时能力下发 修复灰度配置刷新读取空本地缓存的问题 补充灰度规则和后台下拉维护约定文档
105 lines
3.2 KiB
Rust
105 lines
3.2 KiB
Rust
use crate::*;
|
|
|
|
#[spacetimedb::table(accessor = feature_gate_config)]
|
|
pub struct FeatureGateConfig {
|
|
#[primary_key]
|
|
pub(crate) gate_key: String,
|
|
pub(crate) enabled: bool,
|
|
pub(crate) rollout_percent: u32,
|
|
pub(crate) allow_user_ids: Vec<String>,
|
|
pub(crate) allow_user_tags: Vec<String>,
|
|
pub(crate) deny_user_ids: Vec<String>,
|
|
pub(crate) description: String,
|
|
pub(crate) updated_at: Timestamp,
|
|
}
|
|
|
|
#[spacetimedb::procedure]
|
|
pub fn get_feature_gate_config(ctx: &mut ProcedureContext) -> FeatureGateConfigProcedureResult {
|
|
match ctx.try_with_tx(|tx| Ok(list_feature_gate_config_snapshots(tx))) {
|
|
Ok(records) => FeatureGateConfigProcedureResult {
|
|
ok: true,
|
|
records,
|
|
error_message: None,
|
|
},
|
|
Err(message) => FeatureGateConfigProcedureResult {
|
|
ok: false,
|
|
records: vec![],
|
|
error_message: Some(message),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[spacetimedb::procedure]
|
|
pub fn upsert_feature_gate_config(
|
|
ctx: &mut ProcedureContext,
|
|
input: FeatureGateConfigAdminUpsertInput,
|
|
) -> FeatureGateConfigProcedureResult {
|
|
match ctx.try_with_tx(|tx| upsert_feature_gate_config_in_tx(tx, input.clone())) {
|
|
Ok(records) => FeatureGateConfigProcedureResult {
|
|
ok: true,
|
|
records,
|
|
error_message: None,
|
|
},
|
|
Err(message) => FeatureGateConfigProcedureResult {
|
|
ok: false,
|
|
records: vec![],
|
|
error_message: Some(message),
|
|
},
|
|
}
|
|
}
|
|
|
|
fn upsert_feature_gate_config_in_tx(
|
|
ctx: &ReducerContext,
|
|
input: FeatureGateConfigAdminUpsertInput,
|
|
) -> Result<Vec<FeatureGateConfigSnapshot>, String> {
|
|
let input = module_runtime::normalize_feature_gate_admin_upsert_input(input)?;
|
|
let row = FeatureGateConfig {
|
|
gate_key: input.gate_key.clone(),
|
|
enabled: input.enabled,
|
|
rollout_percent: input.rollout_percent,
|
|
allow_user_ids: input.allow_user_ids,
|
|
allow_user_tags: input.allow_user_tags,
|
|
deny_user_ids: input.deny_user_ids,
|
|
description: input.description,
|
|
updated_at: ctx.timestamp,
|
|
};
|
|
|
|
if ctx
|
|
.db
|
|
.feature_gate_config()
|
|
.gate_key()
|
|
.find(&row.gate_key)
|
|
.is_some()
|
|
{
|
|
ctx.db.feature_gate_config().gate_key().update(row);
|
|
} else {
|
|
ctx.db.feature_gate_config().insert(row);
|
|
}
|
|
|
|
Ok(list_feature_gate_config_snapshots(ctx))
|
|
}
|
|
|
|
fn list_feature_gate_config_snapshots(ctx: &ReducerContext) -> Vec<FeatureGateConfigSnapshot> {
|
|
let mut records = ctx
|
|
.db
|
|
.feature_gate_config()
|
|
.iter()
|
|
.map(feature_gate_config_snapshot_from_row)
|
|
.collect::<Vec<_>>();
|
|
records.sort_by(|left, right| left.gate_key.cmp(&right.gate_key));
|
|
records
|
|
}
|
|
|
|
fn feature_gate_config_snapshot_from_row(row: FeatureGateConfig) -> FeatureGateConfigSnapshot {
|
|
FeatureGateConfigSnapshot {
|
|
gate_key: row.gate_key,
|
|
enabled: row.enabled,
|
|
rollout_percent: row.rollout_percent,
|
|
allow_user_ids: row.allow_user_ids,
|
|
allow_user_tags: row.allow_user_tags,
|
|
deny_user_ids: row.deny_user_ids,
|
|
description: row.description,
|
|
updated_at_micros: row.updated_at.to_micros_since_unix_epoch(),
|
|
}
|
|
}
|