Sprint 15: User-Profil (Bio, Wohnort, Erfahrung, Social, Avatar, Mitglied-seit)
This commit is contained in:
parent
3642995409
commit
596c11d207
6 changed files with 189 additions and 1 deletions
37
backend/ratelimit.py
Normal file
37
backend/ratelimit.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
"""
|
||||
BAN YARO — Rate Limiter
|
||||
Sliding-Window-Limiter, in-memory (kein Redis nötig für Single-Container).
|
||||
"""
|
||||
|
||||
import threading
|
||||
from collections import defaultdict, deque
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from fastapi import HTTPException, Request
|
||||
|
||||
_buckets: dict[str, deque] = defaultdict(deque)
|
||||
_lock = threading.Lock()
|
||||
|
||||
|
||||
def check(request: Request, *, max_requests: int, window_seconds: int, key: str = ""):
|
||||
"""
|
||||
Wirft HTTP 429 wenn max_requests im Zeitfenster überschritten.
|
||||
key: optionaler Präfix um verschiedene Limits zu trennen (z.B. 'register', 'login').
|
||||
"""
|
||||
ip = (request.client.host if request.client else "unknown")
|
||||
bucket_key = f"{key}:{ip}"
|
||||
now = datetime.utcnow()
|
||||
cutoff = now - timedelta(seconds=window_seconds)
|
||||
|
||||
with _lock:
|
||||
dq = _buckets[bucket_key]
|
||||
# Alte Einträge raus
|
||||
while dq and dq[0] < cutoff:
|
||||
dq.popleft()
|
||||
if len(dq) >= max_requests:
|
||||
minutes = window_seconds // 60
|
||||
raise HTTPException(
|
||||
429,
|
||||
f"Zu viele Versuche. Bitte warte {minutes} Minute(n) und versuche es erneut."
|
||||
)
|
||||
dq.append(now)
|
||||
Loading…
Add table
Add a link
Reference in a new issue