Entrega read-only de apontamentos DNS (Cloudflare, OpenPanel BIND, público) no Desk e Console /admin/dominio, com spec, scripts de rollback e patches VM112 para painel lateral no passo DNS do onboarding (deploy wizard pendente). Co-authored-by: Cursor <cursoragent@cursor.com>
123 lines
4.9 KiB
Python
123 lines
4.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Spec 037 V4 — painel DNS read-only no passo DNS do wizard (App.jsx). Idempotente."""
|
|
from pathlib import Path
|
|
|
|
APP = Path("/opt/ligbox-wizard/frontend/src/App.jsx")
|
|
text = APP.read_text(encoding="utf-8")
|
|
|
|
if "dnsViewerData" in text:
|
|
print("App.jsx: dns viewer V4 already patched")
|
|
raise SystemExit(0)
|
|
|
|
text = text.replace(
|
|
" const [showAdvancedDns, setShowAdvancedDns] = useState(false)",
|
|
" const [showAdvancedDns, setShowAdvancedDns] = useState(false)\n"
|
|
" const [dnsViewerData, setDnsViewerData] = useState(null)\n"
|
|
" const [dnsViewerLoading, setDnsViewerLoading] = useState(false)",
|
|
)
|
|
|
|
LOAD_FN = """
|
|
async function refreshDnsViewer(dom) {
|
|
const d = (dom || domain || '').trim().toLowerCase()
|
|
if (!d || d.length < 3) return
|
|
setDnsViewerLoading(true)
|
|
try {
|
|
const data = await api(`/onboarding/dns/viewer/${d}`)
|
|
setDnsViewerData(data)
|
|
} catch {
|
|
setDnsViewerData(null)
|
|
} finally {
|
|
setDnsViewerLoading(false)
|
|
}
|
|
}
|
|
|
|
"""
|
|
|
|
if "refreshDnsViewer" not in text:
|
|
text = text.replace(" async function choosePortalDns() {", LOAD_FN + " async function choosePortalDns() {")
|
|
|
|
# Refresh when instructions load
|
|
text = text.replace(
|
|
" setDnsResolve(data.resolve || null)",
|
|
" setDnsResolve(data.resolve || null)\n refreshDnsViewer(dom)",
|
|
)
|
|
|
|
text = text.replace(
|
|
" setDnsChoice('portal')\n setError(null)\n startBusy('dns_zone')",
|
|
" setDnsChoice('portal')\n setError(null)\n refreshDnsViewer(domain)\n startBusy('dns_zone')",
|
|
)
|
|
|
|
text = text.replace(
|
|
" setDnsChoice('external')\n markActionDone('chooseExternalDns')",
|
|
" setDnsChoice('external')\n refreshDnsViewer(domain)\n markActionDone('chooseExternalDns')",
|
|
)
|
|
|
|
PANEL = """
|
|
{(dnsViewerData || dnsViewerLoading) && step === 1 && (
|
|
<aside className="dns-viewer-wizard-panel" style={{
|
|
marginTop: '1rem',
|
|
padding: '0.85rem 1rem',
|
|
borderRadius: '10px',
|
|
border: '1px solid #dbe4f4',
|
|
background: '#f8fbff',
|
|
}}>
|
|
<strong>Apontamentos DNS (visualização)</strong>
|
|
{dnsViewerLoading && <p className="sub">Carregando…</p>}
|
|
{!dnsViewerLoading && dnsViewerData && (
|
|
<>
|
|
<p className="sub" style={{ margin: '0.35rem 0' }}>
|
|
{dnsViewerData.mode_message || dnsViewerData.mode_label}
|
|
</p>
|
|
<p className="sub">
|
|
Modo: <code>{dnsViewerData.mode_label || dnsViewerData.dns_mode}</code>
|
|
{' · '}
|
|
{dnsViewerData.summary?.total || 0} registo(s)
|
|
</p>
|
|
<table className="data-table" style={{ fontSize: '0.82rem', marginTop: '0.5rem' }}>
|
|
<thead>
|
|
<tr><th>Tipo</th><th>Nome</th><th>Conteúdo</th></tr>
|
|
</thead>
|
|
<tbody>
|
|
{(dnsViewerData.records || []).slice(0, 12).map((r, i) => (
|
|
<tr key={i}>
|
|
<td>{r.type}</td>
|
|
<td><code>{r.name}</code></td>
|
|
<td style={{ wordBreak: 'break-all' }}>{r.content}</td>
|
|
</tr>
|
|
))}
|
|
{!(dnsViewerData.records || []).length && (
|
|
<tr><td colSpan={3}>Sem registos — escolha o caminho DNS acima.</td></tr>
|
|
)}
|
|
</tbody>
|
|
</table>
|
|
{(dnsViewerData.planned_records || []).length > 0 && (
|
|
<>
|
|
<p className="sub" style={{ marginTop: '0.65rem' }}><strong>Serão aplicados (Ligbox):</strong></p>
|
|
<table className="data-table" style={{ fontSize: '0.82rem' }}>
|
|
<tbody>
|
|
{(dnsViewerData.planned_records || []).slice(0, 8).map((r, i) => (
|
|
<tr key={`p-${i}`}>
|
|
<td>{r.type}</td>
|
|
<td><code>{r.name}</code></td>
|
|
<td>{r.content}</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</>
|
|
)}
|
|
</>
|
|
)}
|
|
</aside>
|
|
)}
|
|
|
|
"""
|
|
|
|
if "dns-viewer-wizard-panel" not in text:
|
|
text = text.replace(
|
|
" {dnsChoice === 'byo' && showAdvancedDns && (",
|
|
PANEL + " {dnsChoice === 'byo' && showAdvancedDns && (",
|
|
)
|
|
|
|
APP.write_text(text, encoding="utf-8")
|
|
print("App.jsx: DNS viewer V4 panel OK")
|