use chrono::Utc;
use serde::{Deserialize, Serialize};
use tracing::info;
use crate::auth::AuthClaims;
use crate::error::AppError;
use crate::store::KeyspaceHandle;
#[derive(Debug, Serialize, Deserialize)]
struct CacheEntry {
value: String,
expires_at: i64,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CacheGetResponse {
pub key: String,
pub value: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CachePutRequest {
pub value: String,
pub ttl_secs: u64,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CachePutResponse {
pub key: String,
pub expires_at: i64,
}
fn cache_store_key(caller_did: &str, key: &str) -> String {
format!("cache:{caller_did}:{key}")
}
pub async fn get_cached(
cache_ks: &KeyspaceHandle,
auth: &AuthClaims,
key: &str,
channel: &str,
) -> Result<Option<CacheGetResponse>, AppError> {
let store_key = cache_store_key(&auth.did, key);
let entry: Option<CacheEntry> = cache_ks.get(store_key.clone()).await?;
match entry {
Some(e) if e.expires_at > Utc::now().timestamp() => {
info!(channel, key, caller = %auth.did, "cache hit");
Ok(Some(CacheGetResponse {
key: key.to_string(),
value: e.value,
}))
}
Some(_) => {
cache_ks.remove(store_key).await?;
info!(channel, key, caller = %auth.did, "cache miss (expired)");
Ok(None)
}
None => {
info!(channel, key, caller = %auth.did, "cache miss");
Ok(None)
}
}
}
pub async fn put_cached(
cache_ks: &KeyspaceHandle,
auth: &AuthClaims,
key: &str,
req: &CachePutRequest,
channel: &str,
) -> Result<CachePutResponse, AppError> {
let expires_at = Utc::now().timestamp() + req.ttl_secs as i64;
let entry = CacheEntry {
value: req.value.clone(),
expires_at,
};
let store_key = cache_store_key(&auth.did, key);
cache_ks.insert(store_key, &entry).await?;
info!(channel, key, caller = %auth.did, ttl = req.ttl_secs, "cache set");
Ok(CachePutResponse {
key: key.to_string(),
expires_at,
})
}
pub async fn delete_cached(
cache_ks: &KeyspaceHandle,
auth: &AuthClaims,
key: &str,
channel: &str,
) -> Result<(), AppError> {
let store_key = cache_store_key(&auth.did, key);
cache_ks.remove(store_key).await?;
info!(channel, key, caller = %auth.did, "cache cleared");
Ok(())
}