feat: add runtime policy and observability
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
237899745
2026-07-25 20:00:11 +08:00
parent f3c7a77a37
commit 64b1169e8c
32 changed files with 1236 additions and 120 deletions

View File

@@ -1298,6 +1298,7 @@ async fn update_stripe_config(
Some(admin_id),
)
.await?;
audit_config_action(&state, admin_id, "stripe", ip).await?;
Ok(Json(Envelope {
success: true,
@@ -1363,6 +1364,7 @@ async fn update_auth_config(
Some(admin_id),
)
.await?;
audit_config_action(&state, admin_id, "auth", ip).await?;
Ok(Json(Envelope {
success: true,
@@ -1520,6 +1522,7 @@ async fn update_mail_config(
Some(admin_id),
)
.await?;
audit_config_action(&state, admin_id, "mail", ip).await?;
Ok(Json(Envelope {
success: true,
@@ -1652,6 +1655,10 @@ async fn update_config(
if key.is_empty() {
return Err(AppError::new(ErrorCode::InvalidRequest, "key 不能为空"));
}
if key.len() > 100 {
return Err(AppError::new(ErrorCode::InvalidRequest, "key 过长"));
}
settings::validate_runtime_config_value(key, &req.value)?;
let row = sqlx::query_as::<_, ConfigRow>(
r#"
@@ -1673,12 +1680,38 @@ async fn update_config(
.await
.map_err(|err| AppError::new(ErrorCode::Internal, "更新配置失败").with_source(err))?;
if matches!(key, "auth" | "features" | "rate_limits" | "file_limits") {
state.runtime_policy_cache.invalidate().await;
}
audit_config_action(&state, admin_id, key, ip).await?;
Ok(Json(Envelope {
success: true,
data: row,
}))
}
async fn audit_config_action(
state: &AppState,
admin_id: Uuid,
key: &str,
ip: IpAddr,
) -> Result<(), AppError> {
sqlx::query(
r#"
INSERT INTO audit_logs (user_id, action, resource_type, details, ip_address)
VALUES ($1, 'system_config_update', 'system_config', $2, $3::inet)
"#,
)
.bind(admin_id)
.bind(serde_json::json!({ "key": key }))
.bind(ip.to_string())
.execute(&state.db)
.await
.map_err(|err| AppError::new(ErrorCode::Internal, "写入配置审计日志失败").with_source(err))?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;