Actualiza dns-viewer, exec/rollback, fichas VM112/122/123, API Console proxy, token interno wizard e verify script para evitar drift documental. Co-authored-by: Cursor <cursoragent@cursor.com>
401 lines
18 KiB
Markdown
401 lines
18 KiB
Markdown
# Spec 037-DNS-VIEWER — Painel DNS read-only unificado
|
||
|
||
**Criado:** 2026-06-25
|
||
**Solicitado por:** Roger
|
||
**Status:** 🏗️ V1–V3 em produção (VM122/VM123) · V4 patches prontos · deploy VM112 pendente
|
||
**Prioridade:** P1
|
||
**Depende de:** [spec.md](./spec.md) · [004 cloudflare-zone-provision](../004-onboard-funnel-events/cloudflare-zone-provision.md) · [009](../009-ops-audit-overview/spec.md) · [028](../028-openpanel-ce-ligbox-reengineering/DNS53_OPENPANEL_PORTA53.md) · [035 §5.5](../035-ligbox-mail-bundles-foss-openpanel/domain-manager-console-ui.md) · [027 RBAC](../027-desk-rbac-function-matrix/spec.md)
|
||
|
||
---
|
||
|
||
## 1. Objectivo
|
||
|
||
Permitir que **staff Ligbox** e **gerente de domínio** vejam **todos os apontamentos DNS** relevantes para um domínio — **sem alterar** registos na UI — com:
|
||
|
||
1. **Lista completa read-only** (tipo, nome, conteúdo, TTL, função)
|
||
2. **Origem do DNS** (Cloudflare Ligbox · BYO · OpenPanel BIND · registrador externo · resolução pública)
|
||
3. **Link «Editar aqui»** para a consola correcta (Cloudflare, OpenPanel, registrador)
|
||
4. **Regra wizard:** se o cliente escolhe **trazer DNS para Ligbox** → mostrar o que **será / foi aplicado**; caso contrário → mostrar **onde o DNS está no momento** da operação
|
||
|
||
**Fora de scope:** PATCH/POST/DELETE de registos no Desk ou Console — edição só via deep-link externo.
|
||
|
||
---
|
||
|
||
## 2. Problema actual (Roger 2026-06-25)
|
||
|
||
| Superfície | O que existe hoje | Lacuna |
|
||
|------------|-------------------|--------|
|
||
| Desk Overview → modal domínio | Tabela CF via `GET /api/v1/dns/cloudflare/records` | Só Cloudflare; sem OpenPanel; sem link editar |
|
||
| Desk Serviços IaaS | Resumo zona CF | Sem tabela de registos |
|
||
| Wizard passo DNS | Modos Ligbox/BYO/Registrador (037) | Cliente vê passos; staff não tem painel unificado |
|
||
| OpenPanel BIND | Zonas em `openpanel_dns` VM123 | **Zero UI Desk/Console** — operador não sabe apontamentos |
|
||
| Console `/admin` §5.5 | MX/NS status (mock) | Sem lista completa |
|
||
|
||
---
|
||
|
||
## 3. Regra de produto — caminho Ligbox vs externo
|
||
|
||
Esta regra **obrigatória** aplica-se em **Wizard**, **Desk** e **Console `/admin`**.
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[Domínio + sessão onboarding] --> B{dns_mode escolhido?}
|
||
B -->|ligbox_cf_*| C[Modo PLANEADO / APLICADO]
|
||
B -->|byo_cf| D[Modo BYO — zona cliente CF]
|
||
B -->|external / registrador| E[Modo ACTUAL — DNS público]
|
||
B -->|openpanel_bind| F[Modo OPENPANEL — zona BIND VM123]
|
||
|
||
C --> C1[Mostrar NS Cloudflare Ligbox]
|
||
C --> C2[Mostrar mail_dns_records preview]
|
||
C --> C3[Após apply: registos CF reais]
|
||
C --> C4[Link: Cloudflare conta Ligbox]
|
||
|
||
D --> D1[Mostrar registos zona BYO via token vault]
|
||
D --> D2[Link: dash.cloudflare.com zona cliente]
|
||
|
||
E --> E1[GET dns/instructions — o que falta colar]
|
||
E --> E2[GET dns/verify + dig público 009]
|
||
E --> E3[NS actuais no registrador]
|
||
E --> E4[Link: registrador ou painel CF cliente]
|
||
|
||
F --> F1[Listar zona BIND OpenPanel]
|
||
F --> F2[Link: OpenPanel DNS UI]
|
||
```
|
||
|
||
### 3.1 Tabela de decisão UI
|
||
|
||
| `dns_mode` (persistido) | Badge UI | Fonte de dados | O que mostrar |
|
||
|-------------------------|----------|----------------|---------------|
|
||
| `ligbox_cf_ligit` / `itecnologys` / `ibytera` | **DNS Ligbox** | CF API conta Ligbox + `mail_dns_records()` | Registos **aplicados**; se pré-NS → **preview** + NS a configurar |
|
||
| `ligbox_cf_provision_pending` | **DNS Ligbox (aguarda NS)** | `provision-zone` + preview | NS Cloudflare + tabela «será aplicado após NS» |
|
||
| `byo_cf` | **Cloudflare cliente** | Token BYO vault + CF API | Registos actuais na zona BYO |
|
||
| `external` / `registrar` | **DNS externo** | `dns/instructions` + `dig` público | Estado **actual** + instruções manuais |
|
||
| `openpanel_bind` | **OpenPanel BIND** | OpenAdmin API / zone file VM123 | Registos autoritativos `:53` Ligbox |
|
||
| `unknown` | **A determinar** | `dns/resolve` + NS lookup | NS actuais + sugestão de caminho |
|
||
|
||
### 3.2 Texto UX (gerente / staff)
|
||
|
||
| Modo | Mensagem principal |
|
||
|------|-------------------|
|
||
| Ligbox (pré-apply) | «Estes apontamentos **serão configurados** na Cloudflare Ligbox quando confirmar o passo DNS.» |
|
||
| Ligbox (pós-apply) | «Apontamentos **activos** na Cloudflare Ligbox.» |
|
||
| Externo | «O domínio usa DNS **fora da Ligbox**. Abaixo: o que está **publicamente** resolvido agora.» |
|
||
| OpenPanel | «Zona servida pelo **DNS Ligbox (OpenPanel)** em `95.216.14.162`.» |
|
||
|
||
---
|
||
|
||
## 4. Fontes de dados (backend)
|
||
|
||
### 4.1 Matriz de providers
|
||
|
||
| Provider ID | Quando usar | API / método | Edit link template |
|
||
|-------------|-------------|--------------|-------------------|
|
||
| `cf_ligbox` | Zona numa conta Ligbox (037) | CF API token conta + `zone_id` | `https://dash.cloudflare.com/{account_id}/{zone_name}/dns` |
|
||
| `cf_byo` | BYO wizard | Token vault sessão / entitlements | `https://dash.cloudflare.com/` (zona cliente) |
|
||
| `openpanel_bind` | Subdomínio/site bundle OP | OpenAdmin `GET /api/domains/{domain}/dns` ou `rndc`/zone export | `https://openpanel.ligbox.com.br/domains/{domain}/dns` |
|
||
| `public_resolver` | Sempre (fallback / externo) | `dig` + `dns/verify` VM112 | — |
|
||
| `planned_ligbox` | Ligbox antes de apply | `mail_dns_records(domain)` wizard | — (preview only) |
|
||
|
||
### 4.2 Endpoint unificado (novo — Desk + Console)
|
||
|
||
```http
|
||
GET /api/v1/dns/viewer/{domain}
|
||
Authorization: Bearer <JWT staff ou domain-admin>
|
||
# OU (wizard / workers internos):
|
||
X-Ops-Internal-Token: <OPS_INTERNAL_TOKEN>
|
||
Query: ?include_public=true&email_service=true
|
||
```
|
||
|
||
**Variante Console gerente (Spec 035 V3):**
|
||
|
||
```http
|
||
GET /api/v1/domain-console/dns/viewer/{domain}
|
||
Authorization: Bearer <JWT domain-admin ou staff>
|
||
Query: ?email_service=true&include_public=true
|
||
```
|
||
|
||
Implementação: `domain_console_routes.py` — proxy para `fetch_dns_viewer()`; filtra `edit_links` CF Ligbox para roles não-staff.
|
||
|
||
**Autenticação interna (V4 wizard → Desk):**
|
||
|
||
| Header | Quem usa | Função |
|
||
|--------|----------|--------|
|
||
| `Authorization: Bearer …` | Desk UI, Console `/admin` | JWT staff ou domain-admin |
|
||
| `X-Ops-Internal-Token` | VM112 wizard backend | `require_internal_or_user` em `main.py` — evita duplicar lógica DNS no wizard |
|
||
|
||
Variáveis VM112: `DESK_API_URL=http://10.10.10.122:8080`, `OPS_INTERNAL_TOKEN` (mesmo valor Desk).
|
||
|
||
**Response:**
|
||
|
||
```json
|
||
{
|
||
"domain": "empresa.com.br",
|
||
"dns_mode": "ligbox_cf_ibytera",
|
||
"mode_label": "Cloudflare Ligbox (ibytera)",
|
||
"display_mode": "applied",
|
||
"authoritative_source": "cf_ligbox",
|
||
"nameservers": {
|
||
"current_public": ["ns1.registro.br", "ns2.registro.br"],
|
||
"ligbox_cloudflare": ["ada.ns.cloudflare.com", "bob.ns.cloudflare.com"],
|
||
"match_ligbox": false
|
||
},
|
||
"records": [
|
||
{
|
||
"source": "cf_ligbox",
|
||
"status": "applied",
|
||
"type": "MX",
|
||
"name": "empresa.com.br",
|
||
"content": "mail.empresa.com.br",
|
||
"priority": 10,
|
||
"ttl": 3600,
|
||
"purpose": "mx",
|
||
"email_related": true
|
||
}
|
||
],
|
||
"planned_records": [],
|
||
"public_checks": {
|
||
"mx": { "ok": true, "values": ["10 mail.empresa.com.br"] },
|
||
"spf": { "ok": true },
|
||
"dkim": { "ok": false, "hint": "TXT _domainkey ausente" },
|
||
"dmarc": { "ok": true }
|
||
},
|
||
"edit_links": [
|
||
{
|
||
"label": "Editar na Cloudflare (Ligbox)",
|
||
"provider": "cf_ligbox",
|
||
"url": "https://dash.cloudflare.com/…/empresa.com.br/dns",
|
||
"roles": ["super_admin", "ops_lead", "devops", "seo"]
|
||
}
|
||
],
|
||
"instructions": null,
|
||
"errors": []
|
||
}
|
||
```
|
||
|
||
**Variante wizard (sessão cliente — sem staff JWT):**
|
||
|
||
```http
|
||
GET /api/onboarding/dns/viewer/{domain}
|
||
Header: X-Onboarding-Session: {session}
|
||
```
|
||
|
||
Reutiliza mesma shape; filtra `edit_links` vazios para cliente; mostra `planned_records` quando `display_mode=planned`.
|
||
|
||
### 4.3 Endpoints existentes reutilizados
|
||
|
||
| Endpoint | Papel no viewer |
|
||
|----------|-----------------|
|
||
| `GET /api/onboarding/dns/resolve/{domain}` | Detectar `dns_mode` + conta CF |
|
||
| `GET /api/onboarding/dns/instructions/{domain}` | Modo externo — o que colar |
|
||
| `GET /api/onboarding/dns/verify/{domain}` | Checks públicos |
|
||
| `GET /api/onboarding/dns/portal-onboarding/{domain}` | NS + passos registrador |
|
||
| `GET /api/v1/dns/cloudflare/records` | **Legado Desk** — migrar para viewer |
|
||
| Spec 009 audit | `public_checks` MX/SPF/DKIM/DMARC |
|
||
|
||
### 4.4 OpenPanel BIND (Fase 2 viewer)
|
||
|
||
| Método | Descrição |
|
||
|--------|-----------|
|
||
| Desk proxy | `GET /api/v1/dns/openpanel/records?domain=` |
|
||
| Backend | Bridge VM123 → OpenAdmin list records |
|
||
| Fallback | SSH `docker exec openpanel_dns` zone dump (read-only) |
|
||
|
||
Ver [028 DNS53](../028-openpanel-ce-ligbox-reengineering/DNS53_OPENPANEL_PORTA53.md).
|
||
|
||
---
|
||
|
||
## 5. Superfícies UI
|
||
|
||
### 5.1 Desk VM122 (staff)
|
||
|
||
| Local | Componente | RBAC |
|
||
|-------|------------|------|
|
||
| Overview → modal tenant → clicar domínio | Secção **«DNS do domínio»** (substitui só-CF) | `cloudflare_dns.read` + roles 027 |
|
||
| Serviços IaaS → modal domínio | Mesma secção embed | idem |
|
||
| Chamado / ticket domínio | Tab DNS read-only | technician+ |
|
||
|
||
**Wireframe Desk:**
|
||
|
||
```
|
||
┌─ DNS — empresa.com.br ────────────────────────────────────────┐
|
||
│ [DNS Ligbox ▼] Zona activa · 14 registos · 6 e-mail │
|
||
│ NS públicos: ns1.registro.br … ⚠ ainda não apontam CF Ligbox │
|
||
├───────────────────────────────────────────────────────────────┤
|
||
│ Função │ Nome │ Tipo │ Conteúdo │ Origem │ Estado │
|
||
│ MX │ @ │ MX │ mail… │ CF Lig │ ✅ aplicado │
|
||
│ SPF │ @ │ TXT │ v=spf1… │ CF Lig │ ✅ aplicado │
|
||
├───────────────────────────────────────────────────────────────┤
|
||
│ Checks públicos (dig): MX ✅ SPF ✅ DKIM ⚠ DMARC ✅ │
|
||
├───────────────────────────────────────────────────────────────┤
|
||
│ [Editar na Cloudflare ↗] [Verificação avançada] [Actualizar]│
|
||
└───────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 5.2 Console gerente `/admin` (Spec 035)
|
||
|
||
Secção **Domínio & DNS** — ver [domain-manager-console-ui.md §5.5](../035-ligbox-mail-bundles-foss-openpanel/domain-manager-console-ui.md#55-domínio--dns-dns-viewer).
|
||
|
||
Gerente vê read-only + link externo **se** tiver permissão (BYO: link CF cliente; Ligbox: mensagem «contacte suporte» ou link help).
|
||
|
||
### 5.3 Wizard passo DNS (cliente)
|
||
|
||
| Estado wizard | Painel lateral viewer |
|
||
|---------------|----------------------|
|
||
| Escolheu Ligbox | Preview `planned_records` + NS |
|
||
| Escolheu BYO | Registos actuais BYO + diff vs mail |
|
||
| Escolheu Registrador | `instructions` + verify |
|
||
|
||
---
|
||
|
||
## 6. Links «Editar aqui» (deep-link)
|
||
|
||
| Provider | Quem vê o botão | URL | Mecanismo |
|
||
|----------|-----------------|-----|-----------|
|
||
| Cloudflare Ligbox | staff `super_admin`, `ops_lead`, `devops`, `seo` | dash.cloudflare.com | Conta + zone_id de `dns/resolve` |
|
||
| Cloudflare BYO | gerente (zona dele) + staff | dash.cloudflare.com | Zona BYO |
|
||
| OpenPanel | staff + gerente hub | openpanel.ligbox.com.br | Autologin bridge 027 |
|
||
| Registrador externo | gerente | URL detectada ou genérica | `instructions.registrar_url` |
|
||
| Registro.br | gerente BR | https://registro.br | Manual |
|
||
|
||
**Desk:** botão abre **nova tab** — nunca iframe CF (CSP).
|
||
|
||
**Implementação Fase 1:** só link CF Ligbox (conta conhecida). Fase 2: OpenPanel autologin.
|
||
|
||
---
|
||
|
||
## 7. RBAC (Spec 027)
|
||
|
||
| Acção | Roles |
|
||
|-------|-------|
|
||
| `dns.viewer.read` — ver painel | super_admin, ops_lead, technician, noc, seo, devops, developer |
|
||
| Ver link Cloudflare Ligbox | super_admin, ops_lead, devops, seo |
|
||
| Ver link OpenPanel | super_admin, ops_lead, sales_admin, sales_support, seo |
|
||
| Gerente domínio `/admin` | Só domínio próprio; sem CF Ligbox interna |
|
||
| Agente A2/A3 (Desk) | Lê viewer API — sugere fixes, não edita |
|
||
|
||
Formalizar permissão `dns.viewer.read` em `data-model.md` 027 (backlog).
|
||
|
||
---
|
||
|
||
## 8. Persistência `dns_mode`
|
||
|
||
Gravar em:
|
||
|
||
| Store | Campo |
|
||
|-------|-------|
|
||
| Wizard sessão onboarding | `session.dns_mode`, `session.cf_account_id` |
|
||
| `bundle_entitlements` (035) | `dns_mode`, `dns_provider`, `cf_zone_id` |
|
||
| Desk `billing_accounts` / tenant meta | espelho read-only |
|
||
|
||
Resolver ordem:
|
||
|
||
1. Entitlements domínio activo
|
||
2. Sessão onboarding em curso
|
||
3. `GET dns/resolve/{domain}`
|
||
4. NS lookup público → `unknown`
|
||
|
||
---
|
||
|
||
## 9. Fases de implementação
|
||
|
||
| Fase | Entregável | Estado |
|
||
|------|------------|--------|
|
||
| **V0** | Desk CF only (`cloudflare_dns.py`) | ✅ Parcial |
|
||
| **V1** | `GET /api/v1/dns/viewer/{domain}` — CF + public + planned | ✅ VM122 2026-06-25 |
|
||
| **V1b** | Desk UI unificada (`dns-viewer.js`) | ✅ VM122 2026-06-25 |
|
||
| **V2** | OpenPanel BIND records no viewer | ✅ VM122 2026-06-25 |
|
||
| **V3** | Console `/admin/dominio` | ✅ VM123 2026-06-25 |
|
||
| **V4** | Wizard painel lateral + diff planned vs actual | 📋 patches no repo · deploy VM112 pendente (SSH) |
|
||
|
||
---
|
||
|
||
## 10. Critérios de aceitação
|
||
|
||
1. Staff abre domínio email Ligbox no Desk → vê **≥ MX, SPF, DKIM, DMARC, A mail** com origem «CF Ligbox».
|
||
2. Domínio **externo** (registrador) → viewer mostra **NS actuais** + registos **públicos** (`dig`), **não** preview Ligbox.
|
||
3. Domínio **Ligbox pré-NS** → viewer mostra **planned_records** + NS Cloudflare a configurar.
|
||
4. Botão «Editar na Cloudflare» visible para `ops_lead` → abre zona correcta (conta ibytera/ligit/itecnologys).
|
||
5. **Nenhum** botão «Guardar» / «Apagar registo» no viewer.
|
||
6. Gerente em `/admin` vê mesma tabela (domínio próprio) — Spec 035.
|
||
7. OpenPanel zone (Fase V2): registos BIND listados + link OpenPanel.
|
||
8. API responde ≤3s (cache 60s por domínio OK).
|
||
|
||
---
|
||
|
||
## 11. Código (monorepo + deploy)
|
||
|
||
| Caminho | Função | Deploy |
|
||
|---------|--------|--------|
|
||
| `projects/ops-desk/api/app/dns_viewer.py` | Orquestrador providers | VM122 API |
|
||
| `projects/ops-desk/api/app/cloudflare_dns.py` | Provider CF | VM122 API |
|
||
| `projects/ops-desk/api/app/openpanel_dns.py` | Provider BIND (`dig @10.10.10.123`) | VM122 API |
|
||
| `projects/ops-desk/api/app/domain_console_routes.py` | Proxy Console `/admin/dominio` | VM122 API |
|
||
| `projects/ops-desk/api/app/main.py` | Rotas viewer + `require_internal_or_user` | VM122 API |
|
||
| `projects/ops-desk/frontend/assets/dns-viewer.js` | Componente Desk | VM122 frontend |
|
||
| `specs/019-.../deploy/frontend/src/components/DnsViewerPanel.jsx` | Componente Console | VM123 |
|
||
| `specs/019-.../deploy/frontend/src/views/admin/AdminDominio.jsx` | Página `/admin/dominio` | VM123 |
|
||
| `deploy/vm112-wizard/onboarding-dns-viewer-v4.patch.py` | Endpoint wizard proxy Desk | VM112 backend |
|
||
| `deploy/vm112-wizard/frontend-dns-viewer-v4.patch.py` | Painel lateral step DNS | VM112 frontend |
|
||
| `deploy/vm112-wizard/DNS-VIEWER-V4.md` | Runbook deploy V4 | — |
|
||
|
||
**Nota deploy VM122:** código API corre em container Docker — após `rsync` ao host, copiar para o container (`docker cp …/main.py …_api_1:/app/app/`) ou rebuild imagem.
|
||
|
||
---
|
||
|
||
## 12. Documentos relacionados
|
||
|
||
| Doc | Relação |
|
||
|-----|---------|
|
||
| [spec.md](./spec.md) | Fluxo decisão DNS wizard |
|
||
| [cloudflare-zone-provision.md](../004-onboard-funnel-events/cloudflare-zone-provision.md) | provision-zone / apply |
|
||
| [domain-manager-console-ui.md §5.5](../035-ligbox-mail-bundles-foss-openpanel/domain-manager-console-ui.md) | UI gerente |
|
||
| [009 spec](../009-ops-audit-overview/spec.md) | Checks públicos |
|
||
| [027 spec](../027-desk-rbac-function-matrix/spec.md) | Deep-links CF / OP |
|
||
| [DNS-VIEWER-ROLLBACK](./deploy/DNS-VIEWER-ROLLBACK.md) | Rollback e feature flag |
|
||
| [DNS-VIEWER-EXEC-20260625](./deploy/DNS-VIEWER-EXEC-20260625.md) | Registo deploys e commits |
|
||
| [deploy/vm112-wizard/DNS-VIEWER-V4.md](../../../deploy/vm112-wizard/DNS-VIEWER-V4.md) | Runbook V4 wizard |
|
||
|
||
---
|
||
|
||
## 13. Decisões Roger (2026-06-25)
|
||
|
||
| # | Decisão |
|
||
|---|---------|
|
||
| D1 | Viewer é **read-only** — edição só via link externo |
|
||
| D2 | Caminho **Ligbox** → mostrar **planned/applied** Ligbox; **externo** → mostrar **actual** público |
|
||
| D3 | OpenPanel BIND **deve** aparecer no viewer (Fase V2) — gap operacional actual |
|
||
| D4 | Um endpoint unificado `dns/viewer` — não proliferar modais CF-only |
|
||
| D5 | Mesmo componente visual Desk + Console `/admin` (design system 035) |
|
||
|
||
---
|
||
|
||
## 14. Versionamento e rollback
|
||
|
||
**Obrigatório antes de cada deploy V1+:** backup + tag git + feature flag.
|
||
|
||
| Documento | Função |
|
||
|-----------|--------|
|
||
| [deploy/DNS-VIEWER-EXEC-20260625.md](./deploy/DNS-VIEWER-EXEC-20260625.md) | Checklist deploy, ficheiros, validação |
|
||
| [deploy/DNS-VIEWER-ROLLBACK.md](./deploy/DNS-VIEWER-ROLLBACK.md) | Rollback por fase (Desk / Wizard / Console) |
|
||
| [deploy/scripts/preflight-dns-viewer.sh](./deploy/scripts/preflight-dns-viewer.sh) | Backup VM122 (+ `--wizard` / `--console`) |
|
||
| [deploy/scripts/verify-dns-viewer.sh](./deploy/scripts/verify-dns-viewer.sh) | Smoke test pós-deploy ou pós-rollback |
|
||
| [deploy/scripts/rollback-dns-viewer.sh](./deploy/scripts/rollback-dns-viewer.sh) | Rollback automatizado VM122 |
|
||
|
||
### Feature flag VM122
|
||
|
||
```bash
|
||
DNS_VIEWER_ENABLED=1 # viewer activo
|
||
DNS_VIEWER_ENABLED=0 # fallback legado /dns/cloudflare/records + UI V0
|
||
```
|
||
|
||
### Tag git recomendada
|
||
|
||
```bash
|
||
git tag -a dns-viewer-pre-v1-YYYYMMDD -m "Antes DNS Viewer V1"
|
||
```
|
||
|
||
Entrega monorepo V1–V3 + patches V4: commit **`038fb8f`** (2026-06-25).
|
||
|
||
### Endpoint legado (mantido até cutover)
|
||
|
||
`GET /api/v1/dns/cloudflare/records` — **não remover** enquanto rollback não estiver validado em produção ≥7 dias.
|