[object Object]

← 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

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 →