← 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.');
})();