use super::SecretBackend;
use crate::{Mecha10Error, Result};
pub struct RedisBackend {
client: redis::Client,
prefix: String,
}
impl RedisBackend {
pub async fn connect(url: &str, prefix: &str) -> Result<Self> {
let client = redis::Client::open(url)
.map_err(|e| Mecha10Error::Configuration(format!("Failed to connect to Redis: {}", e)))?;
Ok(Self {
client,
prefix: prefix.to_string(),
})
}
pub async fn from_env(prefix: &str) -> Result<Self> {
let url = std::env::var("REDIS_URL").unwrap_or_else(|_| "redis://localhost:6379".to_string());
Self::connect(&url, prefix).await
}
fn make_key(&self, key: &str) -> String {
format!("{}{}", self.prefix, key)
}
async fn connection(&self) -> Result<redis::aio::MultiplexedConnection> {
self.client
.get_multiplexed_async_connection()
.await
.map_err(|e| Mecha10Error::Other(format!("Failed to get Redis connection: {}", e)))
}
}
#[async_trait::async_trait]
impl SecretBackend for RedisBackend {
async fn get(&self, key: &str) -> Result<String> {
use redis::AsyncCommands;
let mut conn = self.connection().await?;
let redis_key = self.make_key(key);
let value: Option<String> = conn
.get(&redis_key)
.await
.map_err(|e| Mecha10Error::Other(format!("Failed to get secret from Redis: {}", e)))?;
value.ok_or_else(|| Mecha10Error::Configuration(format!("Secret '{}' not found in Redis", key)))
}
async fn set(&self, key: &str, value: &str) -> Result<()> {
use redis::AsyncCommands;
let mut conn = self.connection().await?;
let redis_key = self.make_key(key);
conn.set::<_, _, ()>(&redis_key, value)
.await
.map_err(|e| Mecha10Error::Other(format!("Failed to set secret in Redis: {}", e)))?;
Ok(())
}
async fn delete(&self, key: &str) -> Result<()> {
use redis::AsyncCommands;
let mut conn = self.connection().await?;
let redis_key = self.make_key(key);
conn.del::<_, ()>(&redis_key)
.await
.map_err(|e| Mecha10Error::Other(format!("Failed to delete secret from Redis: {}", e)))?;
Ok(())
}
async fn list(&self) -> Result<Vec<String>> {
use redis::AsyncCommands;
let mut conn = self.connection().await?;
let pattern = format!("{}*", self.prefix);
let keys: Vec<String> = conn
.keys(&pattern)
.await
.map_err(|e| Mecha10Error::Other(format!("Failed to list secrets from Redis: {}", e)))?;
let prefix_len = self.prefix.len();
let keys = keys.into_iter().map(|k| k[prefix_len..].to_string()).collect();
Ok(keys)
}
}