feat(saas): Phase 4 — 配置迁移模块

- 配置项 CRUD (列表/详情/创建/更新/删除)
- 配置分析端点 (按类别汇总, SaaS 托管统计)
- 13 个默认配置项种子数据 (server/agent/memory/llm)
- 配置同步协议 (客户端→SaaS, SaaS 优先策略)
- 同步日志记录和查询
- 3 个新集成测试覆盖配置迁移端点
This commit is contained in:
iven
2026-03-27 12:52:42 +08:00
parent a99a3df9dd
commit 00a08c9f9b
6 changed files with 569 additions and 0 deletions

View File

@@ -0,0 +1,104 @@
//! 配置迁移 HTTP 处理器
use axum::{
extract::{Extension, Path, Query, State},
http::StatusCode, Json,
};
use crate::state::AppState;
use crate::error::SaasResult;
use crate::auth::types::AuthContext;
use super::{types::*, service};
/// GET /api/v1/config/items?category=xxx&source=xxx
pub async fn list_config_items(
State(state): State<AppState>,
Query(query): Query<ConfigQuery>,
_ctx: Extension<AuthContext>,
) -> SaasResult<Json<Vec<ConfigItemInfo>>> {
service::list_config_items(&state.db, &query).await.map(Json)
}
/// GET /api/v1/config/items/:id
pub async fn get_config_item(
State(state): State<AppState>,
Path(id): Path<String>,
_ctx: Extension<AuthContext>,
) -> SaasResult<Json<ConfigItemInfo>> {
service::get_config_item(&state.db, &id).await.map(Json)
}
/// POST /api/v1/config/items (admin only)
pub async fn create_config_item(
State(state): State<AppState>,
Extension(ctx): Extension<AuthContext>,
Json(req): Json<CreateConfigItemRequest>,
) -> SaasResult<(StatusCode, Json<ConfigItemInfo>)> {
if !ctx.permissions.contains(&"config:manage".to_string()) {
return Err(crate::error::SaasError::Forbidden("需要 config:manage 权限".into()));
}
let item = service::create_config_item(&state.db, &req).await?;
Ok((StatusCode::CREATED, Json(item)))
}
/// PUT /api/v1/config/items/:id (admin only)
pub async fn update_config_item(
State(state): State<AppState>,
Path(id): Path<String>,
Extension(ctx): Extension<AuthContext>,
Json(req): Json<UpdateConfigItemRequest>,
) -> SaasResult<Json<ConfigItemInfo>> {
if !ctx.permissions.contains(&"config:manage".to_string()) {
return Err(crate::error::SaasError::Forbidden("需要 config:manage 权限".into()));
}
service::update_config_item(&state.db, &id, &req).await.map(Json)
}
/// DELETE /api/v1/config/items/:id (admin only)
pub async fn delete_config_item(
State(state): State<AppState>,
Path(id): Path<String>,
Extension(ctx): Extension<AuthContext>,
) -> SaasResult<Json<serde_json::Value>> {
if !ctx.permissions.contains(&"config:manage".to_string()) {
return Err(crate::error::SaasError::Forbidden("需要 config:manage 权限".into()));
}
service::delete_config_item(&state.db, &id).await?;
Ok(Json(serde_json::json!({"ok": true})))
}
/// GET /api/v1/config/analysis
pub async fn analyze_config(
State(state): State<AppState>,
_ctx: Extension<AuthContext>,
) -> SaasResult<Json<ConfigAnalysis>> {
service::analyze_config(&state.db).await.map(Json)
}
/// POST /api/v1/config/seed (admin only)
pub async fn seed_config(
State(state): State<AppState>,
Extension(ctx): Extension<AuthContext>,
) -> SaasResult<Json<serde_json::Value>> {
if !ctx.permissions.contains(&"config:manage".to_string()) {
return Err(crate::error::SaasError::Forbidden("需要 config:manage 权限".into()));
}
let count = service::seed_default_config_items(&state.db).await?;
Ok(Json(serde_json::json!({"created": count})))
}
/// POST /api/v1/config/sync
pub async fn sync_config(
State(state): State<AppState>,
Extension(ctx): Extension<AuthContext>,
Json(req): Json<SyncConfigRequest>,
) -> SaasResult<Json<Vec<ConfigSyncLogInfo>>> {
service::sync_config(&state.db, &ctx.account_id, &req).await.map(Json)
}
/// GET /api/v1/config/sync-logs
pub async fn list_sync_logs(
State(state): State<AppState>,
Extension(ctx): Extension<AuthContext>,
) -> SaasResult<Json<Vec<ConfigSyncLogInfo>>> {
service::list_sync_logs(&state.db, &ctx.account_id).await.map(Json)
}