Files
the-other-dude/backend/app/middleware/rate_limit.py
Jason Staack b840047e19 feat: The Other Dude v9.0.1 — full-featured email system
ci: add GitHub Pages deployment workflow for docs site

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 19:30:44 -05:00

49 lines
1.5 KiB
Python

"""Rate limiting middleware using slowapi with Redis backend.
Per-route rate limits only -- no global limits to avoid blocking the
Go poller, NATS subscribers, and health check endpoints.
Rate limit data uses Redis DB 1 (separate from app data in DB 0).
"""
from fastapi import FastAPI
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.errors import RateLimitExceeded
from slowapi.util import get_remote_address
from app.config import settings
def _get_redis_url() -> str:
"""Return Redis URL pointing to DB 1 for rate limit storage.
Keeps rate limit counters separate from application data in DB 0.
"""
url = settings.REDIS_URL
if url.endswith("/0"):
return url[:-2] + "/1"
# If no DB specified or different DB, append /1
if url.rstrip("/").split("/")[-1].isdigit():
# Replace existing DB number
parts = url.rsplit("/", 1)
return parts[0] + "/1"
return url.rstrip("/") + "/1"
limiter = Limiter(
key_func=get_remote_address,
storage_uri=_get_redis_url(),
default_limits=[], # No global limits -- per-route only
)
def setup_rate_limiting(app: FastAPI) -> None:
"""Register the rate limiter on the FastAPI app.
This sets app.state.limiter (required by slowapi) and registers
the 429 exception handler. It does NOT add middleware -- the
@limiter.limit() decorators handle actual limiting per-route.
"""
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)