"""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() desk_counts: dict[str, int] = {} try: domain_l = domain.lower().strip() like = f"%{domain_l}%" desk_steps = ( ("Desk — webhook_events", "webhook_events", "DELETE FROM webhook_events WHERE payload LIKE ?", (like,)), ("Desk — tickets", "tickets", "DELETE FROM tickets WHERE subject LIKE ? OR payload LIKE ?", (like, like)), ("Desk — audit_domains", "audit_domains", "DELETE FROM audit_domains WHERE domain = ?", (domain_l,)), ("Desk — assist_sessions", "assist_sessions", "DELETE FROM assist_sessions WHERE domain = ?", (domain_l,)), ("Desk — audit_checks", "audit_checks", "DELETE FROM audit_checks WHERE domain = ?", (domain_l,)), ) for label, key, sql, params in desk_steps: yield _sse({"type": "step", "step": vm112_domains._timeline_entry(label, "running")}) n = conn.execute(sql, params).rowcount desk_counts[key] = n yield _sse({ "type": "step", "step": vm112_domains._timeline_entry(label, "ok", f"{n} registo(s) removido(s)"), "phase": "desk", }) conn.commit() finally: conn.close() total_desk = sum(desk_counts.values()) 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, })