Lightweight containerized prosody tooling + moved auth scripts + xmpp reconnect/auth stabilization
This commit is contained in:
@@ -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 {}),
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user