Spaces:
Sleeping
Sleeping
| """Rate limiting via slowapi (global default limit per client IP). | |
| Applied as middleware so all HTTP routes are covered without per-endpoint | |
| decorators. Degrades gracefully: if slowapi isn't installed or is disabled via | |
| env, the app still runs unthrottled (with a warning). | |
| Tuning (env): | |
| RATE_LIMIT_ENABLED — "true"/"false" (default true) | |
| RATE_LIMIT_DEFAULT — e.g. "120/minute" (default) | |
| RATE_LIMIT_STORAGE_URI — e.g. "redis://redis:6379" for multi-worker; the | |
| default "memory://" is per-process only. | |
| """ | |
| from __future__ import annotations | |
| import logging | |
| import os | |
| from fastapi import FastAPI | |
| log = logging.getLogger("bodhi.ratelimit") | |
| def setup_rate_limiting(app: FastAPI) -> None: | |
| enabled = os.getenv("RATE_LIMIT_ENABLED", "true").strip().lower() in ( | |
| "1", | |
| "true", | |
| "yes", | |
| ) | |
| if not enabled: | |
| log.warning("Rate limiting disabled via RATE_LIMIT_ENABLED.") | |
| return | |
| try: | |
| from slowapi import Limiter | |
| from slowapi.errors import RateLimitExceeded | |
| from slowapi.middleware import SlowAPIMiddleware | |
| from slowapi.util import get_remote_address | |
| from starlette.responses import JSONResponse | |
| except ImportError: | |
| log.warning("slowapi not installed; rate limiting disabled.") | |
| return | |
| default_limit = os.getenv("RATE_LIMIT_DEFAULT", "120/minute") | |
| storage_uri = os.getenv("RATE_LIMIT_STORAGE_URI", "memory://") | |
| limiter = Limiter( | |
| key_func=get_remote_address, | |
| default_limits=[default_limit], | |
| storage_uri=storage_uri, | |
| headers_enabled=True, | |
| ) | |
| app.state.limiter = limiter | |
| async def _handler(request, exc): # noqa: ANN001 | |
| return JSONResponse( | |
| status_code=429, | |
| content={"detail": "Rate limit exceeded. Please slow down."}, | |
| ) | |
| app.add_exception_handler(RateLimitExceeded, _handler) | |
| app.add_middleware(SlowAPIMiddleware) | |
| log.info( | |
| "Rate limiting enabled (default=%s, storage=%s).", default_limit, storage_uri | |
| ) | |