from __future__ import annotations from collections import deque from threading import Lock from time import monotonic from fastapi import HTTPException _BUCKETS: dict[str, deque[float]] = {} _LOCK = Lock() async def enforce_rate_limit( *, scope: str, identifier: str, limit: int, window_seconds: int, ) -> None: _enforce_rate_limit_in_memory( key=f"auth:rate_limit:{scope}:{identifier.lower()}", limit=limit, window_seconds=window_seconds, ) def _enforce_rate_limit_in_memory( *, key: str, limit: int, window_seconds: int, ) -> None: now = monotonic() with _LOCK: bucket = _BUCKETS.setdefault(key, deque()) cutoff = now - float(window_seconds) while bucket and bucket[0] <= cutoff: bucket.popleft() if len(bucket) >= limit: raise HTTPException(status_code=429, detail="Too many requests") bucket.append(now) def reset_rate_limit_state() -> None: with _LOCK: _BUCKETS.clear()