초기 커밋
This commit is contained in:
0
app/middleware/__init__.py
Normal file
0
app/middleware/__init__.py
Normal file
16
app/middleware/cors.py
Normal file
16
app/middleware/cors.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
from app.core.config import settings
|
||||
|
||||
|
||||
def add_cors_middleware(app: FastAPI) -> None:
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=settings.CORS_ORIGINS,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
37
app/middleware/rate_limit.py
Normal file
37
app/middleware/rate_limit.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import JSONResponse, Response
|
||||
|
||||
from app.db.redis import get_redis
|
||||
|
||||
|
||||
class RateLimitMiddleware(BaseHTTPMiddleware):
|
||||
def __init__(self, app, max_requests: int = 100, window_seconds: int = 60): # type: ignore[no-untyped-def]
|
||||
super().__init__(app)
|
||||
self.max_requests = max_requests
|
||||
self.window_seconds = window_seconds
|
||||
|
||||
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
|
||||
if request.url.path.startswith("/docs") or request.url.path.startswith("/redoc"):
|
||||
return await call_next(request)
|
||||
|
||||
client_ip = request.client.host if request.client else "unknown"
|
||||
key = f"rate_limit:{client_ip}"
|
||||
|
||||
try:
|
||||
redis = get_redis()
|
||||
current = await redis.incr(key)
|
||||
if current == 1:
|
||||
await redis.expire(key, self.window_seconds)
|
||||
|
||||
if current > self.max_requests:
|
||||
return JSONResponse(
|
||||
status_code=429,
|
||||
content={"detail": "Too many requests"},
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return await call_next(request)
|
||||
17
app/middleware/request_id.py
Normal file
17
app/middleware/request_id.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
|
||||
import structlog
|
||||
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import Response
|
||||
|
||||
|
||||
class RequestIDMiddleware(BaseHTTPMiddleware):
|
||||
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
|
||||
request_id = request.headers.get("X-Request-ID", str(uuid.uuid4()))
|
||||
structlog.contextvars.bind_contextvars(request_id=request_id)
|
||||
response = await call_next(request)
|
||||
response.headers["X-Request-ID"] = request_id
|
||||
return response
|
||||
26
app/middleware/request_logging.py
Normal file
26
app/middleware/request_logging.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
|
||||
import structlog
|
||||
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import Response
|
||||
|
||||
logger = structlog.get_logger("request")
|
||||
|
||||
|
||||
class RequestLoggingMiddleware(BaseHTTPMiddleware):
|
||||
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
|
||||
start = time.perf_counter()
|
||||
response = await call_next(request)
|
||||
elapsed_ms = round((time.perf_counter() - start) * 1000, 2)
|
||||
|
||||
logger.info(
|
||||
"request",
|
||||
method=request.method,
|
||||
path=request.url.path,
|
||||
status=response.status_code,
|
||||
elapsed_ms=elapsed_ms,
|
||||
)
|
||||
return response
|
||||
Reference in New Issue
Block a user