nxtgauge-backend-rust/crates/cache/src/rate_limit.rs
2026-03-22 15:55:29 +01:00

52 lines
1.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Generic sliding-window rate limiter.
//!
//! Key pattern: `rate:{namespace}:{identifier}`
//! Returns `Ok(true)` if the request is allowed, `Ok(false)` if rate-limited.
use redis::AsyncCommands;
use crate::RedisPool;
/// Check + increment a rate-limit counter.
///
/// * `namespace` e.g. `"login"`, `"register"`, `"lead"`
/// * `identifier` e.g. email, IP, user_id
/// * `max` maximum requests allowed in `window_secs`
/// * `window_secs` sliding window length in seconds
///
/// Returns `Ok(true)` = allowed, `Ok(false)` = blocked.
pub async fn check(
redis: &mut RedisPool,
namespace: &str,
identifier: &str,
max: i64,
window_secs: i64,
) -> Result<bool, redis::RedisError> {
let key = format!("rate:{namespace}:{identifier}");
let count: i64 = redis.incr(&key, 1i64).await?;
if count == 1 {
redis.expire::<_, ()>(&key, window_secs).await?;
}
Ok(count <= max)
}
/// Convenience wrappers ───────────────────────────────────────────────────────
/// Register: max 10 per hour per email
pub async fn check_register(redis: &mut RedisPool, email: &str) -> Result<bool, redis::RedisError> {
check(redis, "register", email, 10, 3_600).await
}
/// Login: max 10 attempts per 15 min per email
pub async fn check_login(redis: &mut RedisPool, email: &str) -> Result<bool, redis::RedisError> {
check(redis, "login", email, 10, 900).await
}
/// Lead request: max 5 per hour per professional
pub async fn check_lead(redis: &mut RedisPool, professional_id: &str) -> Result<bool, redis::RedisError> {
check(redis, "lead", professional_id, 5, 3_600).await
}
/// Job post: max 20 per hour per company
pub async fn check_job_post(redis: &mut RedisPool, company_id: &str) -> Result<bool, redis::RedisError> {
check(redis, "job_post", company_id, 20, 3_600).await
}