← back to Coming Soon Template

godaddy_flip.js

95 lines

// Playwright script: drives Chrome through GoDaddy's nameserver-change UI for
// all 21 abrams/butler domains. Reads target nameservers from cf_nameservers.json.
//
// USAGE (first run logs you in, subsequent runs are silent):
//   cd ~/Projects/coming-soon-template
//   npm i -D playwright   # one-time
//   npx playwright install chromium   # one-time
//   node godaddy_flip.js
//
// First run: a Chromium window opens at GoDaddy. Log in manually. The script
// waits for you to land on the domains page, then drives the rest.
// Profile state is persisted at ./.playwright-godaddy so future runs skip login.
//
// PREREQUISITE: run ./add_zones.sh first — it produces cf_nameservers.json
// (one entry per domain with the 2 CF NS values to enter).

const { chromium } = require('playwright');
const fs = require('fs');
const path = require('path');

const NS_FILE = path.join(__dirname, 'cf_nameservers.json');
if (!fs.existsSync(NS_FILE)) {
  console.error(`✗ ${NS_FILE} not found. Run ./add_zones.sh first (with --emit-json flag) or write cf_nameservers.json manually.`);
  console.error('  Format: { "abramsindex.com": ["lia.ns.cloudflare.com", "xander.ns.cloudflare.com"], ... }');
  process.exit(1);
}
const targets = JSON.parse(fs.readFileSync(NS_FILE, 'utf8'));

(async () => {
  const userData = path.join(__dirname, '.playwright-godaddy');
  const ctx = await chromium.launchPersistentContext(userData, {
    headless: false,
    viewport: { width: 1280, height: 900 },
  });
  const page = ctx.pages()[0] || await ctx.newPage();

  console.log('→ opening GoDaddy domain control center');
  await page.goto('https://dcc.godaddy.com/control/portfolio', { waitUntil: 'domcontentloaded' });

  // First-run gate: wait for the user to log in if needed.
  console.log('   if you are not logged in, log in now. Waiting for the domain list to load...');
  await page.waitForURL(/portfolio|domains/, { timeout: 10 * 60 * 1000 });
  await page.waitForLoadState('networkidle').catch(() => {});

  let ok = 0, fail = 0;
  for (const [domain, ns] of Object.entries(targets)) {
    if (!Array.isArray(ns) || ns.length < 2) {
      console.log(`  ! ${domain}: skipping — need exactly 2 nameservers, got ${JSON.stringify(ns)}`);
      continue;
    }
    console.log(`→ ${domain}: target NS = ${ns.join(', ')}`);
    try {
      // Open the domain detail page directly
      await page.goto(`https://dcc.godaddy.com/control/portfolio/${domain}/settings?tab=dns`, { waitUntil: 'domcontentloaded' });
      await page.waitForLoadState('networkidle').catch(() => {});

      // GoDaddy's DOM changes often; selectors are intentionally flexible.
      // We look for a "Nameservers" section with a Change / Edit button.
      const changeBtn = page.locator('button:has-text("Change Nameservers"), button:has-text("Change"), a:has-text("Change Nameservers")').first();
      await changeBtn.click({ timeout: 30000 });

      // Choose "Enter my own nameservers"
      const ownNs = page.locator('label:has-text("own nameservers"), input[value="custom"], input[value="CUSTOM"]').first();
      await ownNs.click({ timeout: 10000 }).catch(() => {});

      // Fill 2 NS inputs
      const nsInputs = page.locator('input[type="text"]').filter({ hasText: /./ }).or(page.locator('input[name*="nameserver"], input[name*="ns"]'));
      const count = await nsInputs.count();
      const fillTargets = [];
      for (let i = 0; i < Math.min(count, 4); i++) fillTargets.push(nsInputs.nth(i));
      // Fill the first two
      await fillTargets[0].fill(ns[0]);
      await fillTargets[1].fill(ns[1]);

      // Save
      const saveBtn = page.locator('button:has-text("Save"), button:has-text("Continue"), button[type="submit"]').first();
      await saveBtn.click({ timeout: 10000 });

      // Confirm dialog if present
      const confirmBtn = page.locator('button:has-text("Continue"), button:has-text("Yes"), button:has-text("OK"), button:has-text("Confirm")').first();
      await confirmBtn.click({ timeout: 5000 }).catch(() => {});

      console.log(`   ✓ ${domain}: nameservers updated`);
      ok++;
      await page.waitForTimeout(1500);
    } catch (e) {
      console.error(`   ✗ ${domain}: ${e.message.split('\n')[0]}`);
      fail++;
    }
  }

  console.log(`\nsummary: ${ok} updated, ${fail} failed`);
  console.log('\nLeaving Chromium open so you can spot-check. Close it when done.');
})();