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>
16 KiB
Feature Specification: Domínios VM112 — Purge & Histórico (017)
Criado: 2026-06-16
Actualizado: 2026-06-25 (performance modal + UX Escopo OPS + blocklist UI)
Solicitado por: Roger
Status: v1 + v2 concluídos · Fase 3 VM112 pendente
Prioridade: P1 (testes E2E + padrão de limpeza)
Sistema: Desk VM122 + Wizard VM112
Módulo: vm112-domains
UI purge: página Serviços (Spec 018)
UI histórico: Eventos → Histórico de purges
Resumo
Técnicos Admin (super_admin, ops_lead) executam purge completo de domínios VM112 (Carbonio, site, portal, Cloudflare, Traefik/SNI, registos Desk) a partir da página Serviços, com timeline ao vivo no drawer lateral.
v2 (2026-06-16): cada purge fica persistido em SQLite e consultável em Eventos → Histórico de purges — lista clicável + modal com timeline, utilizador e serviços removidos.
Uso inicial: limpar domínios de teste para reentrarem no wizard. Futuro: padrão de limpeza de dados por domínio.
Módulo Desk (Spec 015)
| Campo | Valor |
|---|---|
id |
vm112-domains |
label |
Domínios VM112 |
default_enabled |
true |
nav_views |
(vazio — purge na página Serviços, histórico em Eventos) |
RBAC
| Acção | Perfis |
|---|---|
| Executar purge (Serviços) | super_admin, ops_lead + senha Root |
| Ver histórico de purges (Eventos) | super_admin, ops_lead |
| Listar / detalhe job purge | super_admin, ops_lead |
Técnicos technician e noc não acedem.
UI — Serviços (Spec 018)
Tile E-mail Tenant → modal purge
- Resumo — domínio, mail host, admin portal, contas Carbonio, zona CF
- Infra — passos
get_status()(Carbonio, DNS, SNI, Traefik) - Contas — lista e-mails Carbonio
- Zona perigosa — Purge (Admin only)
- Aviso irreversível
- Confirmação: digitar domínio exacto
- Campo senha Root (Desk)
- Botão «Apagar domínio e todos os dados»
- Drawer lateral
vm112-purge-drawer— timeline em tempo real durante execução
API Desk (VM122)
| Método | Path | Descrição |
|---|---|---|
| GET | /api/v1/vm112/domains?q= |
Lista domínios orquestrados (proxy VM112) |
| GET | /api/v1/vm112/domains/{domain} |
Detalhe + infra status |
| POST | /api/v1/vm112/domains/{domain}/purge/jobs |
Recomendado — purge async + polling |
| GET | /api/v1/vm112/purge/jobs/{job_id} |
Estado / timeline do job |
| POST | /api/v1/vm112/purge/jobs/{job_id}/recover |
Recuperar job após timeout UI |
| GET | /api/v1/vm112/purge/jobs?limit=&offset= |
v2 — lista histórico persistido |
| POST | /api/v1/vm112/domains/{domain}/purge/stream |
Purge SSE (legado Traefik) |
| POST | /api/v1/vm112/domains/{domain}/purge |
Purge síncrono (legado) |
Body purge:
{
"confirm_domain": "iofficebooks.com",
"root_password": "********"
}
Validações purge:
user.role∈ {super_admin, ops_lead}verify_password(root_password, hash do user root)confirm_domain=== domínio (case-insensitive)- Domínio ∉ blocklist (
ligbox.com.br, etc.) - Proxy VM112
POST /api/admin/domains/{domain}/purgecomX-Api-Key
Pós-purge Desk: apagar audit_domains, tickets, assist_sessions, audit_checks, domain_console_scenarios com referência ao domínio. Preservar webhook_events e inserir marcador domain.purged (source=desk.purge).
API VM112
| Método | Path | Auth |
|---|---|---|
| GET | /api/admin/domains |
X-Api-Key — resposta inclui cached, cache_age_sec (2026-06-25) |
| GET | /api/admin/domains/{domain} |
X-Api-Key |
| POST | /api/admin/domains/{domain}/purge |
X-Api-Key |
| GET | /api/admin/domains/purge-jobs/{job_id} |
X-Api-Key (memória, efémero) |
Cache listagem domínios (2026-06-25)
Problema: list_orchestrated_domains() chamava zmprov gad em cada GET /api/admin/domains (~5s) — gargalo da página Serviços IaaS no Desk (Spec 018).
Implementação VM112 (deploy/vm112-wizard/perf-domains-list-20260625/):
| Cache | TTL | Chave | Invalidação |
|---|---|---|---|
Lista Carbonio (gad) |
90s | gad_all_domains |
zmprov cd, zmprov dd, purge concluído |
| Lista orquestrada montada | 60s | orchestrated_domains_list |
idem |
| Detalhe domínio (purge modal) | 120s | domain_detail:{domain} |
purge / dd / cd |
Infra checks (get_status) |
90s | infra_status:{domain} |
idem |
Contas (zmprov gaa) |
45s | accounts_list:{domain} |
purge contas / dd |
Optimizações modal purge (2026-06-25): get_domain_detail() executa gaa + infra + Cloudflare em paralelo (ThreadPoolExecutor). 1.º hit ~16s → 2.º hit ~0,04s.
Ficheiros: carbonio_cache.py, carbonio.list_all_domains(), domain_orchestration.get_domain_detail(), infrastructure.get_status() cache.
Desk VM122 mantém cache proxy adicional 60s (VM112_DOMAINS_CACHE_TTL) — ver Spec 018 § Performance.
Purge VM112 (ordem):
- Apagar contas Carbonio (
zmprov da) - Apagar domínio Carbonio (
zmprov dd) - Remover portal users com
planned_corporate_emailno domínio - Apagar
/opt/ligbox-sites/domains/{domain}/ - Apagar zona Cloudflare (se existir na conta Ibytera)
- Remover
mail.{domain}do SNI + routers Traefik (CT114) - Apagar logs sessão JSONL com referência ao domínio
Fase 2 — Jobs async + polling (implementado)
POST /api/v1/vm112/domains/{domain}/purge/jobs inicia thread em background.
UI faz polling GET /api/v1/vm112/purge/jobs/{id} a cada 2s.
Motivo: SSE longo falhava via Traefik (504 / Failed to fetch ~60–79s).
Fix nginx Desk: proxy_read_timeout 600s em frontend/nginx.conf.
Persistência SQLite (vm112_purge_jobs) criada nesta fase — base para v2.
Fase 2 — SSE (implementado, legado)
POST /api/v1/vm112/domains/{domain}/purge/stream · text/event-stream
| type | Conteúdo |
|---|---|
step |
{ label, at, status, detail } |
heartbeat |
{ elapsed } — cada 5s |
error |
purge falhou |
done |
{ desk, vm112, domain } |
Ordem: validação → VM112 (heartbeat) → passos VM112 → passos Desk → concluído.
v2 — Histórico de purges (implementado 2026-06-16)
Problema resolvido
| Antes | Depois |
|---|---|
| Timeline só ao vivo no drawer | Histórico persistente no Desk |
| Dados em SQLite sem UI | Lista + modal de detalhe |
| VM112 jobs em memória (efémero) | Fonte de verdade: VM122 ops.db |
| Purges «desapareciam» ao fechar modal | Consulta por domínio, data, utilizador |
Nota: purges antes da persistência (ex.: betinsport.com) não aparecem no histórico.
UI — Eventos
- Aba Webhooks (existente)
- Aba Histórico de purges (Admin only)
- Lista: Job ID, domínio, status, utilizador, resumo Desk, data, duração VM112
- Clique na linha → modal com:
- Cabeçalho (domínio, status, utilizador, data, job id)
- Removido no Desk — webhook_events, tickets, audit_domains, assist_sessions, audit_checks
- Removido na VM112 — Carbonio, portal, site, Cloudflare, Traefik
- Timeline completa (
timeline_json)
Persistência
| Campo | Valor |
|---|---|
| Base | /var/lib/ligbox-ops-platform/ops.db (Docker: /data/ops.db) |
| Tabela | vm112_purge_jobs |
| Colunas | timeline_json, desk_json, vm112_json, by_user, status, created_at |
Ficheiros v2
| Ficheiro | Alteração |
|---|---|
api/app/vm112_purge_jobs.py |
list_jobs(), schema, persistência |
api/app/vm112_domains_routes.py |
GET /purge/jobs |
frontend/assets/app.js |
renderPurgeHistory(), modal, aba Eventos |
frontend/index.html |
Toolbar Eventos + purge-history-modal |
frontend/assets/styles.css |
Estilos lista/modal |
Critérios de aceitação v2
- Admin vê aba «Histórico de purges» em Eventos.
- Lista mostra purges com status, utilizador, data e resumo Desk.
- Clique abre modal com timeline completa e contagens por serviço.
- Badges correctos:
done,error,running,queued. technician/nocnão vêem a aba.
Consulta manual (SSH VM122)
sqlite3 /var/lib/ligbox-ops-platform/ops.db \
"SELECT id, domain, status, by_user, created_at FROM vm112_purge_jobs ORDER BY created_at DESC;"
curl -s -H "Authorization: Bearer $TOKEN" \
"https://desk.ligbox.com.br/api/v1/vm112/purge/jobs/57845ca1c5c64b53"
Extensão — Spec 026 (purge Traefik validation)
Validação YAML + smoke onboard pós-remoção Traefik (CT114). Incidente 2026-06-19: dynamic.yml inválido após purge → 404 global no onboard.
Spec dedicada: specs/026-purge-traefik-validation/spec.md
Extensão — Spec 032 (purge autorização extra)
Domínios em PURGE_EXTRA_AUTH_DOMAINS (ex.: myvexx.com) exigem código de autorização gerado pelo root em Infra (senha Root), além da validação purge normal.
| Camada | Regra |
|---|---|
| Blocklist | ligbox.com.br, itecnologys.com — purge proibido |
| Extra auth | Código único + senha Root |
| Normal | Senha Root (Spec 017 v1) |
Spec dedicada: specs/032-purge-domain-extra-auth/spec.md
UI geração: Infraestrutura → «Códigos autorização purge»
UI consumo: Serviços → modal purge (campo código)
Fase 3 — VM112 passos em tempo real (pendente)
VM112 (/opt/ligbox-wizard) emitir passos individuais durante execução (Carbonio, CF, Traefik) em vez de bloco único + heartbeat. Alterações no wizard, não só no Desk.
Critérios de aceitação (v1)
- Admin executa purge a partir de Serviços.
- Purge com senha root errada → erro na timeline.
- Purge com domínio confirmado errado → HTTP 400.
- Após purge, domínio ausente em Carbonio, ligbox-sites e Desk.
- Drawer mostra progresso ao vivo; job persiste em SQLite.
Fora de escopo
- Purge parcial (só contas, só DNS)
- Scheduler de limpeza automática
- Export CSV/PDF do histórico
- Filtro por domínio/data na lista de histórico
- Retenção automática / purge de jobs antigos
- Link directo Serviços → histórico do domínio
Conclusão (2026-06-16)
A Spec 017 cobre o ciclo completo de purge de domínio VM112:
| Fase | Entrega | Estado |
|---|---|---|
| v1 | Purge completo via Serviços + validação Root | ✅ |
| Fase 2 | Jobs async, polling, persistência SQLite | ✅ |
| Fase 2 SSE | Timeline drawer (legado) | ✅ |
| v2 | Histórico em Eventos — lista + modal audit trail | ✅ |
| 032 | Códigos autorização extra (myvexx.com) — Infra + Serviços | ✅ |
| 026 | Validação Traefik pós-purge | ✅ |
| Fase 3 | Passos VM112 em tempo real no wizard | ⏳ |
Purges registados (exemplo): myvexx.com, diarissima.com, ibytera.com — visíveis em Eventos → Histórico de purges.
Próximo passo natural: Fase 3 no wizard VM112; depois filtros/export no histórico se necessário.
Incidente 2026-06-25 — Auditor 404 + purge timeout (Roger)
Sintomas reportados
| # | UI | Mensagem | Domínio / ref |
|---|---|---|---|
| 1 | Eventos → Auditor de Eventos | Erro: Not Found |
webhook · 291 (domain.purged, cenario-demo.ops.ligbox.com.br) |
| 2 | Eventos → Histórico de purges | badge ERRO, VM112 timed out (33s), Desk 0 | eplacebets.com · job 84a78c85d6b6490c |
Diagnóstico
| Erro | Causa raiz | Impacto real |
|---|---|---|
| Auditor 404 | Modal chamava detalhe inexistente (GET /webhooks/events/{id} ausente); evento 291 estava OK em SQLite |
Purge cenario-demo concluído — falha só de visualização |
| Purge timed out | httpx poll VM112 com timeout 60s → excepção timed out; job marcado error antes da fase Desk; recover não actuava em jobs error |
eplacebets.com pode ter ficado parcial na VM112; Desk não limpo |
Correções (2026-06-25)
| Camada | Alteração |
|---|---|
| API | GET /api/v1/webhooks/events/{event_id} — detalhe para Auditor |
| API purge | VM112_PURGE_HTTP_TIMEOUT=300 (env); poll tolerante a falhas transitórias; auto-recuperação se domínio já ausente na VM112 |
| Jobs | recover_job actua em status error; _execute_job tenta recuperação automática pós-timeout |
| UI Eventos | Linhas clicáveis → modal Auditor de Eventos; render dedicado domain.purged; filtro origem Purge |
| UI Histórico | Labels Desk actualizados (domain.purged preserva histórico); botão Recuperar purge em jobs com erro |
Ficheiros
| Ficheiro | Alteração |
|---|---|
api/app/main.py |
GET /webhooks/events/{id} |
api/app/vm112_domains.py |
timeout 300s, poll resiliente, recover pós-timeout |
api/app/vm112_purge_jobs.py |
recover em error, auto-recover |
frontend/index.html |
event-auditor-modal, filtro Purge |
frontend/assets/app.js |
Auditor, purge desk labels, botão recover |
Validação pós-deploy
# Evento 291 — deve retornar domain.purged (HTTP 200)
curl -s -H "Authorization: Bearer $TOKEN" \
"https://desk.ligbox.com.br/api/v1/webhooks/events/291"
# Recuperar job eplacebets se VM112 já limpou
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
"https://desk.ligbox.com.br/api/v1/vm112/purge/jobs/84a78c85d6b6490c/recover"
Resultado esperado
- Clicar evento 291 → modal mostra utilizador, ferramenta, domínio e payload (sem 404).
- Job
84a78c85d6b6490c→ Recuperar purge conclui fase Desk seeplacebets.comjá não existir na VM112; senão repetir purge após fix de timeout. - Novos purges longos (Carbonio) não falham aos ~33–60s por timeout HTTP do poll.
Resultado pós-deploy (2026-06-25, VM122)
| Verificação | Resultado |
|---|---|
GET /webhooks/events/291 |
✅ domain.purged · desk.purge |
eplacebets.com na VM112 |
✅ ausente (purge VM112 efectivo) |
Job 84a78c85d6b6490c recover |
✅ status=done, Desk limpo, marcador domain.purged inserido |
Job posterior e238c1efb7c24d92 |
✅ já estava done (retry manual anterior) |
UI Serviços — loading, blocklist, Escopo OPS (2026-06-25)
Melhorias UX na página Serviços IaaS (Spec 018) sem alterar regras de purge.
Blocklist — apresentação
| Domínio | API purge_blocked |
UI lista clientes | UI modal purge |
|---|---|---|---|
ligbox.com.br |
true |
badge 🔒 (tooltip) | formulário desactivado |
itecnologys.com |
true |
idem | idem |
| restantes | false |
— | senha Root + confirmação |
Constante Desk: PURGE_BLOCKLIST em vm112_domains.py — espelhada no frontend (servicos.js) para badges imediatos antes do detalhe API.
Painel Escopo OPS: banner vermelho quando cliente seleccionado ∈ blocklist, listando domínios protegidos.
Modal «Gerir» — loading animado
Enquanto GET /api/v1/vm112/domains/{domain} corre (~10–15s cold):
| Elemento | Comportamento |
|---|---|
| Skeleton | Dados da lista clientes (admin portal, Carbonio, site) |
| Barra progresso | Indeterminada (CSS vm112-load-bar) |
| Etapas | Rotação Carbonio → Infra CT114 → Cloudflare |
| Botão purge | Spinner + «A carregar…» desactivado |
| A11y | aria-busy, aria-live="polite" |
Escopo OPS — cards clicáveis
Ver Spec 018 § Coluna Escopo OPS (mapa completo camada → Spec → destino).
Navegação cross-módulo: window.DeskNavigate.go(view, opts) em app.js.
Ficheiros (2026-06-25 UX)
| Ficheiro | Alteração |
|---|---|
frontend/assets/servicos.js |
OPS_SCOPES enriquecido, navigateScope, blocklist UI, loading modal |
frontend/assets/styles.css |
.vm112-load-*, .servicos-scope-*, alinhamento lista clientes |
frontend/assets/app.js |
DeskNavigate |
frontend/index.html |
cache bust ?v=20260625align1 |
Versão deploy VM122 (2026-06-25)
# Frontend (sem rebuild — docker cp)
docker cp servicos.js ligbox-ops-platform_frontend_1:/usr/share/nginx/html/assets/
docker cp styles.css ligbox-ops-platform_frontend_1:/usr/share/nginx/html/assets/
docker cp app.js ligbox-ops-platform_frontend_1:/usr/share/nginx/html/assets/
docker cp index.html ligbox-ops-platform_frontend_1:/usr/share/nginx/html/