use crate::db::Database;
use anyhow::Result;
const PREFIX: &str = "apikey:";
pub async fn set_key(db: &Database, env_name: &str, value: &str) -> Result<()> {
let key = format!("{PREFIX}{env_name}");
db.kv_set(&key, value).await
}
pub async fn get_key(db: &Database, env_name: &str) -> Result<Option<String>> {
let key = format!("{PREFIX}{env_name}");
db.kv_get(&key).await
}
pub async fn remove_key(db: &Database, env_name: &str) -> Result<()> {
let key = format!("{PREFIX}{env_name}");
db.kv_delete(&key).await
}
pub async fn inject_into_env(db: &Database) -> Result<()> {
let entries = db.kv_list_prefix(PREFIX).await?;
for (key, value) in entries {
let env_name = &key[PREFIX.len()..];
if crate::runtime_env::get(env_name).is_none() {
crate::runtime_env::set(env_name, &value);
tracing::debug!("Injected stored key: {env_name}");
}
}
Ok(())
}
pub fn mask_key(key: &str) -> String {
if key.len() > 8 {
format!("{}...{}", &key[..4], &key[key.len() - 4..])
} else {
"****".to_string()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mask_key() {
assert_eq!(mask_key("sk-ant-api03-longkey1234"), "sk-a...1234");
assert_eq!(mask_key("short"), "****");
}
#[tokio::test]
async fn test_keystore_roundtrip() {
let tmp = tempfile::TempDir::new().unwrap();
let db = Database::init(tmp.path()).await.unwrap();
set_key(&db, "TEST_KEY", "test-value").await.unwrap();
let val = get_key(&db, "TEST_KEY").await.unwrap();
assert_eq!(val.as_deref(), Some("test-value"));
remove_key(&db, "TEST_KEY").await.unwrap();
let val = get_key(&db, "TEST_KEY").await.unwrap();
assert_eq!(val, None);
}
#[tokio::test]
async fn test_inject_skips_existing() {
let tmp = tempfile::TempDir::new().unwrap();
let db = Database::init(tmp.path()).await.unwrap();
let unique = format!("KODA_TEST_{}", std::process::id());
crate::runtime_env::set(&unique, "original");
set_key(&db, &unique, "overwritten").await.unwrap();
inject_into_env(&db).await.unwrap();
assert_eq!(
crate::runtime_env::get(&unique).as_deref(),
Some("original")
);
}
}