Rthur2003 commited on
Commit
3580561
·
1 Parent(s): dfd803e

fix: implement rate limiting for analysis endpoints to prevent abuse

Browse files
Files changed (1) hide show
  1. app/routes/analyze.py +31 -2
app/routes/analyze.py CHANGED
@@ -8,8 +8,9 @@ from __future__ import annotations
8
 
9
  import time
10
  import uuid
 
11
  from typing import Optional, List, Dict, Any, Literal
12
- from fastapi import APIRouter, File, UploadFile, Form
13
  from pydantic import BaseModel, Field
14
 
15
  from ..services.youtube_analysis import YouTubeAnalysisService
@@ -18,6 +19,34 @@ from ..services.logging_config import get_logger
18
 
19
  router = APIRouter()
20
  logger = get_logger(__name__)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  youtube_service = YouTubeAnalysisService()
22
  preview_model = PreviewModel()
23
 
@@ -66,7 +95,7 @@ class AnalyzeResponse(BaseModel):
66
  errors: List[str] = Field(default_factory=list)
67
 
68
 
69
- @router.post("/api/analyze", response_model=AnalyzeResponse)
70
  async def analyze(
71
  sourceType: str = Form(...),
72
  url: Optional[str] = Form(None),
 
8
 
9
  import time
10
  import uuid
11
+ from collections import defaultdict
12
  from typing import Optional, List, Dict, Any, Literal
13
+ from fastapi import APIRouter, File, UploadFile, Form, Request, Depends, HTTPException, status
14
  from pydantic import BaseModel, Field
15
 
16
  from ..services.youtube_analysis import YouTubeAnalysisService
 
19
 
20
  router = APIRouter()
21
  logger = get_logger(__name__)
22
+
23
+ # Rate limiter for public heavy endpoints
24
+ _analyze_rate_store: dict[str, list[float]] = defaultdict(list)
25
+ _ANALYZE_RATE_WINDOW = 60 # seconds
26
+ _ANALYZE_RATE_MAX = 20 # requests per minute per IP
27
+ _ANALYZE_MAX_IPS = 10_000
28
+
29
+
30
+ async def _analyze_rate_limit(request: Request) -> None:
31
+ """Rate limit for analysis endpoints."""
32
+ client_ip = request.client.host if request.client else "unknown"
33
+ now = time.time()
34
+ cutoff = now - _ANALYZE_RATE_WINDOW
35
+
36
+ # Evict stale IPs when map grows large
37
+ if len(_analyze_rate_store) > _ANALYZE_MAX_IPS:
38
+ stale = [ip for ip, ts in _analyze_rate_store.items() if all(t <= cutoff for t in ts)]
39
+ for ip in stale:
40
+ del _analyze_rate_store[ip]
41
+
42
+ hits = [t for t in _analyze_rate_store[client_ip] if t > cutoff]
43
+ if len(hits) >= _ANALYZE_RATE_MAX:
44
+ raise HTTPException(
45
+ status_code=status.HTTP_429_TOO_MANY_REQUESTS,
46
+ detail={"code": "rate_limit_exceeded", "message": "Too many analysis requests. Please wait before trying again."}
47
+ )
48
+ hits.append(now)
49
+ _analyze_rate_store[client_ip] = hits
50
  youtube_service = YouTubeAnalysisService()
51
  preview_model = PreviewModel()
52
 
 
95
  errors: List[str] = Field(default_factory=list)
96
 
97
 
98
+ @router.post("/api/analyze", response_model=AnalyzeResponse, dependencies=[Depends(_analyze_rate_limit)])
99
  async def analyze(
100
  sourceType: str = Form(...),
101
  url: Optional[str] = Form(None),