#!/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 = """ """ NEW_GRID = """ """ text = text.replace(OLD_GRID, NEW_GRID) BYO_PANEL = """ {dnsChoice === 'byo' && showAdvancedDns && (
Sua conta Cloudflare (BYO)

Crie um API Token com permissão Zone DNS Edit apenas na zona{' '} {domain}.

)} """ if "dnsChoice === 'byo'" not in text: text = text.replace( " {dnsChoice === 'external' && showAdvancedDns && (", BYO_PANEL + " {dnsChoice === 'external' && showAdvancedDns && (", ) # Simple mode message when not matched SIMPLE_MSG = """ {!dnsResolve?.matched && !portalDnsApplied && (

Este domínio não está nas contas Cloudflare Ligbox. Use{' '} Minha conta Cloudflare ou{' '} Registrador / DNS externo (botão técnico abaixo).

)} """ if "não está nas contas Cloudflare Ligbox" not in text: text = text.replace( " {portalDnsApplied && (", SIMPLE_MSG + " {portalDnsApplied && (", ) APP.write_text(text, encoding="utf-8") print("App.jsx patched OK")