Fix rate limiting

This commit is contained in:
2025-11-12 16:31:55 +03:00
Unverified
parent 22d667093d
commit 019414608f
4 changed files with 37 additions and 49 deletions
+11 -30
View File
@@ -4,43 +4,24 @@ from typing import Callable
from fastapi import Request
from slowapi import Limiter
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded
from utils import get_client_ip
# Initialize limiter with IP-based key function
limiter = Limiter(
key_func=lambda request: get_client_ip(request) or get_remote_address(request),
default_limits=["1000/hour"], # Global default limit
storage_uri="memory://", # In-memory storage (can be changed to Redis later)
)
def get_user_id_key(request: Request) -> str:
"""Get rate limit key based on authenticated user ID."""
user = getattr(getattr(request, "state", None), "current_user", None)
if user and hasattr(user, "id"):
return f"user:{user.id}"
# Fallback to IP if not authenticated
return get_client_ip(request) or get_remote_address(request)
def get_ip_key(request: Request) -> str:
"""Get rate limit key based on IP address."""
return get_client_ip(request) or get_remote_address(request)
# Initialize limiter with IP-based key function
# Note: We don't set default_limits to avoid affecting all users if one IP is attacked.
# Each endpoint should have an explicit rate limit based on its sensitivity.
limiter = Limiter(
key_func=get_ip_key,
default_limits=[], # No global default - each endpoint must have explicit limits
storage_uri="memory://", # In-memory storage (can be changed to Redis later)
)
# Rate limit decorators for different endpoint types
# Rate limit decorator for IP-based limiting
def rate_limit_per_ip(limit: str) -> Callable:
"""Rate limit based on IP address."""
return limiter.limit(limit, key_func=get_ip_key)
def rate_limit_per_user(limit: str) -> Callable:
"""Rate limit based on authenticated user ID, fallback to IP.
Note: The user must be authenticated (get_current_user dependency must run first).
The user will be available in request.state.current_user after authentication.
"""
return limiter.limit(limit, key_func=get_user_id_key)
return limiter.limit(limit, key_func=get_ip_key)