File size: 834 Bytes
5ea3240
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from __future__ import annotations

import threading
import time
from collections import defaultdict, deque

from .config import get_settings


class RateLimitExceeded(ValueError):
    pass


class SlidingWindowLimiter:
    def __init__(self):
        self.limit = get_settings().queries_per_hour_per_ip
        self.events: dict[str, deque[float]] = defaultdict(deque)
        self.lock = threading.Lock()

    def check(self, key: str) -> None:
        now = time.time()
        cutoff = now - 3600
        with self.lock:
            q = self.events[key]
            while q and q[0] < cutoff:
                q.popleft()
            if len(q) >= self.limit:
                raise RateLimitExceeded(f"Rate limit reached ({self.limit} operations/hour for this client).")
            q.append(now)


limiter = SlidingWindowLimiter()