use async_trait::async_trait;
use chrono::{DateTime, Utc};
use super::JtiCache;
use crate::error::AuthError;
pub struct RedisJtiCache {
client: redis::Client,
}
impl RedisJtiCache {
pub fn new(redis_url: &str) -> Result<Self, AuthError> {
let client = redis::Client::open(redis_url)
.map_err(|e| AuthError::InternalError(format!("Redis connection error: {}", e)))?;
Ok(Self { client })
}
fn key(jti: &str) -> String {
format!("hfs:jti:{}", jti)
}
}
#[async_trait]
impl JtiCache for RedisJtiCache {
async fn check_and_store(
&self,
jti: &str,
expires_at: DateTime<Utc>,
) -> Result<bool, AuthError> {
let mut conn = self
.client
.get_multiplexed_async_connection()
.await
.map_err(|e| AuthError::InternalError(format!("Redis connection error: {}", e)))?;
let now = Utc::now();
let ttl_secs = if expires_at > now {
(expires_at - now).num_seconds().max(1) as u64
} else {
60 };
let key = Self::key(jti);
let was_set: bool = redis::cmd("SET")
.arg(&key)
.arg("1")
.arg("NX")
.arg("EX")
.arg(ttl_secs)
.query_async(&mut conn)
.await
.map(|v: Option<String>| v.is_some())
.map_err(|e| AuthError::InternalError(format!("Redis SET error: {}", e)))?;
Ok(!was_set) }
}