"""FastAPI dependency wrappers for the rate limiters. Lets a route declare: @router.post("/login", dependencies=[Depends(limit_login)]) async def login(...): ... … and have the per-IP cap enforced before the handler runs. 429 on overflow with a friendly message; the limiter itself logs nothing — add tracing if you ever care about who's hitting the cap. """ from __future__ import annotations from fastapi import HTTPException, Request from ..services.ratelimit import ( login_limiter, register_limiter, write_limiter, RateLimiter, ) def _client_ip(request: Request) -> str: """Best-effort source IP. Honours one X-Forwarded-For hop so the limiter behaves correctly behind Render / HuggingFace's proxies. Falls back to the direct peer when no header is present.""" fwd = request.headers.get("x-forwarded-for") if fwd: return fwd.split(",")[0].strip() return request.client.host if request.client else "unknown" def _make_dep(limiter: RateLimiter, scope: str): """Build a dependency that rate-limits per IP. `scope` is included in the bucket key so the same IP can have separate buckets per endpoint family (login vs register vs writes).""" async def dep(request: Request): if not limiter.allow(f"{scope}:{_client_ip(request)}"): raise HTTPException( 429, f"Too many requests — please slow down for a moment ({scope})", ) return dep limit_login = _make_dep(login_limiter, "login") limit_register = _make_dep(register_limiter, "register") limit_write = _make_dep(write_limiter, "write")