apertodns 1.2.5 → 2.0.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 +63 -8
- package/index.js +2 -109
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,16 +11,19 @@ ApertoDNS is a free Dynamic DNS service that lets you point a subdomain to your
|
|
|
11
11
|
|
|
12
12
|
## Why ApertoDNS?
|
|
13
13
|
|
|
14
|
-
| Feature | ApertoDNS | No-IP | DuckDNS |
|
|
15
|
-
|
|
16
|
-
| Free
|
|
17
|
-
|
|
|
14
|
+
| Feature | ApertoDNS | Dyn (Oracle) | No-IP | DuckDNS |
|
|
15
|
+
|---------|-----------|--------------|-------|---------|
|
|
16
|
+
| Free plan | Yes | No ($55/yr) | Yes | Yes |
|
|
17
|
+
| Free subdomains | Unlimited | 0 | 1 | 5 |
|
|
18
|
+
| API Keys with scopes | Yes | No | No | No |
|
|
18
19
|
| CLI tool | Yes | No | No | No |
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
20
|
+
| Docker images | Yes | No | No | No |
|
|
21
|
+
| IPv6 support | Yes | Paid | Paid | Yes |
|
|
22
|
+
| No forced renewal | Yes | N/A | 30 days | Yes |
|
|
23
|
+
| DynDNS2 compatible | Yes | Yes | Yes | No |
|
|
22
24
|
| Webhooks | Yes | No | No | No |
|
|
23
25
|
| Team sharing | Yes | No | No | No |
|
|
26
|
+
| Open Source | Yes | No | No | No |
|
|
24
27
|
|
|
25
28
|
## Features
|
|
26
29
|
|
|
@@ -33,6 +36,7 @@ ApertoDNS is a free Dynamic DNS service that lets you point a subdomain to your
|
|
|
33
36
|
- **IPv4 & IPv6** - Full dual-stack support
|
|
34
37
|
- **Real-time Stats** - View usage statistics and logs
|
|
35
38
|
- **Router/NAS Compatible** - Works with Synology, QNAP, and DynDNS2-compatible routers
|
|
39
|
+
- **Docker Images** - Official [apertodns/cli](https://hub.docker.com/r/apertodns/cli) and [apertodns/updater](https://hub.docker.com/r/apertodns/updater) images
|
|
36
40
|
|
|
37
41
|
## Requirements
|
|
38
42
|
|
|
@@ -123,7 +127,15 @@ apertodns --force
|
|
|
123
127
|
| `--config` | Edit configuration |
|
|
124
128
|
| `--logout` | Remove local configuration |
|
|
125
129
|
| `--force` | Force DNS update now |
|
|
126
|
-
|
|
130
|
+
|
|
131
|
+
### Standalone Update (DynDNS2)
|
|
132
|
+
|
|
133
|
+
| Command | Description |
|
|
134
|
+
|---------|-------------|
|
|
135
|
+
| `--update` | Standalone DynDNS2 update (no config required) |
|
|
136
|
+
| `--domain <fqdn>` | Domain to update (with --update) |
|
|
137
|
+
| `--token <token>` | Token for authentication (with --update) |
|
|
138
|
+
| `--ip <address>` | Custom IP address (optional, auto-detected if omitted) |
|
|
127
139
|
|
|
128
140
|
### Daemon Mode
|
|
129
141
|
|
|
@@ -185,6 +197,49 @@ apertodns --daemon
|
|
|
185
197
|
apertodns --daemon --interval 60
|
|
186
198
|
```
|
|
187
199
|
|
|
200
|
+
## Standalone Update
|
|
201
|
+
|
|
202
|
+
Update DNS without any saved configuration - perfect for scripts and one-off updates:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
# Auto-detect IP and update
|
|
206
|
+
apertodns --update --domain myserver.apertodns.com --token YOUR_TOKEN
|
|
207
|
+
|
|
208
|
+
# Specify custom IP
|
|
209
|
+
apertodns --update --domain myserver.apertodns.com --token YOUR_TOKEN --ip 203.0.113.42
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Docker
|
|
213
|
+
|
|
214
|
+
Run without installing Node.js:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
# Run CLI via Docker
|
|
218
|
+
docker run --rm apertodns/cli --help
|
|
219
|
+
|
|
220
|
+
# Interactive setup (persisted config)
|
|
221
|
+
docker run -it -v apertodns_config:/root/.config/apertodns apertodns/cli --setup
|
|
222
|
+
|
|
223
|
+
# List domains
|
|
224
|
+
docker run -v apertodns_config:/root/.config/apertodns apertodns/cli --domains
|
|
225
|
+
|
|
226
|
+
# Standalone update (no config needed)
|
|
227
|
+
docker run --rm apertodns/cli --update --domain myhost.apertodns.com --token YOUR_TOKEN
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
For continuous updates, use the dedicated updater image:
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
docker run -d \
|
|
234
|
+
--name apertodns-updater \
|
|
235
|
+
--restart unless-stopped \
|
|
236
|
+
-e TOKEN=your_token \
|
|
237
|
+
-e DOMAINS=myhost.apertodns.com \
|
|
238
|
+
apertodns/updater
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
See [Docker Hub](https://hub.docker.com/r/apertodns/cli) for more options.
|
|
242
|
+
|
|
188
243
|
## Automatic Updates (Cron)
|
|
189
244
|
|
|
190
245
|
Set up automatic IP updates with cron:
|
package/index.js
CHANGED
|
@@ -173,14 +173,6 @@ const daemonInterval = args.includes("--interval") ? parseInt(args[args.indexOf(
|
|
|
173
173
|
const showMyIp = args.includes("--my-ip") || args.includes("--ip");
|
|
174
174
|
const logout = args.includes("--logout");
|
|
175
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;
|
|
183
|
-
|
|
184
176
|
// JSON output helper
|
|
185
177
|
const jsonOutput = (data) => {
|
|
186
178
|
if (showJson) {
|
|
@@ -234,12 +226,6 @@ ${chalk.bold("CONFIGURAZIONE:")}
|
|
|
234
226
|
${cyan("--logout")} Rimuovi configurazione locale
|
|
235
227
|
${cyan("--force")} Forza aggiornamento DNS
|
|
236
228
|
|
|
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
229
|
${chalk.bold("DAEMON MODE:")}
|
|
244
230
|
${cyan("--daemon")} Avvia in modalità daemon (aggiornamento continuo)
|
|
245
231
|
${cyan("--interval")} <sec> Intervallo aggiornamento daemon (default: 300s)
|
|
@@ -267,8 +253,6 @@ ${gray("Esempi:")}
|
|
|
267
253
|
${gray("$")} apertodns --test mioserver.apertodns.com
|
|
268
254
|
${gray("$")} apertodns --daemon --interval 60
|
|
269
255
|
${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
256
|
|
|
273
257
|
${gray("Docs: https://apertodns.com/docs")}
|
|
274
258
|
`);
|
|
@@ -318,8 +302,8 @@ const spinner = (text) => ora({ text, spinner: "dots", color: "yellow" });
|
|
|
318
302
|
|
|
319
303
|
// Helper: get auth headers (supports both JWT and API Key)
|
|
320
304
|
const getAuthHeaders = (token) => {
|
|
321
|
-
// API Keys start with "ak_"
|
|
322
|
-
if (token && token.startsWith('ak_')) {
|
|
305
|
+
// API Keys start with "ak_" or "apertodns_live_" or "apertodns_test_"
|
|
306
|
+
if (token && (token.startsWith('ak_') || token.startsWith('apertodns_live_') || token.startsWith('apertodns_test_'))) {
|
|
323
307
|
return { 'X-API-Key': token };
|
|
324
308
|
}
|
|
325
309
|
return { Authorization: `Bearer ${token}` };
|
|
@@ -1602,96 +1586,6 @@ const runDaemonMode = async () => {
|
|
|
1602
1586
|
setInterval(update, daemonInterval * 1000);
|
|
1603
1587
|
};
|
|
1604
1588
|
|
|
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
1589
|
// ==================== LOGOUT ====================
|
|
1696
1590
|
|
|
1697
1591
|
const runLogout = async () => {
|
|
@@ -1804,7 +1698,6 @@ const interactiveMode = async () => {
|
|
|
1804
1698
|
const main = async () => {
|
|
1805
1699
|
try {
|
|
1806
1700
|
if (logout) await runLogout();
|
|
1807
|
-
else if (standaloneUpdate) await runStandaloneUpdate(updateDomain, updateToken, updateIp);
|
|
1808
1701
|
else if (showMyIp) await showMyIpCommand();
|
|
1809
1702
|
else if (runDaemon) await runDaemonMode();
|
|
1810
1703
|
else if (enableTokenId) await updateTokenState(enableTokenId, true);
|
package/package.json
CHANGED