itssKarthiii's picture
Upload 70 files
6b408d7 verified
"""
Rate limiting middleware using SlowAPI.
Provides request rate limiting per API key.
"""
from slowapi import Limiter
from slowapi.util import get_remote_address
from starlette.requests import Request
from app.config import get_settings
from app.utils.logger import get_logger
logger = get_logger(__name__)
def get_api_key_or_ip(request: Request) -> str:
"""
Extract rate limit key from request.
Uses API key if present, otherwise falls back to IP address.
Args:
request: Incoming request
Returns:
Rate limit key (API key or IP)
"""
api_key = request.headers.get("x-api-key")
if api_key:
# Use API key for per-key rate limiting
return f"key:{api_key}"
# Fall back to IP address
return f"ip:{get_remote_address(request)}"
def get_limiter() -> Limiter:
"""
Create and configure rate limiter.
Returns:
Configured Limiter instance
"""
settings = get_settings()
# Build default limit string
default_limit = f"{settings.RATE_LIMIT_REQUESTS}/minute"
return Limiter(
key_func=get_api_key_or_ip,
default_limits=[default_limit],
# Note: Redis storage will be configured in main.py if available
)
# Global limiter instance
limiter = get_limiter()
def rate_limit_exceeded_handler(request: Request, exc: Exception):
"""
Handle rate limit exceeded errors.
Args:
request: Request that exceeded the limit
exc: Rate limit exception
Returns:
JSON response with 429 status
"""
from starlette.responses import JSONResponse
logger.warning(
"Rate limit exceeded",
path=request.url.path,
client=get_api_key_or_ip(request),
)
return JSONResponse(
status_code=429,
content={
"status": "error",
"message": "Rate limit exceeded. Please try again later.",
},
headers={
"Retry-After": "60",
},
)