main
use std::time::Duration;
use crate::cache::Cache;
use crate::errors::{AuthError, AuthResult};
#[tracing::instrument(skip_all, fields(request_id = %envelope::context::resolve_request_id(_request_id)))]
pub async fn rate_limit_once(
cache: &Cache,
key: &str,
window_secs: u64,
_request_id: Option<&str>,
) -> AuthResult<()> {
let window = window_secs.max(1);
let is_new = cache.set_nx_ex(key, 1, Duration::from_secs(window)).await?;
if !is_new {
return Err(AuthError::RateLimited {
retry_after_secs: window,
});
}
Ok(())
}
#[tracing::instrument(skip_all, fields(request_id = %envelope::context::resolve_request_id(_request_id)))]
pub async fn attempts_track(
cache: &Cache,
key: &str,
max: u64,
window_secs: u64,
_request_id: Option<&str>,
) -> AuthResult<bool> {
let count = cache
.scripts()
.incr_ex(key, Duration::from_secs(window_secs.max(1)))
.await?;
Ok(count <= max as i64)
}
#[tracing::instrument(skip_all, fields(request_id = %envelope::context::resolve_request_id(_request_id)))]
pub async fn attempts_load(cache: &Cache, key: &str, _request_id: Option<&str>) -> AuthResult<u64> {
let count: Option<i64> = cache.get(key).await?;
Ok(count.unwrap_or_default().max(0) as u64)
}
#[tracing::instrument(skip_all, fields(request_id = %envelope::context::resolve_request_id(_request_id)))]
pub async fn attempts_reset(cache: &Cache, key: &str, _request_id: Option<&str>) -> AuthResult<()> {
cache.delete(key).await?;
Ok(())
}