Spaces:
Building
Building
Update utils.py
Browse files
utils.py
CHANGED
@@ -2,6 +2,49 @@ import os
|
|
2 |
from datetime import datetime, timedelta
|
3 |
import jwt
|
4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
# ===================== JWT Config =====================
|
6 |
def get_jwt_config():
|
7 |
"""Get JWT configuration based on environment"""
|
@@ -49,4 +92,30 @@ def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security))
|
|
49 |
except jwt.ExpiredSignatureError:
|
50 |
raise HTTPException(status_code=401, detail="Token expired")
|
51 |
except jwt.InvalidTokenError:
|
52 |
-
raise HTTPException(status_code=401, detail="Invalid token")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
from datetime import datetime, timedelta
|
3 |
import jwt
|
4 |
|
5 |
+
# ===================== Rate Limiting =====================
|
6 |
+
|
7 |
+
class RateLimiter:
|
8 |
+
"""Simple in-memory rate limiter"""
|
9 |
+
def __init__(self):
|
10 |
+
self.requests = {} # {key: [(timestamp, count)]}
|
11 |
+
self.lock = threading.Lock()
|
12 |
+
|
13 |
+
def is_allowed(self, key: str, max_requests: int, window_seconds: int) -> bool:
|
14 |
+
"""Check if request is allowed"""
|
15 |
+
with self.lock:
|
16 |
+
now = datetime.now(timezone.utc)
|
17 |
+
|
18 |
+
if key not in self.requests:
|
19 |
+
self.requests[key] = []
|
20 |
+
|
21 |
+
# Remove old entries
|
22 |
+
cutoff = now.timestamp() - window_seconds
|
23 |
+
self.requests[key] = [
|
24 |
+
(ts, count) for ts, count in self.requests[key]
|
25 |
+
if ts > cutoff
|
26 |
+
]
|
27 |
+
|
28 |
+
# Count requests in window
|
29 |
+
total = sum(count for _, count in self.requests[key])
|
30 |
+
|
31 |
+
if total >= max_requests:
|
32 |
+
return False
|
33 |
+
|
34 |
+
# Add this request
|
35 |
+
self.requests[key].append((now.timestamp(), 1))
|
36 |
+
return True
|
37 |
+
|
38 |
+
def reset(self, key: str):
|
39 |
+
"""Reset rate limit for key"""
|
40 |
+
with self.lock:
|
41 |
+
if key in self.requests:
|
42 |
+
del self.requests[key]
|
43 |
+
|
44 |
+
# Create global rate limiter instance
|
45 |
+
import threading
|
46 |
+
rate_limiter = RateLimiter()
|
47 |
+
|
48 |
# ===================== JWT Config =====================
|
49 |
def get_jwt_config():
|
50 |
"""Get JWT configuration based on environment"""
|
|
|
92 |
except jwt.ExpiredSignatureError:
|
93 |
raise HTTPException(status_code=401, detail="Token expired")
|
94 |
except jwt.InvalidTokenError:
|
95 |
+
raise HTTPException(status_code=401, detail="Invalid token")
|
96 |
+
|
97 |
+
# ===================== Utility Functions =====================
|
98 |
+
|
99 |
+
def truncate_string(text: str, max_length: int = 100, suffix: str = "...") -> str:
|
100 |
+
"""Truncate string to max length"""
|
101 |
+
if len(text) <= max_length:
|
102 |
+
return text
|
103 |
+
return text[:max_length - len(suffix)] + suffix
|
104 |
+
|
105 |
+
def format_file_size(size_bytes: int) -> str:
|
106 |
+
"""Format file size in human readable format"""
|
107 |
+
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
|
108 |
+
if size_bytes < 1024.0:
|
109 |
+
return f"{size_bytes:.2f} {unit}"
|
110 |
+
size_bytes /= 1024.0
|
111 |
+
return f"{size_bytes:.2f} PB"
|
112 |
+
|
113 |
+
def is_safe_path(path: str, base_path: str) -> bool:
|
114 |
+
"""Check if path is safe (no directory traversal)"""
|
115 |
+
import os
|
116 |
+
# Resolve to absolute paths
|
117 |
+
base = os.path.abspath(base_path)
|
118 |
+
target = os.path.abspath(os.path.join(base, path))
|
119 |
+
|
120 |
+
# Check if target is under base
|
121 |
+
return target.startswith(base)
|