ligbox-ops-platform/specs/037-dns-multi-cloudflare-orchestration/dns-viewer.md
Ligbox Spec Hub c1881f58e6 chore: sync Console SSO, DNS viewer, specs e infra docs pendentes
Inclui console handoff Desk↔Console (Spec 019), melhorias DNS Viewer (037),
OpenPanel/Nextcloud/VM116 deploy notes, contracts stack e sidebar actualizado.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-25 20:10:17 +00:00

22 KiB
Raw Permalink Blame History

Spec 037-DNS-VIEWER — Painel DNS read-only unificado

Criado: 2026-06-25
Solicitado por: Roger
Status: 🏗️ V1V3 em produção (VM122/VM123) · V4 patches prontos · deploy VM112 pendente
Prioridade: P1
Depende de: spec.md · 004 cloudflare-zone-provision · 009 · 028 · 035 §5.5 · 027 RBAC


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.

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 Staff: https://admin.openpanel.ligbox.com.br/domains/dns?domain={domain} · Cliente: 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)

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):

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:

{
  "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):

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.


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.

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

Regra Roger (2026-06-25) — staff vs cliente

Princípio Detalhe
Visualização DNS Read-only na Console/Desk — perfeita para suporte (apontamentos, faltas, erros, origem)
Login Console /admin/* Uma vez no shell Admin (AdminShell) — não por página. Handoff Desk→Console (POST /auth/console-handoff + ?desk_handoff=) evita re-login ao navegar setups do Wizard
Desk → Console Botão «Abrir no Console ↗» no painel DNS (staff com can_read_dns_viewer)
Suporte NUNCA Painel cliente OpenPanel (openpanel.ligbox.com.br/domains/…/dns) — dá 500 e é vista errada
Suporte SEMPRE OpenAdmin (admin.openpanel.ligbox.com.br/domains/dns?domain=…) ou Cloudflare conta partner Ligbox
Gerente domínio (futuro) Links audience: client — painel cliente / CF BYO própria; sem OpenAdmin

Campo audience em cada link: staff | client | all.

Provider Quem vê URL staff URL cliente
Cloudflare Ligbox staff dash.cloudflare.com/.../zones/{zone_id}/dns — (gerente: «contacte suporte»)
Cloudflare BYO gerente + staff dash CF zona BYO idem
OpenPanel BIND staff https://admin.openpanel.ligbox.com.br/domains/dns?domain={domain} https://openpanel.ligbox.com.br/domains/{domain}/dns
Registrador todos registro.br / detectado idem

Desk / Console: botão abre nova tab — nunca iframe.

Implementação: dns_viewer._edit_links() + permissions.can_open_openpanel_admin_link() vs can_open_openpanel_client_link().


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 OpenAdmin (staff) super_admin, ops_lead, devops, sales_admin, sales_support, seo, technician, noc
Ver link OpenPanel cliente domain_manager (futuro) — proibido para staff suporte
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 Fluxo decisão DNS wizard
cloudflare-zone-provision.md provision-zone / apply
domain-manager-console-ui.md §5.5 UI gerente
009 spec Checks públicos
027 spec Deep-links CF / OP
DNS-VIEWER-ROLLBACK Rollback e feature flag
DNS-VIEWER-EXEC-20260625 Registo deploys e commits
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 Checklist deploy, ficheiros, validação
deploy/DNS-VIEWER-ROLLBACK.md Rollback por fase (Desk / Wizard / Console)
deploy/scripts/preflight-dns-viewer.sh Backup VM122 (+ --wizard / --console)
deploy/scripts/verify-dns-viewer.sh Smoke test pós-deploy ou pós-rollback
deploy/scripts/rollback-dns-viewer.sh Rollback automatizado VM122

Feature flag VM122

DNS_VIEWER_ENABLED=1   # viewer activo
DNS_VIEWER_ENABLED=0   # fallback legado /dns/cloudflare/records + UI V0

Tag git recomendada

git tag -a dns-viewer-pre-v1-YYYYMMDD -m "Antes DNS Viewer V1"

Entrega monorepo V1V3 + patches V4: commit 038fb8f (2026-06-25).


Incidente 2026-06-25 — Console «Failed to fetch» no login DNS

Sintoma

console.ligbox.com.br/admin/dominio → Login Desk → Failed to fetch (abaixo do botão Entrar).
«Sem dados DNS.» aparece em baixo — não é erro, é estado vazio antes de login + consulta.

Causa

O frontend Console (api.js) enviava credentials: 'include' nas chamadas cross-origin para https://api.ops.ligbox.com.br.
A API Desk responde Access-Control-Allow-Origin: *. Browsers bloqueiam pedidos credenciados com wildcard → TypeError: Failed to fetch.

Correção

  1. Remover credentials: 'include' do apiGet / apiPost — auth usa Bearer JWT em localStorage, não cookies.
  2. API Desk: CORS_ORIGINS explícito inclui https://console.ligbox.com.br (deixa de depender só de *).
  3. Console nginx: index.html com Cache-Control: no-cache para forçar bundle novo após deploy.
  4. Mensagens de erro amigáveis quando a rede/CORS falha; sessão expirada (401) pede re-login.
Ficheiro Alteração
specs/019-.../deploy/frontend/src/lib/api.js Sem credentials; wrapNetworkError
specs/019-.../deploy/frontend/src/views/admin/AdminDominio.jsx 401 → clear session
projects/ops-desk/api/app/main.py CORS_ORIGINS
specs/019-.../deploy/nginx/default.conf no-cache em index.html

Consulta DNS (ex.: diarissima.com)

Após fix, GET /api/v1/domain-console/dns/viewer/diarissima.com retorna 200 (~3s) com registos OpenPanel BIND — validado 2026-06-25.


Sintoma

Staff em Console → diarissima.comEditar no OpenPanelopenpanel.ligbox.com.br/domains/diarissima.com/dnsERROR 500 (vista cliente, não OpenAdmin).

Causa

edit_links apontava OPENPANEL_URL (painel utilizador) em vez de OPENADMIN_URL (DNS Zone Editor staff).

Correção

Antes Depois (staff)
openpanel.ligbox.com.br/domains/{d}/dns admin.openpanel.ligbox.com.br/domains/dns?domain={d}
Label «Editar no OpenPanel» «Editar DNS (OpenAdmin)»
provider: openpanel_bind provider: openpanel_admin, audience: staff

Gerente futuro (domain_manager) receberá link cliente separado — staff nunca.

Ficheiros

openpanel_dns.py, dns_viewer.py, permissions.py, domain_console_routes.py, dns-viewer.md §6.

Credenciais

  • Login Desk exige utilizador Ops Desk (ex.: Roger, root) — não ligboxadmin (conta OpenAdmin/hosting).
  • Após login: introduzir domínio e Consultar DNS.

Endpoint legado (mantido até cutover)

GET /api/v1/dns/cloudflare/recordsnão remover enquanto rollback não estiver validado em produção ≥7 dias.