"""Shared rate limiter (slowapi / limits, in-memory by default). Applied per-route via `@limiter.limit(...)`. Auth endpoints get the tightest limits since they're the classic credential-stuffing target. """ from __future__ import annotations from fastapi import Request from slowapi import Limiter from app.core.config import get_settings from app.core.security import get_client_ip def _client_ip_key(request: Request) -> str: """Same IP resolution as everything else in the app (is_localhost, the ban/throttle engine) - slowapi's own get_remote_address reads request.client.host directly, which would be Nginx/Cloudflare's own address for every visitor once deployed behind a reverse proxy, collapsing all rate limits into one shared bucket. See app.core.security.get_client_ip / Settings.trusted_proxy_ip_header.""" return get_client_ip(request, get_settings()) # Disabled under APP_ENV=test so the many auth calls a test suite makes don't # trip real limits (real limiter behavior is covered by its own test). limiter = Limiter(key_func=_client_ip_key, enabled=get_settings().app_env != "test")