@startanaicompany/dns 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +435 -0
- package/bin/saac_dns.js +1340 -0
- package/index.js +675 -0
- package/lib/client.js +134 -0
- package/lib/namesilo.js +754 -0
- package/package.json +27 -0
package/lib/namesilo.js
ADDED
|
@@ -0,0 +1,754 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const NAMESILO_BASE = 'https://www.namesilo.com/api';
|
|
4
|
+
|
|
5
|
+
// ─── Custom Error ──────────────────────────────────────────────────────────
|
|
6
|
+
class NameSiloError extends Error {
|
|
7
|
+
constructor(detail, code) {
|
|
8
|
+
super(detail || 'NameSilo API error');
|
|
9
|
+
this.name = 'NameSiloError';
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.detail = detail;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// ─── Success codes ─────────────────────────────────────────────────────────
|
|
16
|
+
const SUCCESS_CODES = new Set([300, 301, 302]);
|
|
17
|
+
|
|
18
|
+
// ─── Core request function ─────────────────────────────────────────────────
|
|
19
|
+
async function call(operation, params = {}) {
|
|
20
|
+
const key = process.env.NAMESILO_API_KEY;
|
|
21
|
+
|
|
22
|
+
// Use mock mode when: NAMESILO_MOCK=true explicitly set, OR no API key configured
|
|
23
|
+
if (process.env.NAMESILO_MOCK === 'true' || !key) {
|
|
24
|
+
return mockResponse(operation, params);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const url = new URL(`${NAMESILO_BASE}/${operation}`);
|
|
28
|
+
url.searchParams.set('version', '1');
|
|
29
|
+
url.searchParams.set('type', 'json');
|
|
30
|
+
url.searchParams.set('key', key);
|
|
31
|
+
|
|
32
|
+
Object.entries(params).forEach(([k, v]) => {
|
|
33
|
+
if (v !== undefined && v !== null) {
|
|
34
|
+
url.searchParams.set(k, String(v));
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const res = await fetch(url.toString());
|
|
39
|
+
|
|
40
|
+
if (!res.ok) {
|
|
41
|
+
throw new NameSiloError(`HTTP ${res.status}: ${res.statusText}`, res.status);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let json;
|
|
45
|
+
try {
|
|
46
|
+
json = await res.json();
|
|
47
|
+
} catch (e) {
|
|
48
|
+
throw new NameSiloError('Invalid JSON response from NameSilo', 0);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const reply = json.reply || json;
|
|
52
|
+
const code = parseInt(reply.code, 10);
|
|
53
|
+
const detail = reply.detail || 'Unknown error';
|
|
54
|
+
|
|
55
|
+
if (SUCCESS_CODES.has(code)) {
|
|
56
|
+
return reply;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Map known error codes to descriptive messages
|
|
60
|
+
const errorMessages = {
|
|
61
|
+
110: 'Invalid API key',
|
|
62
|
+
108: 'Missing required parameters',
|
|
63
|
+
200: 'Domain not active or not owned by this account',
|
|
64
|
+
261: 'Domain not available for registration',
|
|
65
|
+
280: 'Insufficient account funds',
|
|
66
|
+
101: 'Operation failed',
|
|
67
|
+
102: 'Invalid input',
|
|
68
|
+
103: 'Authorization failed',
|
|
69
|
+
104: 'Domain locked',
|
|
70
|
+
105: 'Domain already exists',
|
|
71
|
+
106: 'Domain transfer prohibited',
|
|
72
|
+
107: 'Domain not eligible for renewal',
|
|
73
|
+
109: 'Invalid domain name',
|
|
74
|
+
111: 'Invalid contact ID',
|
|
75
|
+
112: 'Invalid nameserver',
|
|
76
|
+
113: 'Invalid years value',
|
|
77
|
+
114: 'Domain already private',
|
|
78
|
+
115: 'Domain already public',
|
|
79
|
+
116: 'Domain already locked',
|
|
80
|
+
117: 'Domain already unlocked',
|
|
81
|
+
118: 'Domain already has auto-renewal enabled',
|
|
82
|
+
119: 'Domain already has auto-renewal disabled',
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const message = errorMessages[code] || detail;
|
|
86
|
+
throw new NameSiloError(message, code);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ─── Mock mode data ────────────────────────────────────────────────────────
|
|
90
|
+
// In-memory storage for mock operations that require persistence between calls
|
|
91
|
+
const _mockStore = {
|
|
92
|
+
emailForwards: {} // { domain: [{ email2, forward2 }] }
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
function mockResponse(operation, params) {
|
|
96
|
+
const base = { code: 300, detail: 'success', operationtype: operation };
|
|
97
|
+
|
|
98
|
+
switch (operation) {
|
|
99
|
+
case 'checkRegisterAvailability': {
|
|
100
|
+
const domains = (params.domains || '').split(',').filter(Boolean);
|
|
101
|
+
const available = {};
|
|
102
|
+
const unavailable = {};
|
|
103
|
+
domains.forEach((d, i) => {
|
|
104
|
+
// Mock: every other domain is unavailable for realism
|
|
105
|
+
if (i % 2 === 0) {
|
|
106
|
+
available[d] = { price: '12.99' };
|
|
107
|
+
} else {
|
|
108
|
+
unavailable[d] = {};
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
return { ...base, available, unavailable };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
case 'registerDomain':
|
|
115
|
+
return {
|
|
116
|
+
...base,
|
|
117
|
+
code: 302,
|
|
118
|
+
detail: 'Successful order',
|
|
119
|
+
domain: params.domain,
|
|
120
|
+
order_amount: '12.99',
|
|
121
|
+
total_amount: '12.99',
|
|
122
|
+
message: 'Your domain registration was successfully processed.'
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
case 'getPrices':
|
|
126
|
+
return {
|
|
127
|
+
...base,
|
|
128
|
+
com: { registration: '12.99', renewal: '12.99', transfer: '12.99' },
|
|
129
|
+
net: { registration: '11.99', renewal: '11.99', transfer: '11.99' },
|
|
130
|
+
org: { registration: '10.99', renewal: '10.99', transfer: '10.99' },
|
|
131
|
+
io: { registration: '39.99', renewal: '39.99', transfer: '39.99' },
|
|
132
|
+
dev: { registration: '12.99', renewal: '12.99', transfer: '12.99' },
|
|
133
|
+
app: { registration: '14.99', renewal: '14.99', transfer: '14.99' },
|
|
134
|
+
ai: { registration: '69.99', renewal: '69.99', transfer: '69.99' },
|
|
135
|
+
co: { registration: '24.99', renewal: '24.99', transfer: '24.99' },
|
|
136
|
+
xyz: { registration: '3.99', renewal: '3.99', transfer: '3.99' },
|
|
137
|
+
me: { registration: '19.99', renewal: '19.99', transfer: '19.99' }
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
case 'listDomains':
|
|
141
|
+
return {
|
|
142
|
+
...base,
|
|
143
|
+
domains: {
|
|
144
|
+
domain: [
|
|
145
|
+
{
|
|
146
|
+
domain: 'example.com',
|
|
147
|
+
created: '2024-01-15',
|
|
148
|
+
expires: '2025-01-15',
|
|
149
|
+
status: 'Active',
|
|
150
|
+
locked: 'Yes',
|
|
151
|
+
private: 'No',
|
|
152
|
+
auto_renew: 'Yes',
|
|
153
|
+
traffic_type: 'Forwarding',
|
|
154
|
+
email_verified: 'Yes',
|
|
155
|
+
portfolio: ''
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
domain: 'mysite.io',
|
|
159
|
+
created: '2024-06-01',
|
|
160
|
+
expires: '2025-06-01',
|
|
161
|
+
status: 'Active',
|
|
162
|
+
locked: 'Yes',
|
|
163
|
+
private: 'Yes',
|
|
164
|
+
auto_renew: 'No',
|
|
165
|
+
traffic_type: 'Forwarding',
|
|
166
|
+
email_verified: 'Yes',
|
|
167
|
+
portfolio: ''
|
|
168
|
+
}
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
case 'getDomainInfo':
|
|
174
|
+
return {
|
|
175
|
+
...base,
|
|
176
|
+
domain: params.domain || 'example.com',
|
|
177
|
+
created: '2024-01-15',
|
|
178
|
+
expires: '2025-01-15',
|
|
179
|
+
status: 'Active',
|
|
180
|
+
locked: 'Yes',
|
|
181
|
+
private: 'No',
|
|
182
|
+
auto_renew: 'Yes',
|
|
183
|
+
nameservers: { nameserver: ['ns1.namesilo.com', 'ns2.namesilo.com'] },
|
|
184
|
+
contact_ids: {
|
|
185
|
+
registrant: '12345',
|
|
186
|
+
administrative: '12345',
|
|
187
|
+
technical: '12345',
|
|
188
|
+
billing: '12345'
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
case 'renewDomain':
|
|
193
|
+
return {
|
|
194
|
+
...base,
|
|
195
|
+
code: 302,
|
|
196
|
+
detail: 'Successful order',
|
|
197
|
+
domain: params.domain,
|
|
198
|
+
years: params.years || 1,
|
|
199
|
+
order_amount: '12.99',
|
|
200
|
+
total_amount: '12.99'
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
case 'dnsListRecords':
|
|
204
|
+
return {
|
|
205
|
+
...base,
|
|
206
|
+
domain: params.domain || 'example.com',
|
|
207
|
+
resource_record: [
|
|
208
|
+
{ record_id: 'mock-rr-1', type: 'A', host: params.domain, value: '93.184.216.34', ttl: '3600', distance: '0' },
|
|
209
|
+
{ record_id: 'mock-rr-2', type: 'MX', host: params.domain, value: 'mail.example.com', ttl: '3600', distance: '10' },
|
|
210
|
+
{ record_id: 'mock-rr-3', type: 'TXT', host: params.domain, value: 'v=spf1 include:_spf.google.com ~all', ttl: '3600', distance: '0' }
|
|
211
|
+
]
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
case 'dnsAddRecord':
|
|
215
|
+
return {
|
|
216
|
+
...base,
|
|
217
|
+
record_id: 'mock-rr-' + Math.floor(Math.random() * 100000)
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
case 'dnsUpdateRecord':
|
|
221
|
+
return { ...base };
|
|
222
|
+
|
|
223
|
+
case 'dnsDeleteRecord':
|
|
224
|
+
return { ...base };
|
|
225
|
+
|
|
226
|
+
case 'changeNameServers':
|
|
227
|
+
return { ...base };
|
|
228
|
+
|
|
229
|
+
case 'contactList':
|
|
230
|
+
return {
|
|
231
|
+
...base,
|
|
232
|
+
contact: [
|
|
233
|
+
{
|
|
234
|
+
contact_id: '12345',
|
|
235
|
+
fn: 'John',
|
|
236
|
+
ln: 'Doe',
|
|
237
|
+
ad: '123 Main St',
|
|
238
|
+
cy: 'Anytown',
|
|
239
|
+
st: 'CA',
|
|
240
|
+
zp: '12345',
|
|
241
|
+
ct: 'US',
|
|
242
|
+
ph: '3125551234',
|
|
243
|
+
em: 'john.doe@example.com',
|
|
244
|
+
org: 'ACME Corp'
|
|
245
|
+
}
|
|
246
|
+
]
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
case 'contactAdd':
|
|
250
|
+
return {
|
|
251
|
+
...base,
|
|
252
|
+
contact_id: 'mock-contact-' + Math.floor(Math.random() * 100000)
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
case 'domainLock':
|
|
256
|
+
return { ...base };
|
|
257
|
+
|
|
258
|
+
case 'domainUnlock':
|
|
259
|
+
return { ...base };
|
|
260
|
+
|
|
261
|
+
case 'addPrivacy':
|
|
262
|
+
return { ...base };
|
|
263
|
+
|
|
264
|
+
case 'removePrivacy':
|
|
265
|
+
return { ...base };
|
|
266
|
+
|
|
267
|
+
case 'addAutoRenewal':
|
|
268
|
+
return { ...base };
|
|
269
|
+
|
|
270
|
+
case 'removeAutoRenewal':
|
|
271
|
+
return { ...base };
|
|
272
|
+
|
|
273
|
+
case 'retrieveAuthCode':
|
|
274
|
+
return {
|
|
275
|
+
...base,
|
|
276
|
+
domain: params.domain,
|
|
277
|
+
message: 'The auth code for your domain has been sent to the registrant email address on file.'
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
case 'getAccountBalance':
|
|
281
|
+
return {
|
|
282
|
+
...base,
|
|
283
|
+
balance: '100.00'
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
case 'whoisInfo':
|
|
287
|
+
return {
|
|
288
|
+
...base,
|
|
289
|
+
domain: params.domain || 'example.com',
|
|
290
|
+
registrar: 'NameSilo, LLC',
|
|
291
|
+
registrar_url: 'https://www.namesilo.com',
|
|
292
|
+
creation_date: '2024-01-15T00:00:00Z',
|
|
293
|
+
updated_date: '2024-06-01T00:00:00Z',
|
|
294
|
+
expiry_date: '2025-01-15T00:00:00Z',
|
|
295
|
+
status: 'clientTransferProhibited',
|
|
296
|
+
nameservers: { nameserver: ['ns1.namesilo.com', 'ns2.namesilo.com'] },
|
|
297
|
+
registrant: {
|
|
298
|
+
name: 'Domain Admin',
|
|
299
|
+
organization: 'NameSilo Privacy Protection',
|
|
300
|
+
email: 'proxy@namesilo.com',
|
|
301
|
+
country: 'US'
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
case 'listRegisteredNameServers':
|
|
306
|
+
return {
|
|
307
|
+
...base,
|
|
308
|
+
hosts: [
|
|
309
|
+
{ host: `ns1.${params.domain || 'example.com'}`, ip: '1.2.3.4' }
|
|
310
|
+
]
|
|
311
|
+
};
|
|
312
|
+
case 'addRegisteredNameServer':
|
|
313
|
+
case 'modifyRegisteredNameServer':
|
|
314
|
+
case 'deleteRegisteredNameServer':
|
|
315
|
+
return { ...base };
|
|
316
|
+
|
|
317
|
+
case 'portfolioList':
|
|
318
|
+
return { ...base, portfolios: { portfolio: [{ name: 'my-portfolio', domains: 5 }] } };
|
|
319
|
+
case 'portfolioAdd':
|
|
320
|
+
return { ...base };
|
|
321
|
+
case 'portfolioDelete':
|
|
322
|
+
return { ...base };
|
|
323
|
+
case 'portfolioDomainAssociate':
|
|
324
|
+
return { ...base };
|
|
325
|
+
|
|
326
|
+
case 'listOrders':
|
|
327
|
+
return { ...base, orders: { order: [{ order_number: 'ORD-1001', domain: 'example.com', amount: '12.99', date: '2024-01-15', status: 'Complete' }] } };
|
|
328
|
+
case 'orderDetails':
|
|
329
|
+
return { ...base, order_number: params.order_number, domain: 'example.com', amount: '12.99', date: '2024-01-15', status: 'Complete' };
|
|
330
|
+
|
|
331
|
+
case 'domainPush':
|
|
332
|
+
return { ...base, domain: params.domain };
|
|
333
|
+
case 'registrantVerificationStatus':
|
|
334
|
+
return { ...base, verified: true, email: params.email || 'user@example.com' };
|
|
335
|
+
case 'emailVerification':
|
|
336
|
+
return { ...base, message: 'Verification email sent' };
|
|
337
|
+
|
|
338
|
+
case 'transferUpdateChangeEPPCode':
|
|
339
|
+
return { ...base };
|
|
340
|
+
case 'transferUpdateResendAdminEmail':
|
|
341
|
+
return { ...base };
|
|
342
|
+
case 'transferUpdateResubmitToRegistry':
|
|
343
|
+
return { ...base };
|
|
344
|
+
case 'registerDomainDrop':
|
|
345
|
+
return { ...base, code: 302, detail: 'Successful order', domain: params.domain, order_amount: '12.99' };
|
|
346
|
+
|
|
347
|
+
case 'listEmailForwards': {
|
|
348
|
+
const domain = params.domain || '';
|
|
349
|
+
const forwards = _mockStore.emailForwards[domain] || [];
|
|
350
|
+
return { ...base, forwards };
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
case 'configureEmailForward': {
|
|
354
|
+
const domain = params.domain || '';
|
|
355
|
+
if (!_mockStore.emailForwards[domain]) _mockStore.emailForwards[domain] = [];
|
|
356
|
+
// Remove existing forward for same address if any, then add
|
|
357
|
+
_mockStore.emailForwards[domain] = _mockStore.emailForwards[domain].filter(
|
|
358
|
+
f => f.email2 !== params.email2
|
|
359
|
+
);
|
|
360
|
+
_mockStore.emailForwards[domain].push({ email2: params.email2, forward2: params.forward2 });
|
|
361
|
+
return { ...base };
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
case 'deleteEmailForward': {
|
|
365
|
+
const domain = params.domain || '';
|
|
366
|
+
if (_mockStore.emailForwards[domain]) {
|
|
367
|
+
_mockStore.emailForwards[domain] = _mockStore.emailForwards[domain].filter(
|
|
368
|
+
f => f.email2 !== params.email2
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
return { ...base };
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
default:
|
|
375
|
+
return { ...base, code: 300, detail: 'success (mock)' };
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// ─── Public API operations ─────────────────────────────────────────────────
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Check domain availability for registration.
|
|
383
|
+
* @param {string[]} domains - Array of FQDNs
|
|
384
|
+
*/
|
|
385
|
+
async function checkRegisterAvailability(domains) {
|
|
386
|
+
if (!Array.isArray(domains) || domains.length === 0) {
|
|
387
|
+
throw new NameSiloError('domains must be a non-empty array', 108);
|
|
388
|
+
}
|
|
389
|
+
return call('checkRegisterAvailability', { domains: domains.join(',') });
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Register a domain.
|
|
394
|
+
* @param {string} domain - FQDN to register
|
|
395
|
+
* @param {number} years - Registration years (1-10)
|
|
396
|
+
* @param {Object} opts - Optional: auto_renew, private, contact_id
|
|
397
|
+
*/
|
|
398
|
+
async function registerDomain(domain, years = 1, opts = {}) {
|
|
399
|
+
if (!domain) throw new NameSiloError('domain is required', 108);
|
|
400
|
+
return call('registerDomain', {
|
|
401
|
+
domain,
|
|
402
|
+
years,
|
|
403
|
+
auto_renew: opts.auto_renew !== undefined ? (opts.auto_renew ? 1 : 0) : 1,
|
|
404
|
+
private: opts.private !== undefined ? (opts.private ? 1 : 0) : 0,
|
|
405
|
+
contact_id: opts.contact_id
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Get current pricing for TLDs.
|
|
411
|
+
* @param {string[]} tlds - Optional array of TLDs to filter (without dot)
|
|
412
|
+
*/
|
|
413
|
+
async function getPrices(tlds = []) {
|
|
414
|
+
return call('getPrices');
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* List all domains in the account.
|
|
419
|
+
*/
|
|
420
|
+
async function listDomains() {
|
|
421
|
+
return call('listDomains');
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Get detailed info about a specific domain.
|
|
426
|
+
* @param {string} domain - FQDN
|
|
427
|
+
*/
|
|
428
|
+
async function getDomainInfo(domain) {
|
|
429
|
+
if (!domain) throw new NameSiloError('domain is required', 108);
|
|
430
|
+
return call('getDomainInfo', { domain });
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Renew a domain.
|
|
435
|
+
* @param {string} domain - FQDN
|
|
436
|
+
* @param {number} years - Number of years to renew
|
|
437
|
+
*/
|
|
438
|
+
async function renewDomain(domain, years = 1) {
|
|
439
|
+
if (!domain) throw new NameSiloError('domain is required', 108);
|
|
440
|
+
return call('renewDomain', { domain, years });
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* List DNS records for a domain.
|
|
445
|
+
* @param {string} domain - FQDN
|
|
446
|
+
*/
|
|
447
|
+
async function dnsListRecords(domain) {
|
|
448
|
+
if (!domain) throw new NameSiloError('domain is required', 108);
|
|
449
|
+
return call('dnsListRecords', { domain });
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Add a DNS record.
|
|
454
|
+
* @param {string} domain
|
|
455
|
+
* @param {string} type - Record type (A, AAAA, CNAME, MX, TXT, etc.)
|
|
456
|
+
* @param {string} host - Hostname/subdomain
|
|
457
|
+
* @param {string} value - Record value
|
|
458
|
+
* @param {number} ttl - TTL in seconds (default 3600)
|
|
459
|
+
* @param {number} distance - MX priority / SRV distance
|
|
460
|
+
*/
|
|
461
|
+
async function dnsAddRecord(domain, type, host, value, ttl = 3600, distance = 0) {
|
|
462
|
+
if (!domain || !type || !host || !value) {
|
|
463
|
+
throw new NameSiloError('domain, type, host, and value are required', 108);
|
|
464
|
+
}
|
|
465
|
+
return call('dnsAddRecord', {
|
|
466
|
+
domain,
|
|
467
|
+
rrtype: type,
|
|
468
|
+
rrhost: host,
|
|
469
|
+
rrvalue: value,
|
|
470
|
+
rrttl: ttl,
|
|
471
|
+
rrdistance: distance
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Update an existing DNS record.
|
|
477
|
+
* @param {string} domain
|
|
478
|
+
* @param {string} rrid - Record ID from dnsListRecords
|
|
479
|
+
* @param {string} host
|
|
480
|
+
* @param {string} value
|
|
481
|
+
* @param {number} ttl
|
|
482
|
+
* @param {number} distance
|
|
483
|
+
*/
|
|
484
|
+
async function dnsUpdateRecord(domain, rrid, host, value, ttl = 3600, distance = 0) {
|
|
485
|
+
if (!domain || !rrid || !host || !value) {
|
|
486
|
+
throw new NameSiloError('domain, rrid, host, and value are required', 108);
|
|
487
|
+
}
|
|
488
|
+
return call('dnsUpdateRecord', {
|
|
489
|
+
domain,
|
|
490
|
+
rrid,
|
|
491
|
+
rrhost: host,
|
|
492
|
+
rrvalue: value,
|
|
493
|
+
rrttl: ttl,
|
|
494
|
+
rrdistance: distance
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Delete a DNS record.
|
|
500
|
+
* @param {string} domain
|
|
501
|
+
* @param {string} rrid - Record ID
|
|
502
|
+
*/
|
|
503
|
+
async function dnsDeleteRecord(domain, rrid) {
|
|
504
|
+
if (!domain || !rrid) {
|
|
505
|
+
throw new NameSiloError('domain and rrid are required', 108);
|
|
506
|
+
}
|
|
507
|
+
return call('dnsDeleteRecord', { domain, rrid });
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Change nameservers for a domain.
|
|
512
|
+
* @param {string} domain
|
|
513
|
+
* @param {string[]} ns - Array of nameserver hostnames (2-13)
|
|
514
|
+
*/
|
|
515
|
+
async function changeNameServers(domain, ns) {
|
|
516
|
+
if (!domain) throw new NameSiloError('domain is required', 108);
|
|
517
|
+
if (!Array.isArray(ns) || ns.length < 2) {
|
|
518
|
+
throw new NameSiloError('At least 2 nameservers required', 108);
|
|
519
|
+
}
|
|
520
|
+
const nsParams = {};
|
|
521
|
+
ns.forEach((n, i) => { nsParams[`ns${i + 1}`] = n; });
|
|
522
|
+
return call('changeNameServers', { domain, ...nsParams });
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* List contacts in the account.
|
|
527
|
+
*/
|
|
528
|
+
async function contactList() {
|
|
529
|
+
return call('contactList');
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Add a contact to the account.
|
|
534
|
+
* @param {Object} contact - Contact details
|
|
535
|
+
*/
|
|
536
|
+
async function contactAdd(contact) {
|
|
537
|
+
const required = ['fn', 'ln', 'ad', 'cy', 'st', 'zp', 'ct', 'ph', 'em'];
|
|
538
|
+
for (const field of required) {
|
|
539
|
+
if (!contact[field]) {
|
|
540
|
+
throw new NameSiloError(`Contact field '${field}' is required`, 108);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return call('contactAdd', contact);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Lock a domain (prevent transfers).
|
|
548
|
+
* @param {string} domain
|
|
549
|
+
*/
|
|
550
|
+
async function domainLock(domain) {
|
|
551
|
+
if (!domain) throw new NameSiloError('domain is required', 108);
|
|
552
|
+
return call('domainLock', { domain });
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Unlock a domain (allow transfers).
|
|
557
|
+
* @param {string} domain
|
|
558
|
+
*/
|
|
559
|
+
async function domainUnlock(domain) {
|
|
560
|
+
if (!domain) throw new NameSiloError('domain is required', 108);
|
|
561
|
+
return call('domainUnlock', { domain });
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Enable WHOIS privacy for a domain.
|
|
566
|
+
* @param {string} domain
|
|
567
|
+
*/
|
|
568
|
+
async function addPrivacy(domain) {
|
|
569
|
+
if (!domain) throw new NameSiloError('domain is required', 108);
|
|
570
|
+
return call('addPrivacy', { domain });
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Disable WHOIS privacy for a domain.
|
|
575
|
+
* @param {string} domain
|
|
576
|
+
*/
|
|
577
|
+
async function removePrivacy(domain) {
|
|
578
|
+
if (!domain) throw new NameSiloError('domain is required', 108);
|
|
579
|
+
return call('removePrivacy', { domain });
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Enable auto-renewal for a domain.
|
|
584
|
+
* @param {string} domain
|
|
585
|
+
*/
|
|
586
|
+
async function addAutoRenewal(domain) {
|
|
587
|
+
if (!domain) throw new NameSiloError('domain is required', 108);
|
|
588
|
+
return call('addAutoRenewal', { domain });
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Disable auto-renewal for a domain.
|
|
593
|
+
* @param {string} domain
|
|
594
|
+
*/
|
|
595
|
+
async function removeAutoRenewal(domain) {
|
|
596
|
+
if (!domain) throw new NameSiloError('domain is required', 108);
|
|
597
|
+
return call('removeAutoRenewal', { domain });
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Request the EPP auth/transfer code for a domain (sent via email).
|
|
602
|
+
* @param {string} domain
|
|
603
|
+
*/
|
|
604
|
+
async function retrieveAuthCode(domain) {
|
|
605
|
+
if (!domain) throw new NameSiloError('domain is required', 108);
|
|
606
|
+
return call('retrieveAuthCode', { domain });
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Get the current account balance.
|
|
611
|
+
*/
|
|
612
|
+
async function getAccountBalance() {
|
|
613
|
+
return call('getAccountBalance');
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Get WHOIS info for a domain.
|
|
618
|
+
* @param {string} domain
|
|
619
|
+
*/
|
|
620
|
+
async function whoisInfo(domain) {
|
|
621
|
+
if (!domain) throw new NameSiloError('domain is required', 108);
|
|
622
|
+
return call('whoisInfo', { domain });
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* List registered nameservers for a domain.
|
|
627
|
+
*/
|
|
628
|
+
async function listRegisteredNameServers(domain) {
|
|
629
|
+
if (!domain) throw new NameSiloError('domain is required', 108);
|
|
630
|
+
return call('listRegisteredNameServers', { domain });
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Add/register a private nameserver.
|
|
635
|
+
* @param {string} domain
|
|
636
|
+
* @param {string} host - Nameserver hostname (e.g. ns1.mydomain.com)
|
|
637
|
+
* @param {string[]} ips - IP addresses for the nameserver
|
|
638
|
+
*/
|
|
639
|
+
async function addRegisteredNameServer(domain, host, ips) {
|
|
640
|
+
if (!domain || !host || !ips || !ips.length) {
|
|
641
|
+
throw new NameSiloError('domain, host, and ips are required', 108);
|
|
642
|
+
}
|
|
643
|
+
const params = { domain, host };
|
|
644
|
+
ips.forEach((ip, i) => { params[`ip${i + 1}`] = ip; });
|
|
645
|
+
return call('addRegisteredNameServer', params);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Modify a registered nameserver.
|
|
650
|
+
*/
|
|
651
|
+
async function modifyRegisteredNameServer(domain, host, ips) {
|
|
652
|
+
if (!domain || !host || !ips || !ips.length) {
|
|
653
|
+
throw new NameSiloError('domain, host, and ips are required', 108);
|
|
654
|
+
}
|
|
655
|
+
const params = { domain, host };
|
|
656
|
+
ips.forEach((ip, i) => { params[`ip${i + 1}`] = ip; });
|
|
657
|
+
return call('modifyRegisteredNameServer', params);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Delete a registered nameserver.
|
|
662
|
+
*/
|
|
663
|
+
async function deleteRegisteredNameServer(domain, host) {
|
|
664
|
+
if (!domain || !host) throw new NameSiloError('domain and host are required', 108);
|
|
665
|
+
return call('deleteRegisteredNameServer', { domain, host });
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// ─── Portfolio Management ─────────────────────────────────────────────────
|
|
669
|
+
async function portfolioList() { return call('portfolioList'); }
|
|
670
|
+
async function portfolioAdd(portfolio) { return call('portfolioAdd', { portfolio }); }
|
|
671
|
+
async function portfolioDelete(portfolio) { return call('portfolioDelete', { portfolio }); }
|
|
672
|
+
async function portfolioDomainAssociate(portfolio, domains) {
|
|
673
|
+
return call('portfolioDomainAssociate', { portfolio, domains: Array.isArray(domains) ? domains.join(',') : domains });
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// ─── Orders ───────────────────────────────────────────────────────────────
|
|
677
|
+
async function listOrders() { return call('listOrders'); }
|
|
678
|
+
async function orderDetails(orderNumber) {
|
|
679
|
+
if (!orderNumber) throw new NameSiloError('order_number is required', 108);
|
|
680
|
+
return call('orderDetails', { order_number: orderNumber });
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// ─── Domain Push & Registrant Verification ────────────────────────────────
|
|
684
|
+
async function domainPush(domain, push_user) {
|
|
685
|
+
if (!domain || !push_user) throw new NameSiloError('domain and push_user (email) are required', 108);
|
|
686
|
+
return call('domainPush', { domain, push_user });
|
|
687
|
+
}
|
|
688
|
+
async function registrantVerificationStatus() { return call('registrantVerificationStatus'); }
|
|
689
|
+
async function emailVerification(email) {
|
|
690
|
+
if (!email) throw new NameSiloError('email is required', 108);
|
|
691
|
+
return call('emailVerification', { email });
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
async function transferUpdateChangeEPPCode(domain, auth) {
|
|
695
|
+
if (!domain || !auth) throw new NameSiloError('domain and auth are required', 108);
|
|
696
|
+
return call('transferUpdateChangeEPPCode', { domain, auth });
|
|
697
|
+
}
|
|
698
|
+
async function transferUpdateResendAdminEmail(domain) {
|
|
699
|
+
if (!domain) throw new NameSiloError('domain is required', 108);
|
|
700
|
+
return call('transferUpdateResendAdminEmail', { domain });
|
|
701
|
+
}
|
|
702
|
+
async function transferUpdateResubmitToRegistry(domain) {
|
|
703
|
+
if (!domain) throw new NameSiloError('domain is required', 108);
|
|
704
|
+
return call('transferUpdateResubmitToRegistry', { domain });
|
|
705
|
+
}
|
|
706
|
+
async function registerDomainDrop(domain, opts = {}) {
|
|
707
|
+
if (!domain) throw new NameSiloError('domain is required', 108);
|
|
708
|
+
return call('registerDomainDrop', { domain, years: opts.years || 1, auto_renew: opts.auto_renew !== false ? 1 : 0, private: opts.private ? 1 : 0 });
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// ─── Exports ───────────────────────────────────────────────────────────────
|
|
712
|
+
module.exports = {
|
|
713
|
+
NameSiloError,
|
|
714
|
+
call,
|
|
715
|
+
checkRegisterAvailability,
|
|
716
|
+
registerDomain,
|
|
717
|
+
getPrices,
|
|
718
|
+
listDomains,
|
|
719
|
+
getDomainInfo,
|
|
720
|
+
renewDomain,
|
|
721
|
+
dnsListRecords,
|
|
722
|
+
dnsAddRecord,
|
|
723
|
+
dnsUpdateRecord,
|
|
724
|
+
dnsDeleteRecord,
|
|
725
|
+
changeNameServers,
|
|
726
|
+
contactList,
|
|
727
|
+
contactAdd,
|
|
728
|
+
domainLock,
|
|
729
|
+
domainUnlock,
|
|
730
|
+
addPrivacy,
|
|
731
|
+
removePrivacy,
|
|
732
|
+
addAutoRenewal,
|
|
733
|
+
removeAutoRenewal,
|
|
734
|
+
retrieveAuthCode,
|
|
735
|
+
getAccountBalance,
|
|
736
|
+
whoisInfo,
|
|
737
|
+
listRegisteredNameServers,
|
|
738
|
+
addRegisteredNameServer,
|
|
739
|
+
modifyRegisteredNameServer,
|
|
740
|
+
deleteRegisteredNameServer,
|
|
741
|
+
portfolioList,
|
|
742
|
+
portfolioAdd,
|
|
743
|
+
portfolioDelete,
|
|
744
|
+
portfolioDomainAssociate,
|
|
745
|
+
listOrders,
|
|
746
|
+
orderDetails,
|
|
747
|
+
domainPush,
|
|
748
|
+
registrantVerificationStatus,
|
|
749
|
+
emailVerification,
|
|
750
|
+
transferUpdateChangeEPPCode,
|
|
751
|
+
transferUpdateResendAdminEmail,
|
|
752
|
+
transferUpdateResubmitToRegistry,
|
|
753
|
+
registerDomainDrop
|
|
754
|
+
};
|