946f16a633
Introduce a full Vite/React UI for exams, auth, materials, images, generation, and export. Adapt backend for Sinbad2IA chat API, bcrypt passwords, CORS on port 5173, and schema migrations.
54 lines
2.0 KiB
Python
54 lines
2.0 KiB
Python
import time
|
|
from collections import defaultdict, deque
|
|
|
|
from fastapi import Request, Response
|
|
from fastapi.responses import ORJSONResponse
|
|
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
|
|
|
|
from app.core.config import Settings
|
|
from app.core.errors import error_payload
|
|
|
|
|
|
class RateLimitMiddleware(BaseHTTPMiddleware):
|
|
def __init__(self, app: object, settings: Settings) -> None:
|
|
super().__init__(app)
|
|
self.limit = settings.rate_limit_requests
|
|
self.window_seconds = settings.rate_limit_window_seconds
|
|
self.requests: defaultdict[str, deque[float]] = defaultdict(deque)
|
|
|
|
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
|
|
if request.method == "OPTIONS":
|
|
return await call_next(request)
|
|
|
|
client = request.client.host if request.client else "unknown"
|
|
now = time.monotonic()
|
|
bucket = self.requests[client]
|
|
|
|
while bucket and now - bucket[0] > self.window_seconds:
|
|
bucket.popleft()
|
|
|
|
if len(bucket) >= self.limit:
|
|
return ORJSONResponse(
|
|
status_code=429,
|
|
content=error_payload("rate_limited", "Too many requests"),
|
|
headers={"Retry-After": str(self.window_seconds)},
|
|
)
|
|
|
|
bucket.append(now)
|
|
return await call_next(request)
|
|
|
|
|
|
class RequestSizeLimitMiddleware(BaseHTTPMiddleware):
|
|
def __init__(self, app: object, settings: Settings) -> None:
|
|
super().__init__(app)
|
|
self.max_request_bytes = settings.max_request_bytes
|
|
|
|
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
|
|
content_length = request.headers.get("content-length")
|
|
if content_length and int(content_length) > self.max_request_bytes:
|
|
return ORJSONResponse(
|
|
status_code=413,
|
|
content=error_payload("payload_too_large", "Request body is too large"),
|
|
)
|
|
return await call_next(request)
|