This repository has been archived on 2026-04-16. You can view files and clone it, but cannot push or open issues or pull requests.
substrate-sdk-python/substrate_sdk/django/middleware.py
Tyler King 89a054d656 initial: substrate-sdk-python v0.1.0
Python SDK for shellbound Django applications.
Provides ShellApp, ShardContext, ShellboundMiddleware.
Emits Chronicle events to stdout in dev mode.

Includes fix for IndexError in apps.py when
DJANGO_SETTINGS_MODULE has no dots (e.g.
instance_settings). Shard name now falls back
safely without eager default argument parsing.

Implements SHELLBOUND-APP-0001 §4 (dev mode).
Wired into entropyopposition as of 2026-03-18.
2026-03-18 13:53:58 -04:00

66 lines
1.9 KiB
Python

"""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 ""