ligbox-ops-platform/projects/ops-desk/api/app/vm112_purge_stream.py
Ligbox Spec Hub edffd8b3c0 feat(desk): Serviços IaaS perf, Escopo OPS cards e blocklist UI (Spec 017/018)
Cache VM112/VM122 para lista e detalhe domínio, modal purge com loading animado,
cards Escopo OPS clicáveis (camada + Spec + navegação), blocklist visível na UI,
e documentação nas specs e anais de referência 20260625.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-25 18:53:57 +00:00

91 lines
2.8 KiB
Python

"""SSE stream — purge domínio VM112 + Desk (Spec 017 Fase 2)."""
from __future__ import annotations
import json
import queue
import threading
import time
from collections.abc import Iterator
from typing import Any
from app import auth, vm112_domains
def _sse(payload: dict[str, Any]) -> str:
return f"data: {json.dumps(payload, ensure_ascii=False)}\n\n"
def purge_sse_generator(domain: str, root_password: str, username: str) -> Iterator[str]:
domain = domain.lower().strip()
conn = auth.db()
try:
if not vm112_domains.verify_root_password(conn, root_password):
yield _sse({
"type": "error",
"step": vm112_domains._timeline_entry("Validação Root", "fail", "Senha Root incorrecta"),
})
return
finally:
conn.close()
yield _sse({"type": "step", "step": vm112_domains._timeline_entry("Validação Root + confirmação", "ok")})
yield _sse({
"type": "step",
"step": vm112_domains._timeline_entry(
"Purge VM112 — em execução",
"running",
"Carbonio, site, portal, Cloudflare, Traefik…",
),
})
vm112_result: dict[str, Any] = {"ok": False}
for kind, payload in vm112_domains.purge_vm112_with_poll(domain, poll_interval=2.0):
if kind == "step":
yield _sse({"type": "step", "step": payload, "phase": "vm112"})
elif kind == "heartbeat":
yield _sse({
"type": "heartbeat",
"elapsed": payload.get("elapsed", 0),
"label": "Purge VM112 — em execução",
})
elif kind == "final":
vm112_result = payload
if not vm112_result.get("ok", False):
yield _sse({
"type": "error",
"step": vm112_domains._timeline_entry(
"Purge VM112",
"fail",
str(vm112_result.get("error") or "falhou"),
),
})
return
break
conn = auth.db()
try:
desk_counts, desk_timeline = vm112_domains.purge_desk_timeline(
conn,
domain,
by_user=username,
purge_source="desk.api.purge-stream",
vm112_ok=vm112_result.get("ok"),
vm112_error=vm112_result.get("error"),
)
finally:
conn.close()
total_desk = sum(desk_counts.values())
for step in desk_timeline:
yield _sse({"type": "step", "step": step, "phase": "desk"})
done_step = vm112_domains._timeline_entry("Purge concluído", "ok", f"Desk: {total_desk} registo(s)")
yield _sse({
"type": "done",
"step": done_step,
"domain": domain,
"vm112": vm112_result,
"desk": desk_counts,
"by": username,
})