use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, Default)]
#[serde(default)]
pub struct RateLimitingSecurityConfig {
pub enabled: bool,
pub requests_per_second: u32,
pub burst_size: u32,
pub auth_start_max_requests: u32,
pub auth_start_window_secs: u64,
pub auth_callback_max_requests: u32,
pub auth_callback_window_secs: u64,
pub auth_refresh_max_requests: u32,
pub auth_refresh_window_secs: u64,
#[serde(default)]
pub requests_per_second_per_user: Option<u32>,
pub redis_url: Option<String>,
#[serde(default)]
pub trust_proxy_headers: bool,
#[serde(default)]
pub trusted_proxy_cidrs: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RateLimitConfig {
pub enabled: bool,
pub rps_per_ip: u32,
pub rps_per_user: u32,
pub burst_size: u32,
pub cleanup_interval_secs: u64,
pub trust_proxy_headers: bool,
pub trusted_proxy_cidrs: Vec<ipnet::IpNet>,
}
impl Default for RateLimitConfig {
fn default() -> Self {
Self {
enabled: true,
rps_per_ip: 100, rps_per_user: 1000, burst_size: 500, cleanup_interval_secs: 300, trust_proxy_headers: false,
trusted_proxy_cidrs: Vec::new(),
}
}
}
impl RateLimitConfig {
pub fn from_security_config(sec: &RateLimitingSecurityConfig) -> Self {
let trusted_proxy_cidrs = sec
.trusted_proxy_cidrs
.as_deref()
.unwrap_or(&[])
.iter()
.filter_map(|s| {
s.parse::<ipnet::IpNet>()
.map_err(|e| {
tracing::warn!(cidr = %s, error = %e, "Invalid trusted_proxy_cidr — skipping");
})
.ok()
})
.collect();
Self {
enabled: sec.enabled,
rps_per_ip: sec.requests_per_second,
rps_per_user: sec
.requests_per_second_per_user
.unwrap_or_else(|| sec.requests_per_second.saturating_mul(10)),
burst_size: sec.burst_size,
cleanup_interval_secs: 300,
trust_proxy_headers: sec.trust_proxy_headers,
trusted_proxy_cidrs,
}
}
}
#[derive(Debug, Clone)]
pub struct CheckResult {
pub allowed: bool,
pub remaining: f64,
pub retry_after_secs: u32,
}
impl CheckResult {
pub(super) const fn allow(remaining: f64) -> Self {
Self {
allowed: true,
remaining,
retry_after_secs: 0,
}
}
pub(super) const fn deny(retry_after_secs: u32) -> Self {
Self {
allowed: false,
remaining: 0.0,
retry_after_secs,
}
}
}