Lightweight containerized prosody tooling + moved auth scripts + xmpp reconnect/auth stabilization

This commit is contained in:
2026-03-05 02:18:12 +00:00
parent 0718a06c19
commit 2140c5facf
69 changed files with 3767 additions and 144 deletions

View File

@@ -1,12 +1,15 @@
from django.http import JsonResponse
from django.shortcuts import render
from django.views import View
from core.models import (
AdapterHealthEvent,
AIRequest,
AIResult,
AIResultSignal,
Chat,
ChatSession,
ConversationEvent,
Group,
MemoryItem,
Message,
@@ -25,6 +28,8 @@ from core.models import (
WorkspaceConversation,
WorkspaceMetricSnapshot,
)
from core.events.projection import shadow_compare_session
from core.transports.capabilities import capability_snapshot
from core.views.manage.permissions import SuperUserRequiredMixin
@@ -37,6 +42,8 @@ class SystemSettings(SuperUserRequiredMixin, View):
"messages": Message.objects.filter(user=user).count(),
"queued_messages": QueuedMessage.objects.filter(user=user).count(),
"message_events": MessageEvent.objects.filter(user=user).count(),
"conversation_events": ConversationEvent.objects.filter(user=user).count(),
"adapter_health_events": AdapterHealthEvent.objects.filter(user=user).count(),
"workspace_conversations": WorkspaceConversation.objects.filter(
user=user
).count(),
@@ -85,6 +92,8 @@ class SystemSettings(SuperUserRequiredMixin, View):
conversation__user=user
).delete()[0]
deleted += MessageEvent.objects.filter(user=user).delete()[0]
deleted += ConversationEvent.objects.filter(user=user).delete()[0]
deleted += AdapterHealthEvent.objects.filter(user=user).delete()[0]
deleted += Message.objects.filter(user=user).delete()[0]
deleted += QueuedMessage.objects.filter(user=user).delete()[0]
deleted += WorkspaceConversation.objects.filter(user=user).delete()[0]
@@ -156,3 +165,97 @@ class SystemSettings(SuperUserRequiredMixin, View):
"notice_message": notice_message,
},
)
class ServiceCapabilitySnapshotAPI(SuperUserRequiredMixin, View):
def get(self, request):
service = str(request.GET.get("service") or "").strip().lower()
return JsonResponse(
{
"ok": True,
"data": capability_snapshot(service),
}
)
class AdapterHealthSummaryAPI(SuperUserRequiredMixin, View):
def get(self, request):
latest_by_service = {}
rows = AdapterHealthEvent.objects.order_by("service", "-ts")[:200]
for row in rows:
key = str(row.service or "").strip().lower()
if key in latest_by_service:
continue
latest_by_service[key] = {
"status": str(row.status or ""),
"reason": str(row.reason or ""),
"ts": int(row.ts or 0),
"created_at": row.created_at.isoformat(),
}
return JsonResponse({"ok": True, "services": latest_by_service})
class TraceDiagnosticsAPI(SuperUserRequiredMixin, View):
def get(self, request):
trace_id = str(request.GET.get("trace_id") or "").strip()
if not trace_id:
return JsonResponse(
{"ok": False, "error": "trace_id_required"},
status=400,
)
rows = list(
ConversationEvent.objects.filter(
user=request.user,
trace_id=trace_id,
)
.select_related("session")
.order_by("ts", "created_at")[:500]
)
return JsonResponse(
{
"ok": True,
"trace_id": trace_id,
"count": len(rows),
"events": [
{
"id": str(row.id),
"ts": int(row.ts or 0),
"event_type": str(row.event_type or ""),
"direction": str(row.direction or ""),
"session_id": str(row.session_id or ""),
"origin_transport": str(row.origin_transport or ""),
"origin_message_id": str(row.origin_message_id or ""),
"payload": dict(row.payload or {}),
}
for row in rows
],
}
)
class EventProjectionShadowAPI(SuperUserRequiredMixin, View):
def get(self, request):
session_id = str(request.GET.get("session_id") or "").strip()
if not session_id:
return JsonResponse(
{"ok": False, "error": "session_id_required"},
status=400,
)
detail_limit = int(request.GET.get("detail_limit") or 25)
session = ChatSession.objects.filter(
id=session_id,
user=request.user,
).first()
if session is None:
return JsonResponse(
{"ok": False, "error": "session_not_found"},
status=404,
)
compared = shadow_compare_session(session, detail_limit=max(0, detail_limit))
return JsonResponse(
{
"ok": True,
"result": compared,
"cause_summary": dict(compared.get("cause_counts") or {}),
}
)