import time import uuid from django.conf import settings from django.utils.deprecation import MiddlewareMixin from .models import APIInteractionLog class AuditAPIMiddleware(MiddlewareMixin): def process_request(self, request): request.request_id = str(uuid.uuid4()) request._audit_start_time = time.time() def process_response(self, request, response): config = getattr(settings, "INFRASYNTH_AUDIT", {}) if not config.get("ENABLE_API_LOGGING", True): return response path = request.path if path.startswith("/admin/"): return response if hasattr(request, "_audit_start_time"): duration_ms = int((time.time() - request._audit_start_time) * 1000) else: duration_ms = 0 max_body = config.get("MAX_BODY_SIZE_BYTES", 5000) request_body = None response_body = None sensitive_keys = config.get("SENSITIVE_KEYS", []) try: raw_body = getattr(request, "body", b"") if raw_body and len(raw_body) <= max_body: body = raw_body.decode("utf-8", errors="replace") if not any(k in body.lower() for k in sensitive_keys): import json try: request_body = json.loads(body) except (json.JSONDecodeError, ValueError): request_body = {"_truncated": True} except Exception: pass try: if hasattr(response, "data") and response.data: import json as _json try: raw = _json.dumps(response.data) if len(raw) <= max_body: response_body = response.data except (TypeError, ValueError): pass except Exception: pass actor = getattr(request, "user", None) if actor and not actor.is_authenticated: actor = None if actor is not None and not hasattr(actor, "_meta"): actor = None APIInteractionLog.objects.create( method=request.method, path=path, status_code=response.status_code, request_body=request_body, response_body=response_body, ip_address=request.META.get("REMOTE_ADDR"), actor=actor, duration_ms=duration_ms, request_id=getattr(request, "request_id", ""), user_agent=request.META.get("HTTP_USER_AGENT", ""), ) return response