release(secrets-mcp): 0.5.10 — Web 模块化、性能与错误处理
- 拆分 web.rs 为 web/ 子模块;统一 client_ip 提取 - core: user_scope SQL 复用、env_map N+1 消除、FETCH_ALL 上限调整 - entries 列表页并行查询;PgPool 去 Arc;结构化 NotFound 等错误 - CI: SSH 私钥安全写入;crypto/hex 与依赖清理;MCP 输入长度校验 - AGENTS: API Key 明文存储设计说明
This commit is contained in:
@@ -6,7 +6,7 @@ use uuid::Uuid;
|
||||
|
||||
use crate::models::{Entry, SecretField};
|
||||
|
||||
pub const FETCH_ALL_LIMIT: u32 = 100_000;
|
||||
pub const FETCH_ALL_LIMIT: u32 = 10_000;
|
||||
|
||||
/// Build an ILIKE pattern for fuzzy matching, escaping `%` and `_` literals.
|
||||
pub fn ilike_pattern(value: &str) -> String {
|
||||
@@ -145,7 +145,7 @@ pub async fn run(pool: &PgPool, params: SearchParams<'_>) -> Result<SearchResult
|
||||
let entries = fetch_entries_paged(pool, ¶ms).await?;
|
||||
let entry_ids: Vec<Uuid> = entries.iter().map(|e| e.id).collect();
|
||||
let secret_schemas = if !entry_ids.is_empty() {
|
||||
fetch_secret_schemas(pool, &entry_ids).await?
|
||||
fetch_secrets_for_entries(pool, &entry_ids).await?
|
||||
} else {
|
||||
HashMap::new()
|
||||
};
|
||||
@@ -229,33 +229,6 @@ async fn fetch_entries_paged(pool: &PgPool, a: &SearchParams<'_>) -> Result<Vec<
|
||||
Ok(rows.into_iter().map(Entry::from).collect())
|
||||
}
|
||||
|
||||
/// Fetch secret field names for a set of entry ids (no decryption).
|
||||
pub async fn fetch_secret_schemas(
|
||||
pool: &PgPool,
|
||||
entry_ids: &[Uuid],
|
||||
) -> Result<HashMap<Uuid, Vec<SecretField>>> {
|
||||
if entry_ids.is_empty() {
|
||||
return Ok(HashMap::new());
|
||||
}
|
||||
let fields: Vec<EntrySecretRow> = sqlx::query_as(
|
||||
"SELECT es.entry_id, s.id, s.user_id, s.name, s.type, s.encrypted, s.version, s.created_at, s.updated_at \
|
||||
FROM entry_secrets es \
|
||||
JOIN secrets s ON s.id = es.secret_id \
|
||||
WHERE es.entry_id = ANY($1) \
|
||||
ORDER BY es.entry_id, es.sort_order, s.name",
|
||||
)
|
||||
.bind(entry_ids)
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
|
||||
let mut map: HashMap<Uuid, Vec<SecretField>> = HashMap::new();
|
||||
for f in fields {
|
||||
let entry_id = f.entry_id;
|
||||
map.entry(entry_id).or_default().push(f.secret());
|
||||
}
|
||||
Ok(map)
|
||||
}
|
||||
|
||||
/// Fetch all secret fields (including encrypted bytes) for a set of entry ids.
|
||||
pub async fn fetch_secrets_for_entries(
|
||||
pool: &PgPool,
|
||||
@@ -334,7 +307,10 @@ pub async fn resolve_entry(
|
||||
anyhow::bail!("Not found: '{}'", name)
|
||||
}
|
||||
}
|
||||
1 => Ok(entries.into_iter().next().unwrap()),
|
||||
1 => entries
|
||||
.into_iter()
|
||||
.next()
|
||||
.ok_or_else(|| anyhow::anyhow!("internal: resolve_entry result vanished")),
|
||||
_ => {
|
||||
let folders: Vec<&str> = entries.iter().map(|e| e.folder.as_str()).collect();
|
||||
anyhow::bail!(
|
||||
|
||||
Reference in New Issue
Block a user