alien_bindings/providers/vault/
local.rs1use crate::error::{ErrorData, Result};
2use alien_error::{AlienError, Context, IntoAlienError};
3use async_trait::async_trait;
4use std::collections::HashMap;
5use std::path::PathBuf;
6
7#[derive(Debug)]
12pub struct LocalVault {
13 vault_name: String,
14 vault_dir: PathBuf,
15}
16
17impl LocalVault {
18 pub fn new(vault_name: String, vault_dir: PathBuf) -> Self {
24 Self {
25 vault_name,
26 vault_dir,
27 }
28 }
29
30 fn secrets_file_path(&self) -> PathBuf {
32 self.vault_dir.join("secrets.json")
33 }
34
35 async fn load_secrets(&self) -> Result<HashMap<String, String>> {
37 let secrets_file = self.secrets_file_path();
38
39 if !secrets_file.exists() {
40 return Ok(HashMap::new());
41 }
42
43 let content = tokio::fs::read_to_string(&secrets_file)
44 .await
45 .into_alien_error()
46 .context(ErrorData::CloudPlatformError {
47 message: format!(
48 "Failed to read vault secrets file: {}",
49 secrets_file.display()
50 ),
51 resource_id: None,
52 })?;
53
54 serde_json::from_str(&content)
55 .into_alien_error()
56 .context(ErrorData::CloudPlatformError {
57 message: format!(
58 "Failed to parse vault secrets file: {}",
59 secrets_file.display()
60 ),
61 resource_id: None,
62 })
63 }
64
65 async fn save_secrets(&self, secrets: &HashMap<String, String>) -> Result<()> {
67 let secrets_file = self.secrets_file_path();
68
69 if let Some(parent) = secrets_file.parent() {
71 tokio::fs::create_dir_all(parent)
72 .await
73 .into_alien_error()
74 .context(ErrorData::CloudPlatformError {
75 message: format!("Failed to create vault directory: {}", parent.display()),
76 resource_id: None,
77 })?;
78 }
79
80 let json = serde_json::to_string_pretty(secrets)
81 .into_alien_error()
82 .context(ErrorData::CloudPlatformError {
83 message: "Failed to serialize vault secrets".to_string(),
84 resource_id: None,
85 })?;
86
87 let path = secrets_file.clone();
88 let data = json.into_bytes();
89 tokio::task::spawn_blocking(move || alien_core::file_utils::write_secret_file(&path, &data))
90 .await
91 .into_alien_error()
92 .context(ErrorData::CloudPlatformError {
93 message: "Failed to spawn blocking write task".to_string(),
94 resource_id: None,
95 })?
96 .into_alien_error()
97 .context(ErrorData::CloudPlatformError {
98 message: format!(
99 "Failed to write vault secrets file: {}",
100 secrets_file.display()
101 ),
102 resource_id: None,
103 })
104 }
105}
106
107#[async_trait]
108impl crate::traits::Binding for LocalVault {}
109
110#[async_trait]
111impl crate::traits::Vault for LocalVault {
112 async fn get_secret(&self, secret_name: &str) -> Result<String> {
114 let secrets = self.load_secrets().await?;
115
116 secrets.get(secret_name).cloned().ok_or_else(|| {
117 AlienError::new(ErrorData::CloudPlatformError {
118 message: format!(
119 "Secret '{}' not found in vault '{}'",
120 secret_name, self.vault_name
121 ),
122 resource_id: None,
123 })
124 })
125 }
126
127 async fn set_secret(&self, secret_name: &str, value: &str) -> Result<()> {
129 let mut secrets = self.load_secrets().await?;
130 secrets.insert(secret_name.to_string(), value.to_string());
131 self.save_secrets(&secrets).await
132 }
133
134 async fn delete_secret(&self, secret_name: &str) -> Result<()> {
136 let mut secrets = self.load_secrets().await?;
137
138 secrets.remove(secret_name).ok_or_else(|| {
139 AlienError::new(ErrorData::CloudPlatformError {
140 message: format!(
141 "Secret '{}' not found in vault '{}'",
142 secret_name, self.vault_name
143 ),
144 resource_id: None,
145 })
146 })?;
147
148 self.save_secrets(&secrets).await
149 }
150}