"""Sliding-window rate limiter. Redis if configured, otherwise in-process memory.""" from __future__ import annotations import time from collections import defaultdict, deque from fastapi import HTTPException, status class MemoryRateLimiter: def __init__(self) -> None: self._hits: dict[str, deque[float]] = defaultdict(deque) def check(self, key: str, limit: int, window_s: int, message_fr: str) -> None: now = time.time() q = self._hits[key] while q and q[0] < now - window_s: q.popleft() if len(q) >= limit: raise HTTPException(status.HTTP_429_TOO_MANY_REQUESTS, detail=message_fr) q.append(now) def count(self, key: str, window_s: int) -> int: now = time.time() q = self._hits[key] while q and q[0] < now - window_s: q.popleft() return len(q) limiter = MemoryRateLimiter() MSG_MESSAGES = "Tu as atteint la limite de messages pour l'instant. Réessaie un peu plus tard." MSG_SANDBOX = "Limite d'exécutions Python atteinte pour cette heure." MSG_WEB = "Limite de recherches web atteinte pour aujourd'hui." MSG_UPLOAD = "Limite de téléversements atteinte pour aujourd'hui."