apertodns 1.2.5 → 2.0.1
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/index.js +125 -130
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -141,6 +141,16 @@ if (isCron) {
|
|
|
141
141
|
process.argv.push("--json");
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
+
// Subcommand detection (new style: domains list, update domain.com, etc.)
|
|
145
|
+
const subcommand = args[0] && !args[0].startsWith('-') ? args[0] : null;
|
|
146
|
+
const subcommandArg = args[1] && !args[1].startsWith('-') ? args[1] : null;
|
|
147
|
+
|
|
148
|
+
// Helper to get option value from args (works anywhere in args array)
|
|
149
|
+
const getOption = (name) => {
|
|
150
|
+
const idx = args.indexOf(name);
|
|
151
|
+
return idx !== -1 && args[idx + 1] && !args[idx + 1].startsWith('-') ? args[idx + 1] : null;
|
|
152
|
+
};
|
|
153
|
+
|
|
144
154
|
const isQuiet = args.includes("--quiet");
|
|
145
155
|
const showHelp = args.includes("--help") || args.includes("-h");
|
|
146
156
|
const showVersion = args.includes("--version") || args.includes("-v");
|
|
@@ -149,37 +159,30 @@ const runVerify = args.includes("--verify");
|
|
|
149
159
|
const runSetup = args.includes("--setup");
|
|
150
160
|
const showStatus = args.includes("--status") || args.includes("--show");
|
|
151
161
|
const forceUpdate = args.includes("--force");
|
|
152
|
-
const enableTokenId =
|
|
153
|
-
const disableTokenId =
|
|
154
|
-
const toggleTokenId =
|
|
162
|
+
const enableTokenId = getOption("--enable");
|
|
163
|
+
const disableTokenId = getOption("--disable");
|
|
164
|
+
const toggleTokenId = getOption("--toggle");
|
|
155
165
|
const runConfigEdit = args.includes("--config");
|
|
156
|
-
const listDomains = args.includes("--domains");
|
|
157
|
-
const listTokens = args.includes("--tokens");
|
|
158
|
-
const addDomainArg =
|
|
159
|
-
const deleteDomainArg =
|
|
160
|
-
const showStats = args.includes("--stats");
|
|
161
|
-
const showLogs = args.includes("--logs");
|
|
162
|
-
const testDns =
|
|
163
|
-
const showDashboard = args.includes("--dashboard");
|
|
164
|
-
const listWebhooks = args.includes("--webhooks");
|
|
165
|
-
const listApiKeys = args.includes("--api-keys");
|
|
166
|
-
const createApiKeyArg =
|
|
167
|
-
const deleteApiKeyArg =
|
|
168
|
-
const showScopes = args.includes("--scopes");
|
|
169
|
-
const useApiKey =
|
|
166
|
+
const listDomains = args.includes("--domains") || (subcommand === "domains" && (!subcommandArg || subcommandArg === "list"));
|
|
167
|
+
const listTokens = args.includes("--tokens") || (subcommand === "tokens" && (!subcommandArg || subcommandArg === "list"));
|
|
168
|
+
const addDomainArg = getOption("--add-domain") || (subcommand === "domains" && subcommandArg === "add" ? args[2] : null);
|
|
169
|
+
const deleteDomainArg = getOption("--delete-domain") || (subcommand === "domains" && subcommandArg === "delete" ? args[2] : null);
|
|
170
|
+
const showStats = args.includes("--stats") || subcommand === "stats";
|
|
171
|
+
const showLogs = args.includes("--logs") || subcommand === "logs";
|
|
172
|
+
const testDns = getOption("--test") || (subcommand === "test" ? subcommandArg : null);
|
|
173
|
+
const showDashboard = args.includes("--dashboard") || subcommand === "dashboard";
|
|
174
|
+
const listWebhooks = args.includes("--webhooks") || subcommand === "webhooks";
|
|
175
|
+
const listApiKeys = args.includes("--api-keys") || (subcommand === "api-keys" && (!subcommandArg || subcommandArg === "list"));
|
|
176
|
+
const createApiKeyArg = getOption("--create-api-key") || (subcommand === "api-keys" && subcommandArg === "create" ? args[2] : null);
|
|
177
|
+
const deleteApiKeyArg = getOption("--delete-api-key") || (subcommand === "api-keys" && subcommandArg === "delete" ? args[2] : null);
|
|
178
|
+
const showScopes = args.includes("--scopes") || subcommand === "scopes";
|
|
179
|
+
const useApiKey = getOption("--api-key");
|
|
180
|
+
const updateDomainArg = subcommand === "update" ? subcommandArg : null;
|
|
170
181
|
const runInteractive = args.length === 0;
|
|
171
|
-
const runDaemon = args.includes("--daemon");
|
|
172
|
-
const daemonInterval =
|
|
173
|
-
const showMyIp = args.includes("--my-ip") || args.includes("--ip");
|
|
174
|
-
const logout = args.includes("--logout");
|
|
175
|
-
|
|
176
|
-
// Standalone update command (like docker updater)
|
|
177
|
-
const standaloneUpdate = args.includes("--update");
|
|
178
|
-
const updateDomain = args.includes("--domain") ? args[args.indexOf("--domain") + 1] : null;
|
|
179
|
-
const updateToken = args.includes("--token") ? args[args.indexOf("--token") + 1] : null;
|
|
180
|
-
const updateIp = args.includes("--ip") && args.indexOf("--ip") !== args.indexOf("--my-ip")
|
|
181
|
-
? args[args.indexOf("--ip") + 1]
|
|
182
|
-
: null;
|
|
182
|
+
const runDaemon = args.includes("--daemon") || subcommand === "daemon";
|
|
183
|
+
const daemonInterval = getOption("--interval") ? parseInt(getOption("--interval")) : 300;
|
|
184
|
+
const showMyIp = args.includes("--my-ip") || args.includes("--ip") || subcommand === "ip" || subcommand === "my-ip";
|
|
185
|
+
const logout = args.includes("--logout") || subcommand === "logout";
|
|
183
186
|
|
|
184
187
|
// JSON output helper
|
|
185
188
|
const jsonOutput = (data) => {
|
|
@@ -234,12 +237,6 @@ ${chalk.bold("CONFIGURAZIONE:")}
|
|
|
234
237
|
${cyan("--logout")} Rimuovi configurazione locale
|
|
235
238
|
${cyan("--force")} Forza aggiornamento DNS
|
|
236
239
|
|
|
237
|
-
${chalk.bold("AGGIORNAMENTO STANDALONE:")}
|
|
238
|
-
${cyan("--update")} Aggiorna DNS via DynDNS2 (richiede --domain e --token)
|
|
239
|
-
${cyan("--domain")} <name> Dominio da aggiornare (es: myhost.apertodns.com)
|
|
240
|
-
${cyan("--token")} <token> Token DDNS per l'autenticazione
|
|
241
|
-
${cyan("--ip")} <ip> IP da impostare (opzionale, auto-detect se omesso)
|
|
242
|
-
|
|
243
240
|
${chalk.bold("DAEMON MODE:")}
|
|
244
241
|
${cyan("--daemon")} Avvia in modalità daemon (aggiornamento continuo)
|
|
245
242
|
${cyan("--interval")} <sec> Intervallo aggiornamento daemon (default: 300s)
|
|
@@ -267,8 +264,6 @@ ${gray("Esempi:")}
|
|
|
267
264
|
${gray("$")} apertodns --test mioserver.apertodns.com
|
|
268
265
|
${gray("$")} apertodns --daemon --interval 60
|
|
269
266
|
${gray("$")} apertodns --api-key ak_xxx... --domains --json
|
|
270
|
-
${gray("$")} apertodns --update --domain myhost.apertodns.com --token abc123
|
|
271
|
-
${gray("$")} apertodns --update --domain myhost.apertodns.com --token abc123 --ip 1.2.3.4
|
|
272
267
|
|
|
273
268
|
${gray("Docs: https://apertodns.com/docs")}
|
|
274
269
|
`);
|
|
@@ -318,8 +313,8 @@ const spinner = (text) => ora({ text, spinner: "dots", color: "yellow" });
|
|
|
318
313
|
|
|
319
314
|
// Helper: get auth headers (supports both JWT and API Key)
|
|
320
315
|
const getAuthHeaders = (token) => {
|
|
321
|
-
// API Keys start with "ak_"
|
|
322
|
-
if (token && token.startsWith('ak_')) {
|
|
316
|
+
// API Keys start with "ak_" or "apertodns_live_" or "apertodns_test_"
|
|
317
|
+
if (token && (token.startsWith('ak_') || token.startsWith('apertodns_live_') || token.startsWith('apertodns_test_'))) {
|
|
323
318
|
return { 'X-API-Key': token };
|
|
324
319
|
}
|
|
325
320
|
return { Authorization: `Bearer ${token}` };
|
|
@@ -1550,6 +1545,96 @@ const runUpdate = async () => {
|
|
|
1550
1545
|
}
|
|
1551
1546
|
};
|
|
1552
1547
|
|
|
1548
|
+
// ==================== UPDATE SINGLE DOMAIN ====================
|
|
1549
|
+
|
|
1550
|
+
const updateSingleDomain = async (domainName) => {
|
|
1551
|
+
const token = await getAuthToken();
|
|
1552
|
+
const spin = !showJson ? spinner(`Rilevamento IP per ${domainName}...`).start() : null;
|
|
1553
|
+
|
|
1554
|
+
try {
|
|
1555
|
+
// Get current IP
|
|
1556
|
+
const currentIP = await getCurrentIP('https://api.ipify.org').catch(() => null);
|
|
1557
|
+
const currentIPv6 = await getCurrentIPv6('https://api6.ipify.org').catch(() => null);
|
|
1558
|
+
|
|
1559
|
+
if (!currentIP && !currentIPv6) {
|
|
1560
|
+
spin?.fail("Nessun IP rilevato");
|
|
1561
|
+
if (showJson) {
|
|
1562
|
+
console.log(JSON.stringify({ error: "Nessun IP rilevato" }));
|
|
1563
|
+
}
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
if (spin) spin.text = `Ricerca dominio ${domainName}...`;
|
|
1568
|
+
|
|
1569
|
+
// First, get domain list to find the domain ID
|
|
1570
|
+
const listRes = await fetch(`${API_BASE}/domains`, {
|
|
1571
|
+
headers: getAuthHeaders(token)
|
|
1572
|
+
});
|
|
1573
|
+
|
|
1574
|
+
if (!listRes.ok) {
|
|
1575
|
+
const errData = await listRes.json().catch(() => ({}));
|
|
1576
|
+
spin?.fail(`Errore autenticazione: ${errData.error || errData.message || 'Token non valido'}`);
|
|
1577
|
+
if (showJson) {
|
|
1578
|
+
console.log(JSON.stringify({ error: errData.error || errData.message || 'Autenticazione fallita' }));
|
|
1579
|
+
}
|
|
1580
|
+
return;
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
const listData = await listRes.json();
|
|
1584
|
+
const domains = listData.domains || listData;
|
|
1585
|
+
const domain = domains.find(d => d.name === domainName || d.name.toLowerCase() === domainName.toLowerCase());
|
|
1586
|
+
|
|
1587
|
+
if (!domain) {
|
|
1588
|
+
spin?.fail(`Dominio "${domainName}" non trovato nel tuo account`);
|
|
1589
|
+
if (showJson) {
|
|
1590
|
+
console.log(JSON.stringify({ error: `Dominio "${domainName}" non trovato` }));
|
|
1591
|
+
}
|
|
1592
|
+
return;
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
if (spin) spin.text = `Aggiornamento DNS per ${domainName}...`;
|
|
1596
|
+
|
|
1597
|
+
// Use PATCH /domains/:id to update (supports API Key)
|
|
1598
|
+
const updateBody = { ip: currentIP };
|
|
1599
|
+
if (currentIPv6) updateBody.ipv6 = currentIPv6;
|
|
1600
|
+
|
|
1601
|
+
const res = await fetch(`${API_BASE}/domains/${domain.id}`, {
|
|
1602
|
+
method: "PATCH",
|
|
1603
|
+
headers: {
|
|
1604
|
+
"Content-Type": "application/json",
|
|
1605
|
+
...getAuthHeaders(token)
|
|
1606
|
+
},
|
|
1607
|
+
body: JSON.stringify(updateBody)
|
|
1608
|
+
});
|
|
1609
|
+
|
|
1610
|
+
const data = await res.json();
|
|
1611
|
+
|
|
1612
|
+
if (res.ok) {
|
|
1613
|
+
spin?.succeed(`DNS aggiornato! ${domainName} → ${currentIP}`);
|
|
1614
|
+
if (showJson) {
|
|
1615
|
+
console.log(JSON.stringify({
|
|
1616
|
+
success: true,
|
|
1617
|
+
domain: domainName,
|
|
1618
|
+
ip: currentIP,
|
|
1619
|
+
ipv6: currentIPv6 || null,
|
|
1620
|
+
previousIp: domain.ip,
|
|
1621
|
+
result: data
|
|
1622
|
+
}, null, 2));
|
|
1623
|
+
}
|
|
1624
|
+
} else {
|
|
1625
|
+
spin?.fail(`Errore: ${data.error || data.details || data.message}`);
|
|
1626
|
+
if (showJson) {
|
|
1627
|
+
console.log(JSON.stringify({ error: data.error || data.details || data.message }));
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
} catch (err) {
|
|
1631
|
+
spin?.fail(err.message);
|
|
1632
|
+
if (showJson) {
|
|
1633
|
+
console.log(JSON.stringify({ error: err.message }));
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
};
|
|
1637
|
+
|
|
1553
1638
|
// ==================== DAEMON MODE ====================
|
|
1554
1639
|
|
|
1555
1640
|
const runDaemonMode = async () => {
|
|
@@ -1602,96 +1687,6 @@ const runDaemonMode = async () => {
|
|
|
1602
1687
|
setInterval(update, daemonInterval * 1000);
|
|
1603
1688
|
};
|
|
1604
1689
|
|
|
1605
|
-
// ==================== STANDALONE UPDATE (DynDNS2) ====================
|
|
1606
|
-
|
|
1607
|
-
const runStandaloneUpdate = async (domain, token, customIp) => {
|
|
1608
|
-
// Validate required parameters
|
|
1609
|
-
if (!domain || !token) {
|
|
1610
|
-
if (showJson) {
|
|
1611
|
-
console.log(JSON.stringify({
|
|
1612
|
-
error: "Parametri mancanti. Usa: --update --domain <domain> --token <token> [--ip <ip>]"
|
|
1613
|
-
}));
|
|
1614
|
-
} else {
|
|
1615
|
-
console.log(red("\n❌ Parametri mancanti."));
|
|
1616
|
-
console.log(gray(" Uso: apertodns --update --domain <domain> --token <token> [--ip <ip>]\n"));
|
|
1617
|
-
}
|
|
1618
|
-
process.exit(1);
|
|
1619
|
-
}
|
|
1620
|
-
|
|
1621
|
-
const spin = !showJson ? spinner("Aggiornamento DNS via DynDNS2...").start() : null;
|
|
1622
|
-
|
|
1623
|
-
try {
|
|
1624
|
-
// Get current IP if not provided
|
|
1625
|
-
let ipToUse = customIp;
|
|
1626
|
-
if (!ipToUse) {
|
|
1627
|
-
if (spin) spin.text = "Rilevamento IP pubblico...";
|
|
1628
|
-
ipToUse = await getCurrentIP('https://api.ipify.org');
|
|
1629
|
-
if (!ipToUse) {
|
|
1630
|
-
throw new Error("Impossibile rilevare IP pubblico");
|
|
1631
|
-
}
|
|
1632
|
-
ipToUse = ipToUse.trim();
|
|
1633
|
-
}
|
|
1634
|
-
|
|
1635
|
-
if (spin) spin.text = `Aggiornamento ${domain} → ${ipToUse}...`;
|
|
1636
|
-
|
|
1637
|
-
// Build DynDNS2 URL
|
|
1638
|
-
const updateUrl = `https://api.apertodns.com/nic/update?hostname=${encodeURIComponent(domain)}&myip=${encodeURIComponent(ipToUse)}`;
|
|
1639
|
-
|
|
1640
|
-
// Make request with Basic Auth (DynDNS2 protocol)
|
|
1641
|
-
const authHeader = 'Basic ' + Buffer.from(`${domain}:${token}`).toString('base64');
|
|
1642
|
-
|
|
1643
|
-
const res = await fetch(updateUrl, {
|
|
1644
|
-
method: 'GET',
|
|
1645
|
-
headers: {
|
|
1646
|
-
'Authorization': authHeader,
|
|
1647
|
-
'User-Agent': `ApertoDNS-CLI/${CURRENT_VERSION}`
|
|
1648
|
-
}
|
|
1649
|
-
});
|
|
1650
|
-
|
|
1651
|
-
const responseText = await res.text();
|
|
1652
|
-
|
|
1653
|
-
// Parse DynDNS2 response
|
|
1654
|
-
const isSuccess = responseText.startsWith('good') || responseText.startsWith('nochg');
|
|
1655
|
-
|
|
1656
|
-
if (isSuccess) {
|
|
1657
|
-
spin?.succeed(`DNS aggiornato! ${domain} → ${ipToUse}`);
|
|
1658
|
-
|
|
1659
|
-
if (showJson) {
|
|
1660
|
-
console.log(JSON.stringify({
|
|
1661
|
-
success: true,
|
|
1662
|
-
domain,
|
|
1663
|
-
ip: ipToUse,
|
|
1664
|
-
response: responseText.trim(),
|
|
1665
|
-
timestamp: new Date().toISOString()
|
|
1666
|
-
}, null, 2));
|
|
1667
|
-
} else if (!isCron) {
|
|
1668
|
-
console.log(` ${gray('Risposta:')} ${green(responseText.trim())}`);
|
|
1669
|
-
console.log();
|
|
1670
|
-
}
|
|
1671
|
-
} else {
|
|
1672
|
-
spin?.fail(`Errore aggiornamento: ${responseText.trim()}`);
|
|
1673
|
-
|
|
1674
|
-
if (showJson) {
|
|
1675
|
-
console.log(JSON.stringify({
|
|
1676
|
-
success: false,
|
|
1677
|
-
domain,
|
|
1678
|
-
ip: ipToUse,
|
|
1679
|
-
error: responseText.trim(),
|
|
1680
|
-
timestamp: new Date().toISOString()
|
|
1681
|
-
}, null, 2));
|
|
1682
|
-
}
|
|
1683
|
-
process.exit(1);
|
|
1684
|
-
}
|
|
1685
|
-
} catch (err) {
|
|
1686
|
-
spin?.fail(`Errore: ${err.message}`);
|
|
1687
|
-
|
|
1688
|
-
if (showJson) {
|
|
1689
|
-
console.log(JSON.stringify({ error: err.message }));
|
|
1690
|
-
}
|
|
1691
|
-
process.exit(1);
|
|
1692
|
-
}
|
|
1693
|
-
};
|
|
1694
|
-
|
|
1695
1690
|
// ==================== LOGOUT ====================
|
|
1696
1691
|
|
|
1697
1692
|
const runLogout = async () => {
|
|
@@ -1804,7 +1799,6 @@ const interactiveMode = async () => {
|
|
|
1804
1799
|
const main = async () => {
|
|
1805
1800
|
try {
|
|
1806
1801
|
if (logout) await runLogout();
|
|
1807
|
-
else if (standaloneUpdate) await runStandaloneUpdate(updateDomain, updateToken, updateIp);
|
|
1808
1802
|
else if (showMyIp) await showMyIpCommand();
|
|
1809
1803
|
else if (runDaemon) await runDaemonMode();
|
|
1810
1804
|
else if (enableTokenId) await updateTokenState(enableTokenId, true);
|
|
@@ -1814,6 +1808,7 @@ const main = async () => {
|
|
|
1814
1808
|
else if (listDomains) await showDomainsList();
|
|
1815
1809
|
else if (addDomainArg) await addDomain(addDomainArg);
|
|
1816
1810
|
else if (deleteDomainArg) await deleteDomain(deleteDomainArg);
|
|
1811
|
+
else if (updateDomainArg) await updateSingleDomain(updateDomainArg);
|
|
1817
1812
|
else if (testDns) await testDnsResolution(testDns);
|
|
1818
1813
|
else if (listTokens) await showTokensList();
|
|
1819
1814
|
else if (showStats) await showStatsCommand();
|
package/package.json
CHANGED