//! Database queries for the `admin_rate_limits` table. //! //! Provides read and upsert operations for per-provider rate limit configuration. use sqlx::PgPool; use uuid::Uuid; use crate::errors::AppError; use crate::models::rate_limit::AdminRateLimit; /// Row type returned by sqlx queries against the `admin_rate_limits` table. #[derive(Debug, sqlx::FromRow)] struct RateLimitRow { id: Uuid, provider_name: String, max_requests: i32, time_window_seconds: i32, updated_at: chrono::DateTime, } impl From for AdminRateLimit { fn from(row: RateLimitRow) -> Self { Self { id: row.id, provider_name: row.provider_name, max_requests: row.max_requests, time_window_seconds: row.time_window_seconds, updated_at: row.updated_at, } } } /// List all rate limit configurations. pub async fn list_all(pool: &PgPool) -> Result, AppError> { let rows = sqlx::query_as::<_, RateLimitRow>( r#" SELECT id, provider_name, max_requests, time_window_seconds, updated_at FROM admin_rate_limits ORDER BY provider_name "#, ) .fetch_all(pool) .await?; Ok(rows.into_iter().map(AdminRateLimit::from).collect()) } /// Get the rate limit configuration for a specific provider. pub async fn get_by_provider( pool: &PgPool, provider_name: &str, ) -> Result, AppError> { let row = sqlx::query_as::<_, RateLimitRow>( r#" SELECT id, provider_name, max_requests, time_window_seconds, updated_at FROM admin_rate_limits WHERE provider_name = $1 "#, ) .bind(provider_name) .fetch_optional(pool) .await?; Ok(row.map(AdminRateLimit::from)) } /// Upsert a rate limit configuration for a provider. /// /// Creates the rate limit if it doesn't exist, or updates it if it does. /// Returns the upserted rate limit. pub async fn upsert( pool: &PgPool, provider_name: &str, max_requests: i32, time_window_seconds: i32, ) -> Result { let row = sqlx::query_as::<_, RateLimitRow>( r#" INSERT INTO admin_rate_limits (provider_name, max_requests, time_window_seconds) VALUES ($1, $2, $3) ON CONFLICT (provider_name) DO UPDATE SET max_requests = EXCLUDED.max_requests, time_window_seconds = EXCLUDED.time_window_seconds, updated_at = now() RETURNING id, provider_name, max_requests, time_window_seconds, updated_at "#, ) .bind(provider_name) .bind(max_requests) .bind(time_window_seconds) .fetch_one(pool) .await?; Ok(AdminRateLimit::from(row)) }