""" Rate Limiting Middleware ======================== Redis-backed rate limiting for API endpoints. Supports: - Per-endpoint rate limits - Tier-based limits (FREE, BASIC, PREMIUM, ENTERPRISE) - Burst protection - Automatic key expiration """ import logging import redis from fastapi import Request logger = logging.getLogger(__name__) # ─── RATE LIMIT CONFIGURATION ──────────────────────────────────── RATE_LIMITS = { "FREE": {"rph": 10, "rpd": 100, "burst": 2}, # requests per hour, per day, burst "BASIC": {"rph": 50, "rpd": 500, "burst": 5}, "PREMIUM": {"rph": 200, "rpd": 2000, "burst": 10}, "ENTERPRISE": {"rph": 1000, "rpd": 10000, "burst": 50}, } # Default limits if tier not found DEFAULT_LIMITS = {"rph": 10, "rpd": 100, "burst": 2} class RateLimiter: """Rate limiter with Redis backend.""" def __init__(self, redis_client: redis.Redis | None = None): self.redis = redis_client self.enabled = redis_client is not None def check(self, key: str, limit: int, window: int = 3600) -> tuple[bool, int]: """ Check if request is within rate limit. Args: key: Unique identifier for rate limit bucket limit: Maximum requests allowed window: Time window in seconds (default: 1 hour) Returns: Tuple of (allowed, remaining_count) """ if not self.enabled: return True, limit # No Redis = unlimited try: # Use Redis pipeline for atomic operations pipe = self.redis.pipeline() pipe.incr(key) pipe.expire(key, window) results = pipe.execute() current_count = results[0] remaining = max(0, limit - current_count) if current_count > limit: logger.warning(f"Rate limit exceeded for key: {key}") return False, 0 return True, remaining except redis.RedisError as e: logger.error(f"Redis error in rate limiter: {e}") return True, limit # Fail open if Redis down def get_tier_limits(self, tier: str) -> dict[str, int]: """Get rate limit configuration for a tier.""" return RATE_LIMITS.get(tier.upper(), DEFAULT_LIMITS) def check_tier(self, request: Request, tier: str, endpoint: str = "default") -> bool: """ Check rate limit for a specific tier and endpoint. Args: request: FastAPI request object tier: User's authentication tier endpoint: API endpoint name Returns: True if allowed, False if rate limited """ if not self.enabled: return True # Unl...[truncated]