61 lines
2.3 KiB
Python
61 lines
2.3 KiB
Python
|
|
import time
|
||
|
|
|
||
|
|
# مانیتورینگ خودکار همهی فراخوانیهای API — بدون نیازِ به هیچ تغییری توی
|
||
|
|
# اپ فلاتر، چون توی همان سطحِ میدلور جنگو کار میکند. کاربر از JWT توی
|
||
|
|
# هدرِ Authorization (اگر باشد) استخراج میشود — در این لحظه هنوز
|
||
|
|
# AuthenticationMiddleware کاربر واقعی را روی request نگذاشته (آن فقط برای
|
||
|
|
# auth مبتنیبر session است، نه JWT)، پس خودمان با JWTAuthentication همان
|
||
|
|
# توکن را میخوانیم.
|
||
|
|
|
||
|
|
|
||
|
|
class ApiCallLoggingMiddleware:
|
||
|
|
def __init__(self, get_response):
|
||
|
|
self.get_response = get_response
|
||
|
|
|
||
|
|
def __call__(self, request):
|
||
|
|
start = time.monotonic()
|
||
|
|
response = self.get_response(request)
|
||
|
|
|
||
|
|
if request.path.startswith("/api/"):
|
||
|
|
duration_ms = (time.monotonic() - start) * 1000
|
||
|
|
try:
|
||
|
|
self._log(request, response, duration_ms)
|
||
|
|
except Exception:
|
||
|
|
# لاگگیریِ مانیتورینگ هیچوقت نباید پاسخِ واقعی به کاربر را
|
||
|
|
# خراب کند.
|
||
|
|
pass
|
||
|
|
|
||
|
|
return response
|
||
|
|
|
||
|
|
def _log(self, request, response, duration_ms):
|
||
|
|
from .models import ApiCallLog
|
||
|
|
|
||
|
|
ApiCallLog.objects.create(
|
||
|
|
user=self._resolve_user(request),
|
||
|
|
method=request.method,
|
||
|
|
path=request.path[:255],
|
||
|
|
status_code=response.status_code,
|
||
|
|
duration_ms=duration_ms,
|
||
|
|
ip_address=self._client_ip(request),
|
||
|
|
)
|
||
|
|
|
||
|
|
@staticmethod
|
||
|
|
def _resolve_user(request):
|
||
|
|
auth_header = request.META.get("HTTP_AUTHORIZATION", "")
|
||
|
|
if not auth_header.startswith("Bearer "):
|
||
|
|
return None
|
||
|
|
try:
|
||
|
|
from rest_framework_simplejwt.authentication import JWTAuthentication
|
||
|
|
|
||
|
|
result = JWTAuthentication().authenticate(request)
|
||
|
|
return result[0] if result else None
|
||
|
|
except Exception:
|
||
|
|
return None
|
||
|
|
|
||
|
|
@staticmethod
|
||
|
|
def _client_ip(request):
|
||
|
|
forwarded = request.META.get("HTTP_X_FORWARDED_FOR")
|
||
|
|
if forwarded:
|
||
|
|
return forwarded.split(",")[0].strip()
|
||
|
|
return request.META.get("REMOTE_ADDR") or None
|