first commit
This commit is contained in:
commit
34ea15a54d
246 changed files with 18660 additions and 0 deletions
37
backend/app/core/rate_limit.py
Normal file
37
backend/app/core/rate_limit.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import threading
|
||||
import time
|
||||
from collections import defaultdict
|
||||
|
||||
from fastapi import Request
|
||||
|
||||
from app.core.errors import AppError
|
||||
|
||||
_buckets: dict[str, list[float]] = defaultdict(list)
|
||||
_lock = threading.Lock()
|
||||
|
||||
|
||||
def _client_ip(request: Request) -> str:
|
||||
forwarded = request.headers.get("x-forwarded-for")
|
||||
if forwarded:
|
||||
return forwarded.split(",")[0].strip()
|
||||
if request.client and request.client.host:
|
||||
return request.client.host
|
||||
return "unknown"
|
||||
|
||||
|
||||
def enforce_rate_limit(request: Request, *, scope: str, limit: int, window_seconds: int) -> None:
|
||||
"""In-memory sliding window limiter (per process). Use edge/WAF limits in production at scale."""
|
||||
import os
|
||||
|
||||
if os.environ.get("DESI_CLANS_DISABLE_RATE_LIMIT", "").lower() in {"1", "true", "yes"}:
|
||||
return
|
||||
if limit <= 0:
|
||||
return
|
||||
key = f"{scope}:{_client_ip(request)}"
|
||||
now = time.time()
|
||||
with _lock:
|
||||
window = _buckets[key]
|
||||
window[:] = [stamp for stamp in window if now - stamp < window_seconds]
|
||||
if len(window) >= limit:
|
||||
raise AppError("Too many requests. Try again shortly.", status_code=429)
|
||||
window.append(now)
|
||||
Loading…
Add table
Add a link
Reference in a new issue