mecha10_core/secrets/
redis.rs1use super::SecretBackend;
4use crate::{Mecha10Error, Result};
5
6pub struct RedisBackend {
28 client: redis::Client,
29 prefix: String,
30}
31
32impl RedisBackend {
33 pub async fn connect(url: &str, prefix: &str) -> Result<Self> {
40 let client = redis::Client::open(url)
41 .map_err(|e| Mecha10Error::Configuration(format!("Failed to connect to Redis: {}", e)))?;
42
43 Ok(Self {
44 client,
45 prefix: prefix.to_string(),
46 })
47 }
48
49 pub async fn from_env(prefix: &str) -> Result<Self> {
51 let url = std::env::var("REDIS_URL").unwrap_or_else(|_| "redis://localhost:6379".to_string());
52 Self::connect(&url, prefix).await
53 }
54
55 fn make_key(&self, key: &str) -> String {
56 format!("{}{}", self.prefix, key)
57 }
58}
59
60#[async_trait::async_trait]
61impl SecretBackend for RedisBackend {
62 async fn get(&self, key: &str) -> Result<String> {
63 use redis::AsyncCommands;
64
65 let mut conn = self
66 .client
67 .get_multiplexed_async_connection()
68 .await
69 .map_err(|e| Mecha10Error::Other(format!("Failed to get Redis connection: {}", e)))?;
70
71 let redis_key = self.make_key(key);
72 let value: Option<String> = conn
73 .get(&redis_key)
74 .await
75 .map_err(|e| Mecha10Error::Other(format!("Failed to get secret from Redis: {}", e)))?;
76
77 value.ok_or_else(|| Mecha10Error::Configuration(format!("Secret '{}' not found in Redis", key)))
78 }
79
80 async fn set(&self, key: &str, value: &str) -> Result<()> {
81 use redis::AsyncCommands;
82
83 let mut conn = self
84 .client
85 .get_multiplexed_async_connection()
86 .await
87 .map_err(|e| Mecha10Error::Other(format!("Failed to get Redis connection: {}", e)))?;
88
89 let redis_key = self.make_key(key);
90 conn.set::<_, _, ()>(&redis_key, value)
91 .await
92 .map_err(|e| Mecha10Error::Other(format!("Failed to set secret in Redis: {}", e)))?;
93
94 Ok(())
95 }
96
97 async fn delete(&self, key: &str) -> Result<()> {
98 use redis::AsyncCommands;
99
100 let mut conn = self
101 .client
102 .get_multiplexed_async_connection()
103 .await
104 .map_err(|e| Mecha10Error::Other(format!("Failed to get Redis connection: {}", e)))?;
105
106 let redis_key = self.make_key(key);
107 conn.del::<_, ()>(&redis_key)
108 .await
109 .map_err(|e| Mecha10Error::Other(format!("Failed to delete secret from Redis: {}", e)))?;
110
111 Ok(())
112 }
113
114 async fn list(&self) -> Result<Vec<String>> {
115 use redis::AsyncCommands;
116
117 let mut conn = self
118 .client
119 .get_multiplexed_async_connection()
120 .await
121 .map_err(|e| Mecha10Error::Other(format!("Failed to get Redis connection: {}", e)))?;
122
123 let pattern = format!("{}*", self.prefix);
124 let keys: Vec<String> = conn
125 .keys(&pattern)
126 .await
127 .map_err(|e| Mecha10Error::Other(format!("Failed to list secrets from Redis: {}", e)))?;
128
129 let prefix_len = self.prefix.len();
131 let keys = keys.into_iter().map(|k| k[prefix_len..].to_string()).collect();
132
133 Ok(keys)
134 }
135}