1use async_trait::async_trait;
2pub use greentic_types::{
3 EnvId, InvocationEnvelope, NodeError, NodeResult, TeamId, TenantCtx, TenantId, UserId,
4};
5pub use secrets_core::DefaultResolver;
6use secrets_core::{embedded::SecretsError, errors::Error as CoreError};
7
8#[derive(Clone, Debug)]
9pub struct SecretPath(pub String);
10
11impl SecretPath {
12 pub fn as_str(&self) -> &str {
13 &self.0
14 }
15
16 pub fn to_uri(&self) -> String {
17 let trimmed = self.0.trim_start_matches('/');
18 format!("secret://{}", trimmed)
19 }
20}
21
22#[async_trait]
23pub trait SecretsResolver: Send + Sync {
24 async fn get_json<T>(&self, path: &SecretPath, ctx: &TenantCtx) -> NodeResult<Option<T>>
25 where
26 T: serde::de::DeserializeOwned + Send;
27
28 async fn put_json<T>(&self, path: &SecretPath, ctx: &TenantCtx, value: &T) -> NodeResult<()>
29 where
30 T: serde::Serialize + Sync + Send;
31}
32
33#[async_trait]
34impl SecretsResolver for DefaultResolver {
35 async fn get_json<T>(&self, path: &SecretPath, _ctx: &TenantCtx) -> NodeResult<Option<T>>
36 where
37 T: serde::de::DeserializeOwned + Send,
38 {
39 let uri = path.to_uri();
40 match self.core().get_json::<T>(&uri).await {
41 Ok(value) => Ok(Some(value)),
42 Err(SecretsError::Core(CoreError::NotFound { .. })) => Ok(None),
43 Err(err) => Err(NodeError::new(
44 "secrets_read",
45 format!("failed to fetch secret {}", path.as_str()),
46 )
47 .with_source(err)),
48 }
49 }
50
51 async fn put_json<T>(&self, path: &SecretPath, _ctx: &TenantCtx, value: &T) -> NodeResult<()>
52 where
53 T: serde::Serialize + Sync + Send,
54 {
55 let uri = path.to_uri();
56 self.core()
57 .put_json(&uri, value)
58 .await
59 .map(|_| ())
60 .map_err(|err| {
61 NodeError::new(
62 "secrets_write",
63 format!("failed to store secret {}", path.as_str()),
64 )
65 .with_source(err)
66 })
67 }
68}