feat(auth): 添加异步密码哈希和验证函数
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
refactor(relay): 复用HTTP客户端和请求体序列化结果 feat(kernel): 添加获取单个审批记录的方法 fix(store): 改进SaaS连接错误分类和降级处理 docs: 更新审计文档和系统架构文档 refactor(prompt): 优化SQL查询参数化绑定 refactor(migration): 使用静态SQL和COALESCE更新配置项 feat(commands): 添加审批执行状态追踪和事件通知 chore: 更新启动脚本以支持Admin后台 fix(auth-guard): 优化授权状态管理和错误处理 refactor(db): 使用异步密码哈希函数 refactor(totp): 使用异步密码验证函数 style: 清理无用文件和注释 docs: 更新功能全景和审计文档 refactor(service): 优化HTTP客户端重用和请求处理 fix(connection): 改进SaaS不可用时的降级处理 refactor(handlers): 使用异步密码验证函数 chore: 更新依赖和工具链配置
This commit is contained in:
@@ -54,39 +54,50 @@ pub async fn list_config_items(
|
||||
) -> SaasResult<PaginatedResponse<ConfigItemInfo>> {
|
||||
let (p, ps, offset) = normalize_pagination(page, page_size);
|
||||
|
||||
// Build WHERE clause for count and data queries
|
||||
let (where_clause, has_category, has_source) = match (&query.category, &query.source) {
|
||||
(Some(_), Some(_)) => ("WHERE category = $1 AND source = $2", true, true),
|
||||
(Some(_), None) => ("WHERE category = $1", true, false),
|
||||
(None, Some(_)) => ("WHERE source = $1", false, true),
|
||||
(None, None) => ("", false, false),
|
||||
// Static SQL per combination -- no format!() string interpolation
|
||||
let (total, rows) = match (&query.category, &query.source) {
|
||||
(Some(cat), Some(src)) => {
|
||||
let total: i64 = sqlx::query_scalar(
|
||||
"SELECT COUNT(*) FROM config_items WHERE category = $1 AND source = $2"
|
||||
).bind(cat).bind(src).fetch_one(db).await?;
|
||||
let rows = sqlx::query_as::<_, ConfigItemRow>(
|
||||
"SELECT id, category, key_path, value_type, current_value, default_value, source, description, requires_restart, created_at, updated_at
|
||||
FROM config_items WHERE category = $1 AND source = $2 ORDER BY category, key_path LIMIT $3 OFFSET $4"
|
||||
).bind(cat).bind(src).bind(ps as i64).bind(offset).fetch_all(db).await?;
|
||||
(total, rows)
|
||||
}
|
||||
(Some(cat), None) => {
|
||||
let total: i64 = sqlx::query_scalar(
|
||||
"SELECT COUNT(*) FROM config_items WHERE category = $1"
|
||||
).bind(cat).fetch_one(db).await?;
|
||||
let rows = sqlx::query_as::<_, ConfigItemRow>(
|
||||
"SELECT id, category, key_path, value_type, current_value, default_value, source, description, requires_restart, created_at, updated_at
|
||||
FROM config_items WHERE category = $1 ORDER BY category, key_path LIMIT $2 OFFSET $3"
|
||||
).bind(cat).bind(ps as i64).bind(offset).fetch_all(db).await?;
|
||||
(total, rows)
|
||||
}
|
||||
(None, Some(src)) => {
|
||||
let total: i64 = sqlx::query_scalar(
|
||||
"SELECT COUNT(*) FROM config_items WHERE source = $1"
|
||||
).bind(src).fetch_one(db).await?;
|
||||
let rows = sqlx::query_as::<_, ConfigItemRow>(
|
||||
"SELECT id, category, key_path, value_type, current_value, default_value, source, description, requires_restart, created_at, updated_at
|
||||
FROM config_items WHERE source = $1 ORDER BY category, key_path LIMIT $2 OFFSET $3"
|
||||
).bind(src).bind(ps as i64).bind(offset).fetch_all(db).await?;
|
||||
(total, rows)
|
||||
}
|
||||
(None, None) => {
|
||||
let total: i64 = sqlx::query_scalar(
|
||||
"SELECT COUNT(*) FROM config_items"
|
||||
).fetch_one(db).await?;
|
||||
let rows = sqlx::query_as::<_, ConfigItemRow>(
|
||||
"SELECT id, category, key_path, value_type, current_value, default_value, source, description, requires_restart, created_at, updated_at
|
||||
FROM config_items ORDER BY category, key_path LIMIT $1 OFFSET $2"
|
||||
).bind(ps as i64).bind(offset).fetch_all(db).await?;
|
||||
(total, rows)
|
||||
}
|
||||
};
|
||||
|
||||
let count_sql = format!("SELECT COUNT(*) FROM config_items {}", where_clause);
|
||||
let data_sql = format!(
|
||||
"SELECT id, category, key_path, value_type, current_value, default_value, source, description, requires_restart, created_at, updated_at
|
||||
FROM config_items {} ORDER BY category, key_path LIMIT {} OFFSET {}",
|
||||
where_clause, "$p", "$o"
|
||||
);
|
||||
|
||||
// Determine param indices for LIMIT/OFFSET based on filter params
|
||||
let (limit_idx, offset_idx) = match (has_category, has_source) {
|
||||
(true, true) => ("$3", "$4"),
|
||||
(true, false) | (false, true) => ("$2", "$3"),
|
||||
(false, false) => ("$1", "$2"),
|
||||
};
|
||||
let data_sql = data_sql.replace("$p", limit_idx).replace("$o", offset_idx);
|
||||
|
||||
let mut count_query = sqlx::query_scalar::<_, i64>(&count_sql);
|
||||
if has_category { count_query = count_query.bind(&query.category); }
|
||||
if has_source { count_query = count_query.bind(&query.source); }
|
||||
let total: i64 = count_query.fetch_one(db).await?;
|
||||
|
||||
let mut data_query = sqlx::query_as::<_, ConfigItemRow>(&data_sql);
|
||||
if has_category { data_query = data_query.bind(&query.category); }
|
||||
if has_source { data_query = data_query.bind(&query.source); }
|
||||
let rows = data_query.bind(ps as i64).bind(offset).fetch_all(db).await?;
|
||||
|
||||
let items = rows.into_iter().map(|r| {
|
||||
ConfigItemInfo { id: r.id, category: r.category, key_path: r.key_path, value_type: r.value_type, current_value: r.current_value, default_value: r.default_value, source: r.source, description: r.description, requires_restart: r.requires_restart, created_at: r.created_at, updated_at: r.updated_at }
|
||||
}).collect();
|
||||
@@ -146,29 +157,23 @@ pub async fn update_config_item(
|
||||
db: &PgPool, item_id: &str, req: &UpdateConfigItemRequest,
|
||||
) -> SaasResult<ConfigItemInfo> {
|
||||
let now = chrono::Utc::now().to_rfc3339();
|
||||
let mut updates = Vec::new();
|
||||
let mut params: Vec<String> = Vec::new();
|
||||
let mut param_idx = 1usize;
|
||||
|
||||
if let Some(ref v) = req.current_value { updates.push(format!("current_value = ${}", param_idx)); params.push(v.clone()); param_idx += 1; }
|
||||
if let Some(ref v) = req.source { updates.push(format!("source = ${}", param_idx)); params.push(v.clone()); param_idx += 1; }
|
||||
if let Some(ref v) = req.description { updates.push(format!("description = ${}", param_idx)); params.push(v.clone()); param_idx += 1; }
|
||||
|
||||
if updates.is_empty() {
|
||||
return get_config_item(db, item_id).await;
|
||||
}
|
||||
|
||||
updates.push(format!("updated_at = ${}", param_idx));
|
||||
params.push(now);
|
||||
param_idx += 1;
|
||||
params.push(item_id.to_string());
|
||||
|
||||
let sql = format!("UPDATE config_items SET {} WHERE id = ${}", updates.join(", "), param_idx);
|
||||
let mut query = sqlx::query(&sql);
|
||||
for p in ¶ms {
|
||||
query = query.bind(p);
|
||||
}
|
||||
query.execute(db).await?;
|
||||
// COALESCE pattern: all updatable fields in a single static SQL.
|
||||
// NULL parameters leave the column unchanged.
|
||||
sqlx::query(
|
||||
"UPDATE config_items SET
|
||||
current_value = COALESCE($1, current_value),
|
||||
source = COALESCE($2, source),
|
||||
description = COALESCE($3, description),
|
||||
updated_at = $4
|
||||
WHERE id = $5"
|
||||
)
|
||||
.bind(req.current_value.as_deref())
|
||||
.bind(req.source.as_deref())
|
||||
.bind(req.description.as_deref())
|
||||
.bind(&now)
|
||||
.bind(item_id)
|
||||
.execute(db).await?;
|
||||
|
||||
get_config_item(db, item_id).await
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user