EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
bulk-update-nameservers.js
Go to the documentation of this file.
1#!/usr/bin/env node
2/**
3 * Bulk Nameserver Updater
4 *
5 * This script updates nameservers for multiple domains at once.
6 * Supports Moniker and eNom registrars.
7 *
8 * Usage:
9 * node scripts/bulk-update-nameservers.js --registrar moniker --file domains.json --nameservers "ns1.example.com,ns2.example.com"
10 * node scripts/bulk-update-nameservers.js --registrar moniker --domain example.com --cloudflare
11 * node scripts/bulk-update-nameservers.js --registrar enom --file domains.txt --cloudflare
12 *
13 * Options:
14 * --registrar <name> Registrar API to use (moniker or enom)
15 * --domain <domain> Single domain to update
16 * --file <path> File with list of domains (JSON array or newline-separated)
17 * --nameservers <ns1,ns2> Comma-separated list of nameservers
18 * --cloudflare Use Cloudflare nameservers (auto-fetches from API)
19 * --dry-run Test mode - show what would be updated without making changes
20 * --delay <ms> Delay between API calls in milliseconds (default: 1000)
21 */
22
23require('dotenv').config();
24const fs = require('fs').promises;
25const path = require('path');
26const moniker = require('../services/moniker');
27const enom = require('../services/enom');
28const { createZone, getZone } = require('../services/cloudflare');
29
30// Parse command line arguments
31/**
32 *
33 */
34function parseArgs() {
35 const args = process.argv.slice(2);
36 const options = {
37 registrar: null,
38 domain: null,
39 file: null,
40 nameservers: [],
41 cloudflare: false,
42 dryRun: false,
43 delay: 1000
44 };
45
46 for (let i = 0; i < args.length; i++) {
47 const arg = args[i];
48 const next = args[i + 1];
49
50 switch (arg) {
51 case '--registrar':
52 options.registrar = next;
53 i++;
54 break;
55 case '--domain':
56 options.domain = next;
57 i++;
58 break;
59 case '--file':
60 options.file = next;
61 i++;
62 break;
63 case '--nameservers':
64 options.nameservers = next.split(',').map(ns => ns.trim());
65 i++;
66 break;
67 case '--cloudflare':
68 options.cloudflare = true;
69 break;
70 case '--dry-run':
71 options.dryRun = true;
72 break;
73 case '--delay':
74 options.delay = parseInt(next, 10);
75 i++;
76 break;
77 case '--help':
78 case '-h':
79 printHelp();
80 process.exit(0);
81 break;
82 }
83 }
84
85 return options;
86}
87
88/**
89 *
90 */
91function printHelp() {
92 console.log(`
93Bulk Nameserver Updater
94=======================
95
96Update nameservers for multiple domains across different registrars.
97
98Usage:
99 node scripts/bulk-update-nameservers.js [options]
100
101Options:
102 --registrar <name> Required. Registrar API (moniker or enom)
103 --domain <domain> Single domain to update
104 --file <path> File with domains (JSON or text)
105 --nameservers <ns1,ns2> Comma-separated nameservers
106 --cloudflare Use Cloudflare nameservers
107 --dry-run Test without making changes
108 --delay <ms> Delay between calls (default: 1000ms)
109 --help, -h Show this help
110
111Examples:
112 # Update single domain to Cloudflare
113 node scripts/bulk-update-nameservers.js --registrar moniker --domain example.com --cloudflare
114
115 # Update domains from file to custom nameservers
116 node scripts/bulk-update-nameservers.js --registrar enom --file domains.txt --nameservers "ns1.host.com,ns2.host.com"
117
118 # Dry run to see what would change
119 node scripts/bulk-update-nameservers.js --registrar moniker --file domains.json --cloudflare --dry-run
120
121File Formats:
122 - JSON: ["domain1.com", "domain2.com", "domain3.com"]
123 - Text: One domain per line
124 `);
125}
126
127/**
128 * Load domains from file
129 * @param filePath
130 */
131async function loadDomainsFromFile(filePath) {
132 const content = await fs.readFile(filePath, 'utf8');
133 const ext = path.extname(filePath).toLowerCase();
134
135 if (ext === '.json') {
136 const data = JSON.parse(content);
137 // Handle both array and object with domains property
138 return Array.isArray(data) ? data : data.domains || [];
139 } else {
140 // Text file - one domain per line
141 return content.split('\n')
142 .map(line => line.trim())
143 .filter(line => line && !line.startsWith('#'));
144 }
145}
146
147/**
148 * Get Cloudflare nameservers for a domain
149 * @param domain
150 */
151async function getCloudflareNameservers(domain) {
152 try {
153 console.log(`[Cloudflare] Checking if zone exists for ${domain}...`);
154
155 // Try to get existing zone
156 let zone = await getZone(domain);
157
158 // If no zone, create one
159 if (!zone || !zone.id) {
160 console.log(`[Cloudflare] Creating new zone for ${domain}...`);
161 zone = await createZone(domain);
162 }
163
164 if (!zone || !zone.name_servers || zone.name_servers.length === 0) {
165 throw new Error('Failed to get Cloudflare nameservers');
166 }
167
168 console.log(`[Cloudflare] Zone ID: ${zone.id}`);
169 console.log(`[Cloudflare] Nameservers: ${zone.name_servers.join(', ')}`);
170
171 return {
172 zoneId: zone.id,
173 nameservers: zone.name_servers
174 };
175 } catch (error) {
176 console.error(`[Cloudflare] Error for ${domain}:`, error.message);
177 throw error;
178 }
179}
180
181/**
182 * Update nameservers for a domain
183 * @param registrar
184 * @param domain
185 * @param nameservers
186 * @param dryRun
187 */
188async function updateDomainNameservers(registrar, domain, nameservers, dryRun = false) {
189 console.log(`\n${'='.repeat(60)}`);
190 console.log(`Processing: ${domain}`);
191 console.log(`${'='.repeat(60)}`);
192
193 if (dryRun) {
194 console.log('[DRY RUN] Would update nameservers to:', nameservers);
195 return {
196 domain,
197 success: true,
198 dryRun: true,
199 nameservers
200 };
201 }
202
203 try {
204 let result;
205
206 if (registrar === 'moniker') {
207 console.log('[Moniker] Updating nameservers...');
208 result = await moniker.updateNameservers(domain, nameservers);
209 } else if (registrar === 'enom') {
210 console.log('[eNom] Updating nameservers...');
211 result = await enom.updateNameservers(domain, nameservers);
212 } else {
213 throw new Error(`Unsupported registrar: ${registrar}`);
214 }
215
216 console.log('✅ Success!');
217 console.log('Updated nameservers:', result.nameservers);
218
219 return {
220 domain,
221 success: true,
222 nameservers: result.nameservers,
223 result
224 };
225 } catch (error) {
226 console.error('❌ Error:', error.message);
227
228 return {
229 domain,
230 success: false,
231 error: error.message
232 };
233 }
234}
235
236/**
237 * Delay execution
238 * @param ms
239 */
240function delay(ms) {
241 return new Promise(resolve => setTimeout(resolve, ms));
242}
243
244/**
245 * Main execution
246 */
247async function main() {
248 const options = parseArgs();
249
250 // Validate options
251 if (!options.registrar) {
252 console.error('❌ Error: --registrar is required');
253 console.log('Run with --help for usage information');
254 process.exit(1);
255 }
256
257 if (!['moniker', 'enom'].includes(options.registrar)) {
258 console.error('❌ Error: --registrar must be "moniker" or "enom"');
259 process.exit(1);
260 }
261
262 if (!options.domain && !options.file) {
263 console.error('❌ Error: Either --domain or --file is required');
264 console.log('Run with --help for usage information');
265 process.exit(1);
266 }
267
268 if (!options.cloudflare && options.nameservers.length === 0) {
269 console.error('❌ Error: Either --cloudflare or --nameservers is required');
270 process.exit(1);
271 }
272
273 console.log(`
274╔════════════════════════════════════════════════════════════╗
275║ Bulk Nameserver Updater ║
276╚════════════════════════════════════════════════════════════╝
277
278Configuration:
279 Registrar: ${options.registrar}
280 Mode: ${options.dryRun ? 'DRY RUN (no changes)' : 'LIVE'}
281 Delay: ${options.delay}ms between requests
282 `);
283
284 // Get domains
285 let domains = [];
286 if (options.domain) {
287 domains = [options.domain];
288 } else if (options.file) {
289 console.log(`Loading domains from: ${options.file}`);
290 domains = await loadDomainsFromFile(options.file);
291 console.log(`Loaded ${domains.length} domain(s)\n`);
292 }
293
294 if (domains.length === 0) {
295 console.error('❌ No domains to process');
296 process.exit(1);
297 }
298
299 // Results tracking
300 const results = [];
301 let successCount = 0;
302 let failCount = 0;
303
304 // Process each domain
305 for (let i = 0; i < domains.length; i++) {
306 const domain = domains[i];
307
308 try {
309 // Get nameservers
310 let nameservers = options.nameservers;
311
312 if (options.cloudflare) {
313 console.log(`\n[${i + 1}/${domains.length}] Getting Cloudflare nameservers for ${domain}...`);
314 const cfData = await getCloudflareNameservers(domain);
315 nameservers = cfData.nameservers;
316 }
317
318 // Update nameservers
319 const result = await updateDomainNameservers(
320 options.registrar,
321 domain,
322 nameservers,
323 options.dryRun
324 );
325
326 results.push(result);
327
328 if (result.success) {
329 successCount++;
330 } else {
331 failCount++;
332 }
333
334 // Delay between requests (except for last one)
335 if (i < domains.length - 1) {
336 console.log(`\nWaiting ${options.delay}ms before next request...`);
337 await delay(options.delay);
338 }
339 } catch (error) {
340 console.error(`\n❌ Fatal error processing ${domain}:`, error.message);
341 results.push({
342 domain,
343 success: false,
344 error: error.message
345 });
346 failCount++;
347 }
348 }
349
350 // Print summary
351 console.log(`\n${'='.repeat(60)}`);
352 console.log('SUMMARY');
353 console.log(`${'='.repeat(60)}`);
354 console.log(`Total domains: ${domains.length}`);
355 console.log(`✅ Successful: ${successCount}`);
356 console.log(`❌ Failed: ${failCount}`);
357
358 if (options.dryRun) {
359 console.log('\n⚠️ DRY RUN - No actual changes were made');
360 }
361
362 // Print failed domains
363 if (failCount > 0) {
364 console.log('\n\nFailed Domains:');
365 results
366 .filter(r => !r.success)
367 .forEach(r => {
368 console.log(` • ${r.domain}: ${r.error}`);
369 });
370 }
371
372 // Save results to file
373 const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
374 const resultFile = `nameserver-update-results-${timestamp}.json`;
375 await fs.writeFile(
376 resultFile,
377 JSON.stringify(results, null, 2),
378 'utf8'
379 );
380 console.log(`\nResults saved to: ${resultFile}`);
381
382 process.exit(failCount > 0 ? 1 : 0);
383}
384
385// Run if called directly
386if (require.main === module) {
387 main().catch(error => {
388 console.error('Fatal error:', error);
389 process.exit(1);
390 });
391}
392
393module.exports = {
394 updateDomainNameservers,
395 getCloudflareNameservers,
396 loadDomainsFromFile
397};