← back to Coming Soon Template
rewrite CF zone setup in Node — real error capture (token lacks zone:create)
78446c12c7e682b4b57d66cc4d63b4eb8ffb0fd3 · 2026-05-18 11:49:47 -0700 · SteveStudio2
Files touched
A data/zone-setup.jsonA scripts/cf_setup.js
Diff
commit 78446c12c7e682b4b57d66cc4d63b4eb8ffb0fd3
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Mon May 18 11:49:47 2026 -0700
rewrite CF zone setup in Node — real error capture (token lacks zone:create)
---
data/zone-setup.json | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++
scripts/cf_setup.js | 97 +++++++++++++++++++++++++++++++++
2 files changed, 246 insertions(+)
diff --git a/data/zone-setup.json b/data/zone-setup.json
new file mode 100644
index 0000000..d2e7e55
--- /dev/null
+++ b/data/zone-setup.json
@@ -0,0 +1,149 @@
+[
+ {
+ "domain": "abramsos.com",
+ "status": "create_failed",
+ "http": 403,
+ "error": "0:Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account",
+ "live_mx": ""
+ },
+ {
+ "domain": "abramsintel.com",
+ "status": "create_failed",
+ "http": 403,
+ "error": "0:Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account",
+ "live_mx": ""
+ },
+ {
+ "domain": "abramsintelligence.com",
+ "status": "create_failed",
+ "http": 403,
+ "error": "0:Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account",
+ "live_mx": ""
+ },
+ {
+ "domain": "abramsspace.com",
+ "status": "create_failed",
+ "http": 403,
+ "error": "0:Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account",
+ "live_mx": ""
+ },
+ {
+ "domain": "abramsterminal.com",
+ "status": "create_failed",
+ "http": 403,
+ "error": "0:Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account",
+ "live_mx": ""
+ },
+ {
+ "domain": "abramsvc.com",
+ "status": "create_failed",
+ "http": 403,
+ "error": "0:Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account",
+ "live_mx": ""
+ },
+ {
+ "domain": "abramsindustries.com",
+ "status": "create_failed",
+ "http": 403,
+ "error": "0:Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account",
+ "live_mx": ""
+ },
+ {
+ "domain": "abramsprotection.com",
+ "status": "create_failed",
+ "http": 403,
+ "error": "0:Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account",
+ "live_mx": ""
+ },
+ {
+ "domain": "abramscivic.com",
+ "status": "create_failed",
+ "http": 403,
+ "error": "0:Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account",
+ "live_mx": ""
+ },
+ {
+ "domain": "abramslive.com",
+ "status": "create_failed",
+ "http": 403,
+ "error": "0:Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account",
+ "live_mx": ""
+ },
+ {
+ "domain": "abramsatlas.com",
+ "status": "create_failed",
+ "http": 403,
+ "error": "0:Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account",
+ "live_mx": ""
+ },
+ {
+ "domain": "abramsdirectory.com",
+ "status": "create_failed",
+ "http": 403,
+ "error": "0:Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account",
+ "live_mx": ""
+ },
+ {
+ "domain": "abramsguide.com",
+ "status": "create_failed",
+ "http": 403,
+ "error": "0:Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account",
+ "live_mx": ""
+ },
+ {
+ "domain": "abramsindex.com",
+ "status": "create_failed",
+ "http": 403,
+ "error": "0:Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account",
+ "live_mx": ""
+ },
+ {
+ "domain": "abramslocal.com",
+ "status": "create_failed",
+ "http": 403,
+ "error": "0:Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account",
+ "live_mx": ""
+ },
+ {
+ "domain": "abramsmaps.com",
+ "status": "create_failed",
+ "http": 403,
+ "error": "0:Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account",
+ "live_mx": ""
+ },
+ {
+ "domain": "abramsmarkets.com",
+ "status": "create_failed",
+ "http": 403,
+ "error": "0:Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account",
+ "live_mx": ""
+ },
+ {
+ "domain": "818butler.com",
+ "status": "create_failed",
+ "http": 403,
+ "error": "0:Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account",
+ "live_mx": ""
+ },
+ {
+ "domain": "beverlyhillsbutler.com",
+ "status": "create_failed",
+ "http": 403,
+ "error": "0:Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account",
+ "live_mx": ""
+ },
+ {
+ "domain": "bhbutler.com",
+ "status": "create_failed",
+ "http": 403,
+ "error": "0:Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account",
+ "live_mx": ""
+ },
+ {
+ "domain": "boulevardbutler.com",
+ "status": "create_failed",
+ "http": 403,
+ "error": "0:Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account",
+ "live_mx": ""
+ }
+]
\ No newline at end of file
diff --git a/scripts/cf_setup.js b/scripts/cf_setup.js
new file mode 100644
index 0000000..71c7c78
--- /dev/null
+++ b/scripts/cf_setup.js
@@ -0,0 +1,97 @@
+#!/usr/bin/env node
+/* Cloudflare zone setup for the 21 abrams/butler coming-soon domains.
+ * Replaces the broken bash+python scripts (their python one-liners had
+ * backslash-escaped quotes inside f-strings -> SyntaxError, swallowed
+ * every real CF error as "").
+ *
+ * Usage:
+ * node cf_setup.js # create CF zones, capture real errors
+ * node cf_setup.js --ns-plan # after zones exist: print GoDaddy NS-flip plan
+ *
+ * Idempotent: an existing zone is reused, never duplicated.
+ * Writes data/zone-setup.json (domain, status, zone_id, cf_ns[], live_mx).
+ */
+'use strict';
+const fs = require('fs');
+const os = require('os');
+const path = require('path');
+const { execSync } = require('child_process');
+
+const ENV = path.join(os.homedir(), 'Projects/secrets-manager/.env');
+const ROOT = path.join(os.homedir(), 'Projects/coming-soon-template');
+const OUT = path.join(ROOT, 'data/zone-setup.json');
+
+function envVal(key) {
+ const line = fs.readFileSync(ENV, 'utf8').split('\n').find(l => l.startsWith(key + '='));
+ return line ? line.slice(key.length + 1).trim() : '';
+}
+const TOK = envVal('CLOUDFLARE_API_TOKEN');
+const ACCT = envVal('CLOUDFLARE_ACCOUNT_ID');
+if (!TOK || !ACCT) { console.error('missing CLOUDFLARE_API_TOKEN / CLOUDFLARE_ACCOUNT_ID in', ENV); process.exit(1); }
+
+const manifest = JSON.parse(fs.readFileSync(path.join(ROOT, 'manifest.json'), 'utf8'));
+const domains = manifest.domains.map(d => d.domain);
+
+const CF = 'https://api.cloudflare.com/client/v4';
+const HEADERS = { 'Authorization': `Bearer ${TOK}`, 'Content-Type': 'application/json' };
+
+async function cf(method, urlPath, body) {
+ const res = await fetch(CF + urlPath, {
+ method, headers: HEADERS, body: body ? JSON.stringify(body) : undefined,
+ });
+ let json;
+ try { json = await res.json(); } catch { json = { success: false, errors: [{ code: res.status, message: 'non-JSON response' }] }; }
+ return { http: res.status, json };
+}
+
+function digMX(domain) {
+ try {
+ return execSync(`/usr/bin/dig +short MX ${domain}`, { encoding: 'utf8' })
+ .trim().split('\n').filter(Boolean).slice(0, 3).join('; ');
+ } catch { return ''; }
+}
+
+async function nsPlan() {
+ if (!fs.existsSync(OUT)) { console.error('run `node cf_setup.js` first to create zones'); process.exit(1); }
+ const zones = JSON.parse(fs.readFileSync(OUT, 'utf8'));
+ console.log('=== GoDaddy NS-flip plan (domains with live MX are HELD BACK) ===\n');
+ for (const z of zones) {
+ if (!['created', 'already_exists'].includes(z.status)) { console.log(`SKIP ${z.domain} — zone status: ${z.status}`); continue; }
+ if (z.live_mx) { console.log(`HOLD ${z.domain} — live MX present (${z.live_mx})`); continue; }
+ console.log(`FLIP ${z.domain} -> ${(z.cf_ns || []).join(', ')}`);
+ }
+}
+
+async function main() {
+ if (process.argv.includes('--ns-plan')) return nsPlan();
+
+ const results = [];
+ for (const domain of domains) {
+ // existing zone?
+ const found = await cf('GET', `/zones?name=${domain}&account.id=${ACCT}`);
+ const existing = (found.json.result || [])[0];
+ let rec;
+ if (existing) {
+ rec = { domain, status: 'already_exists', zone_id: existing.id, cf_ns: existing.name_servers || [] };
+ console.log(`OK ${domain} [already_exists] ns=${(rec.cf_ns).join(',')}`);
+ } else {
+ const created = await cf('POST', '/zones', { account: { id: ACCT }, name: domain, type: 'full' });
+ if (created.json.success) {
+ const r = created.json.result;
+ rec = { domain, status: 'created', zone_id: r.id, cf_ns: r.name_servers || [] };
+ console.log(`OK ${domain} [created] ns=${(rec.cf_ns).join(',')}`);
+ } else {
+ const errs = (created.json.errors || []).map(e => `${e.code}:${e.message}`).join(' | ');
+ rec = { domain, status: 'create_failed', http: created.http, error: errs || '(no error detail)' };
+ console.log(`FAIL ${domain} [HTTP ${created.http}] ${rec.error}`);
+ }
+ }
+ rec.live_mx = digMX(domain);
+ if (rec.live_mx) console.log(` ^ live MX: ${rec.live_mx}`);
+ results.push(rec);
+ }
+ fs.writeFileSync(OUT, JSON.stringify(results, null, 2));
+ const ok = results.filter(r => r.status !== 'create_failed').length;
+ console.log(`\n--- ${ok}/${results.length} zones ready --- wrote ${OUT}`);
+}
+main().catch(e => { console.error('FATAL', e); process.exit(1); });
← 5341e99 add CF zone-create + GoDaddy NS-flip scripts for 21 abrams/b
·
back to Coming Soon Template
·
all 21 CF zones created 41007d9 →