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}; /// Decrypt a single named field from an entry. pub async fn get_secret_field( pool: &PgPool, namespace: &str, kind: &str, name: &str, field_name: &str, master_key: &[u8; 32], user_id: Option, ) -> Result { let entries = fetch_entries( pool, Some(namespace), Some(kind), Some(name), &[], None, user_id, ) .await?; let entry = entries .first() .ok_or_else(|| anyhow::anyhow!("Not found: [{}/{}] {}", namespace, kind, name))?; let entry_ids = vec![entry.id]; let secrets_map = fetch_secrets_for_entries(pool, &entry_ids).await?; let fields = secrets_map.get(&entry.id).map(Vec::as_slice).unwrap_or(&[]); let field = fields .iter() .find(|f| f.field_name == field_name) .ok_or_else(|| anyhow::anyhow!("Secret field '{}' not found", field_name))?; crypto::decrypt_json(master_key, &field.encrypted) } /// Decrypt all secret fields from an entry. Returns a map field_name → decrypted Value. pub async fn get_all_secrets( pool: &PgPool, namespace: &str, kind: &str, name: &str, master_key: &[u8; 32], user_id: Option, ) -> Result> { let entries = fetch_entries( pool, Some(namespace), Some(kind), Some(name), &[], None, user_id, ) .await?; let entry = entries .first() .ok_or_else(|| anyhow::anyhow!("Not found: [{}/{}] {}", namespace, kind, name))?; let entry_ids = vec![entry.id]; let secrets_map = fetch_secrets_for_entries(pool, &entry_ids).await?; let fields = secrets_map.get(&entry.id).map(Vec::as_slice).unwrap_or(&[]); let mut map = HashMap::new(); for f in fields { let decrypted = crypto::decrypt_json(master_key, &f.encrypted)?; map.insert(f.field_name.clone(), decrypted); } Ok(map) }