first commit
Some checks failed
CI / backend (push) Has been cancelled
CI / frontend (push) Has been cancelled

This commit is contained in:
2026-07-23 02:38:05 +03:00
commit 8757bec8ac
335 changed files with 61527 additions and 0 deletions

View File

@@ -0,0 +1,36 @@
"""Rate limit на основе Redis `INCR`+`EXPIRE` для публичных (без auth) эндпоинтов.
Используется резолвом конференций и гостевым входом (`api/conferences.py`) —
эндпоинтами без аутентификации, уязвимыми к перебору номера/ссылки конференции
(см. ADR-001, п.4 — оценка энтропии и рекомендуемый лимит 10 запросов/мин на IP).
"""
from fastapi import HTTPException, status
from core.redis import redis_client
RATE_LIMIT_MAX_REQUESTS = 10
RATE_LIMIT_WINDOW_SECONDS = 60
async def enforce_rate_limit(
key: str,
*,
max_requests: int = RATE_LIMIT_MAX_REQUESTS,
window_seconds: int = RATE_LIMIT_WINDOW_SECONDS,
) -> None:
"""Увеличить счётчик запросов по ключу; бросить 429, если лимит превышен.
`INCR` атомарно создаёт ключ со значением 1, если его ещё не было; TTL
выставляется только при первом инкременте в окне (когда счётчик стал
равен 1) — иначе окно продлевалось бы при каждом запросе и лимит
никогда бы не истекал.
"""
redis_key = f"rate_limit:{key}"
current = await redis_client.incr(redis_key)
if current == 1:
await redis_client.expire(redis_key, window_seconds)
if current > max_requests:
raise HTTPException(
status_code=status.HTTP_429_TOO_MANY_REQUESTS, detail="rate_limit_exceeded"
)