ligbox-ops-platform/specs/017-vm112-domain-orchestration/spec.md
Ligbox Spec Hub 3a2c64834b Initial import: ligbox-ops-platform + specs + LAPTOP + obsidian merge (CT130)
Source: VM122 /opt + obsidian-infra + LAPTOP
Hub: CT130 spec-hub 10.10.10.130
2026-06-19 17:26:41 +00:00

248 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Feature Specification: Domínios VM112 — Purge & Histórico (017)
**Criado:** 2026-06-16
**Actualizado:** 2026-06-16 (v2 — histórico de purges)
**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
1. **Resumo** — domínio, mail host, admin portal, contas Carbonio, zona CF
2. **Infra** — passos `get_status()` (Carbonio, DNS, SNI, Traefik)
3. **Contas** — lista e-mails Carbonio
4. **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»
5. **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:**
```json
{
"confirm_domain": "iofficebooks.com",
"root_password": "********"
}
```
**Validações purge:**
1. `user.role` ∈ {super_admin, ops_lead}
2. `verify_password(root_password, hash do user root)`
3. `confirm_domain` === domínio (case-insensitive)
4. Domínio ∉ blocklist (`ligbox.com.br`, etc.)
5. Proxy VM112 `POST /api/admin/domains/{domain}/purge` com `X-Api-Key`
**Pós-purge Desk:** apagar `audit_domains`, `webhook_events`, `tickets`, `assist_sessions`, `audit_checks` com referência ao domínio.
---
## API VM112
| Método | Path | Auth |
|--------|------|------|
| GET | `/api/admin/domains` | `X-Api-Key` |
| 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)_ |
**Purge VM112 (ordem):**
1. Apagar contas Carbonio (`zmprov da`)
2. Apagar domínio Carbonio (`zmprov dd`)
3. Remover portal users com `planned_corporate_email` no domínio
4. Apagar `/opt/ligbox-sites/domains/{domain}/`
5. Apagar zona Cloudflare (se existir na conta Ibytera)
6. Remover `mail.{domain}` do SNI + routers Traefik (CT114)
7. 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` ~6079s).
**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:
1. Cabeçalho (domínio, status, utilizador, data, job id)
2. Removido no Desk — webhook_events, tickets, audit_domains, assist_sessions, audit_checks
3. Removido na VM112 — Carbonio, portal, site, Cloudflare, Traefik
4. 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
1. Admin vê aba «Histórico de purges» em Eventos.
2. Lista mostra purges com status, utilizador, data e resumo Desk.
3. Clique abre modal com timeline completa e contagens por serviço.
4. Badges correctos: `done`, `error`, `running`, `queued`.
5. `technician` / `noc` não vêem a aba.
### Consulta manual (SSH VM122)
```bash
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;"
```
```bash
curl -s -H "Authorization: Bearer $TOKEN" \
"https://desk.ligbox.com.br/api/v1/vm112/purge/jobs/57845ca1c5c64b53"
```
---
## 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)
1. Admin executa purge a partir de Serviços.
2. Purge com senha root errada → erro na timeline.
3. Purge com domínio confirmado errado → HTTP 400.
4. Após purge, domínio ausente em Carbonio, ligbox-sites e Desk.
5. 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 | ✅ |
| 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.