use std::collections::HashMap; use std::sync::Arc; use std::time::Instant; use irc_now_common::auth::OidcConfig; use openid::DiscoveredClient; use sqlx::PgPool; use tokio::sync::Mutex; use crate::csam::CsamScanner; use crate::storage::Storage; #[derive(Clone)] pub struct AppState { pub db: PgPool, pub oidc: OidcConfig, pub oidc_client: Arc, pub storage: Storage, pub csam_scanner: Option>, pub upload_limiter: Arc>>>, } const FREE_UPLOAD_LIMIT: usize = 10; const PRO_UPLOAD_LIMIT: usize = 30; const RATE_WINDOW_SECS: u64 = 3600; pub fn check_rate_limit( entries: &mut Vec, is_pro: bool, ) -> bool { let now = Instant::now(); let window = std::time::Duration::from_secs(RATE_WINDOW_SECS); entries.retain(|t| now.duration_since(*t) < window); let limit = if is_pro { PRO_UPLOAD_LIMIT } else { FREE_UPLOAD_LIMIT }; if entries.len() >= limit { return false; } entries.push(now); true } #[cfg(test)] mod tests { use super::*; #[test] fn rate_limit_allows_under_limit() { let mut entries = Vec::new(); assert!(check_rate_limit(&mut entries, false)); } #[test] fn rate_limit_blocks_at_limit() { let mut entries: Vec = (0..FREE_UPLOAD_LIMIT).map(|_| Instant::now()).collect(); assert!(!check_rate_limit(&mut entries, false)); } #[test] fn rate_limit_pro_higher_limit() { let mut entries: Vec = (0..FREE_UPLOAD_LIMIT).map(|_| Instant::now()).collect(); assert!(check_rate_limit(&mut entries, true)); } }