#!/usr/bin/env python3
"""Patch App.jsx Spec 037 — multi-conta CF + BYO."""
from pathlib import Path
APP = Path("/opt/ligbox-wizard/frontend/src/App.jsx")
text = APP.read_text(encoding="utf-8")
if "chooseByoDns" in text and "dnsResolve" in text:
print("App.jsx already patched")
raise SystemExit(0)
text = text.replace(
" choosePortalDns: false,\n chooseExternalDns: false,",
" choosePortalDns: false,\n chooseByoDns: false,\n chooseExternalDns: false,",
)
text = text.replace(
" const [showAdvancedDns, setShowAdvancedDns] = useState(false)",
" const [showAdvancedDns, setShowAdvancedDns] = useState(false)\n const [dnsResolve, setDnsResolve] = useState(null)\n const [byoToken, setByoToken] = useState('')",
)
OLD_CHOOSE_PORTAL = """ async function choosePortalDns() {
setDnsChoice('portal')
setError(null)
startBusy('dns_zone')
try {
const guide = await api('/onboarding/dns/cloudflare/provision-zone', {
method: 'POST',
body: JSON.stringify({ domain }),
})
setPortalGuide(guide)
const cf = guide.status || (await refreshCfStatus())
setCfStatus(cf)
markActionDone('choosePortalDns')
if (guide.verification) {
setVerification(guide.verification)
setResult({ type: 'dns', data: { verification: guide.verification } })
markActionDone('verifyZone')
markActionDone('applyPortalDns')
} else if (cf?.zone_in_account && cf?.can_apply_mail_records) {
markActionDone('verifyZone')
const data = await api('/onboarding/dns/cloudflare/apply', {
method: 'POST',
body: JSON.stringify({
domain,
mail_aliases: sanitizeMailAliases(mailAliasInputs, domain),
}),
})
setVerification(data.verification)
setResult({ type: 'dns', data })
markActionDone('applyPortalDns')
}
} catch (e) {
setError(e.message)
setDnsChoice(null)
setActionsDone((prev) => ({ ...prev, choosePortalDns: false }))
} finally {
stopBusy()
}
}"""
NEW_CHOOSE_PORTAL = """ async function choosePortalDns() {
setDnsChoice('portal')
setError(null)
startBusy('dns_zone')
try {
const resolved = dnsResolve || (await api(`/onboarding/dns/resolve/${domain}`))
setDnsResolve(resolved)
if (!resolved.matched) {
throw new Error(
resolved.message ||
'Domínio não está nas contas Ligbox (ligit, itecnologys, ibytera).',
)
}
const cf = await refreshCfStatus()
setCfStatus(cf)
markActionDone('choosePortalDns')
markActionDone('verifyZone')
const data = await api('/onboarding/dns/cloudflare/apply', {
method: 'POST',
body: JSON.stringify({
domain,
zone_id: resolved.zone_id,
mail_aliases: sanitizeMailAliases(mailAliasInputs, domain),
}),
})
setPortalGuide({
message: resolved.message,
nameservers: cf?.nameservers || [],
status: cf,
ligbox_account_id: resolved.account_id,
})
setVerification(data.verification)
setResult({ type: 'dns', data })
markActionDone('applyPortalDns')
} catch (e) {
setError(e.message)
setDnsChoice(null)
setActionsDone((prev) => ({ ...prev, choosePortalDns: false }))
} finally {
stopBusy()
}
}
async function chooseByoDns() {
setDnsChoice('byo')
setError(null)
markActionDone('chooseByoDns')
setShowAdvancedDns(true)
}
async function connectByoAndApply() {
const token = byoToken.trim()
if (!token) {
setError('Cole o API Token da sua conta Cloudflare (DNS Edit na zona).')
return
}
setError(null)
startBusy('dns_byo')
try {
const conn = await api('/onboarding/dns/cloudflare/connect-custom', {
method: 'POST',
body: JSON.stringify({ domain, api_token: token }),
})
setDnsResolve({ ...conn, matched: true, dns_mode: 'byo_cloudflare' })
markActionDone('chooseByoDns')
const data = await api('/onboarding/dns/cloudflare/apply', {
method: 'POST',
body: JSON.stringify({
domain,
zone_id: conn.zone_id,
mail_aliases: sanitizeMailAliases(mailAliasInputs, domain),
}),
})
setVerification(data.verification)
setResult({ type: 'dns', data })
markActionDone('applyPortalDns')
} catch (e) {
setError(e.message)
setActionsDone((prev) => ({ ...prev, chooseByoDns: false, applyPortalDns: false }))
} finally {
stopBusy()
}
}"""
text = text.replace(OLD_CHOOSE_PORTAL, NEW_CHOOSE_PORTAL)
text = text.replace(
""" const DNS_ACTION_KEYS = {
choosePortalDns: false,
chooseExternalDns: false,""",
""" const DNS_ACTION_KEYS = {
choosePortalDns: false,
chooseByoDns: false,
chooseExternalDns: false,""",
)
text = text.replace(
" if (dnsChoice === 'portal') markActionDone('choosePortalDns')\n if (dnsChoice === 'external') markActionDone('chooseExternalDns')",
" if (dnsChoice === 'portal') markActionDone('choosePortalDns')\n if (dnsChoice === 'byo') markActionDone('chooseByoDns')\n if (dnsChoice === 'external') markActionDone('chooseExternalDns')",
)
text = text.replace(
" const data = await api(`/onboarding/dns/instructions/${dom}`)\n setInstructions(data)\n setCfStatus(data.cloudflare)",
" const data = await api(`/onboarding/dns/instructions/${dom}`)\n setInstructions(data)\n setCfStatus(data.cloudflare)\n setDnsResolve(data.resolve || null)",
)
text = text.replace(
" if (step !== 1 || !instructions || showAdvancedDns || dnsChoice === 'external' || asmMode) return",
" if (step !== 1 || !instructions || showAdvancedDns || dnsChoice === 'external' || dnsChoice === 'byo' || asmMode) return",
)
text = text.replace(
" if (portalDnsApplied || loading || autoDnsAttempted.current) return\n autoDnsAttempted.current = true",
" if (portalDnsApplied || loading || autoDnsAttempted.current) return\n const resolve = instructions?.resolve || dnsResolve\n if (resolve && !resolve.matched) return\n autoDnsAttempted.current = true",
)
text = text.replace(
" dns_mode: dnsChoice === 'portal' ? 'Cloudflare Ligbox' : 'Provedor externo',",
" dns_mode:\n dnsChoice === 'portal'\n ? dnsResolve?.dns_mode || cfStatus?.dns_mode || 'ligbox_cf'\n : dnsChoice === 'byo'\n ? 'byo_cloudflare'\n : 'external_registrar',",
)
# Advanced mode choice grid — add BYO button and update portal label
OLD_GRID = """
Crie um API Token com permissão Zone DNS Edit apenas na zona{' '}
{domain}.
Este domínio não está nas contas Cloudflare Ligbox. Use{' '} Minha conta Cloudflare ou{' '} Registrador / DNS externo (botão técnico abaixo).