"""Django middleware — emits APP_DJANGO_REQUEST for every request. Non-fatal.""" import logging import time logger = logging.getLogger(__name__) _emitter = None def set_emitter(emitter): global _emitter _emitter = emitter class ShellboundMiddleware: """WSGI middleware emitting Chronicle events per Django request.""" def __init__(self, get_response): self.get_response = get_response def __call__(self, request): start = time.monotonic() response = self.get_response(request) duration_ms = (time.monotonic() - start) * 1000 try: if _emitter: _emitter.request( method=request.method, path=request.path, status=response.status_code, duration_ms=round(duration_ms, 2), actor_did=_extract_did(request), view_name=_extract_view(request), ) except Exception as e: logger.debug("Chronicle middleware non-fatal: %s", e) return response def _extract_did(request) -> str: """Extract actor DID from request auth context.""" if hasattr(request, "auth") and request.auth: claims = getattr(request.auth, "payload", {}) if isinstance(claims, dict): did = claims.get("substrate_did", "") if did: return did if hasattr(request, "user") and request.user and getattr(request.user, "is_authenticated", False): user = request.user if hasattr(user, "external_id") and user.external_id: org = getattr(user, "organization", None) if org: return f"did:web:guildhouse.dev:{org.slug}:user:{user.external_id}" return "" def _extract_view(request) -> str: resolver = getattr(request, "resolver_match", None) if resolver: return resolver.view_name or getattr(resolver.func, "__name__", "") return ""