Auth specific rate limiting (#3463)

* k

* v1

* fully functional

* finalize

* nit

* nit

* nit

* clean up with wrapper + comments

* k

* update

* minor clean
This commit is contained in:
pablonyx
2024-12-29 18:34:23 -05:00
committed by GitHub
parent d14ef431a7
commit 27acd3387a
8 changed files with 149 additions and 24 deletions

View File

@@ -0,0 +1,47 @@
from collections.abc import Callable
from typing import List
from fastapi import Depends
from fastapi import Request
from fastapi_limiter import FastAPILimiter
from fastapi_limiter.depends import RateLimiter
from onyx.configs.app_configs import RATE_LIMIT_MAX_REQUESTS
from onyx.configs.app_configs import RATE_LIMIT_WINDOW_SECONDS
from onyx.redis.redis_pool import get_async_redis_connection
async def setup_limiter() -> None:
# Use the centralized async Redis connection
redis = await get_async_redis_connection()
await FastAPILimiter.init(redis)
async def close_limiter() -> None:
# This closes the FastAPILimiter connection so we don't leave open connections to Redis.
await FastAPILimiter.close()
async def rate_limit_key(request: Request) -> str:
# Uses both IP and User-Agent to make collisions less likely if IP is behind NAT.
# If request.client is None, a fallback is used to avoid completely unknown keys.
# This helps ensure we have a unique key for each 'user' in simple scenarios.
ip_part = request.client.host if request.client else "unknown"
ua_part = request.headers.get("user-agent", "none").replace(" ", "_")
return f"{ip_part}-{ua_part}"
def get_auth_rate_limiters() -> List[Callable]:
if not (RATE_LIMIT_MAX_REQUESTS and RATE_LIMIT_WINDOW_SECONDS):
return []
return [
Depends(
RateLimiter(
times=RATE_LIMIT_MAX_REQUESTS,
seconds=RATE_LIMIT_WINDOW_SECONDS,
# Use the custom key function to distinguish users
identifier=rate_limit_key,
)
)
]