- 拆分 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 明文存储设计说明
78 lines
2.3 KiB
Rust
78 lines
2.3 KiB
Rust
use anyhow::Result;
|
|
use serde_json::Value;
|
|
use sqlx::PgPool;
|
|
use std::collections::HashMap;
|
|
use uuid::Uuid;
|
|
|
|
use crate::crypto;
|
|
use crate::service::search::{fetch_entries, fetch_secrets_for_entries};
|
|
|
|
/// Build an env variable map from entry secrets (for dry-run preview or injection).
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub async fn build_env_map(
|
|
pool: &PgPool,
|
|
folder: Option<&str>,
|
|
entry_type: Option<&str>,
|
|
name: Option<&str>,
|
|
tags: &[String],
|
|
only_fields: &[String],
|
|
prefix: &str,
|
|
master_key: &[u8; 32],
|
|
user_id: Option<Uuid>,
|
|
) -> Result<HashMap<String, String>> {
|
|
let entries = fetch_entries(pool, folder, entry_type, name, tags, None, user_id).await?;
|
|
if entries.is_empty() {
|
|
return Ok(HashMap::new());
|
|
}
|
|
|
|
let entry_ids: Vec<Uuid> = entries.iter().map(|e| e.id).collect();
|
|
let secrets_map = fetch_secrets_for_entries(pool, &entry_ids).await?;
|
|
|
|
let mut combined: HashMap<String, String> = HashMap::new();
|
|
|
|
for entry in &entries {
|
|
let all_fields = secrets_map.get(&entry.id).map(Vec::as_slice).unwrap_or(&[]);
|
|
let effective_prefix = env_prefix(entry, prefix);
|
|
|
|
let fields: Vec<_> = if only_fields.is_empty() {
|
|
all_fields.iter().collect()
|
|
} else {
|
|
all_fields
|
|
.iter()
|
|
.filter(|f| only_fields.contains(&f.name))
|
|
.collect()
|
|
};
|
|
|
|
for f in fields {
|
|
let decrypted = crypto::decrypt_json(master_key, &f.encrypted)?;
|
|
let key = format!(
|
|
"{}_{}",
|
|
effective_prefix,
|
|
f.name.to_uppercase().replace(['-', '.'], "_")
|
|
);
|
|
combined.insert(key, json_to_env_string(&decrypted));
|
|
}
|
|
}
|
|
|
|
Ok(combined)
|
|
}
|
|
|
|
fn env_prefix(entry: &crate::models::Entry, prefix: &str) -> String {
|
|
let name_part = entry.name.to_uppercase().replace(['-', '.', ' '], "_");
|
|
if prefix.is_empty() {
|
|
name_part
|
|
} else {
|
|
let normalized = prefix.to_uppercase().replace(['-', '.', ' '], "_");
|
|
let normalized = normalized.trim_end_matches('_');
|
|
format!("{}_{}", normalized, name_part)
|
|
}
|
|
}
|
|
|
|
fn json_to_env_string(v: &Value) -> String {
|
|
match v {
|
|
Value::String(s) => s.clone(),
|
|
Value::Null => String::new(),
|
|
other => other.to_string(),
|
|
}
|
|
}
|