apertodns 2.0.2 → 2.0.3
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 +23 -3
- package/index.js +148 -2
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/apertodns)
|
|
4
4
|
[](https://www.npmjs.com/package/apertodns)
|
|
5
|
-
[](https://github.com/
|
|
5
|
+
[](https://github.com/apertodns/apertodns/blob/main/LICENSE)
|
|
6
6
|
[](https://nodejs.org)
|
|
7
7
|
|
|
8
8
|
**Dynamic DNS management from your terminal.** Manage domains, tokens, API keys, and DNS updates with style.
|
|
@@ -100,6 +100,26 @@ apertodns --force
|
|
|
100
100
|
| `--test <domain>` | Test DNS resolution |
|
|
101
101
|
| `update <domain>` | Update a specific domain's IP (use with `--api-key`) |
|
|
102
102
|
|
|
103
|
+
### TXT Records (ACME DNS-01)
|
|
104
|
+
|
|
105
|
+
| Command | Description |
|
|
106
|
+
|---------|-------------|
|
|
107
|
+
| `--txt-set <host> <name> <value>` | Set a TXT record |
|
|
108
|
+
| `--txt-delete <host> <name>` | Delete a TXT record |
|
|
109
|
+
|
|
110
|
+
Perfect for Let's Encrypt DNS-01 challenges:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Set TXT record for certificate validation
|
|
114
|
+
apertodns --txt-set example.apertodns.com _acme-challenge "validation-token"
|
|
115
|
+
|
|
116
|
+
# Delete TXT record after certificate issuance
|
|
117
|
+
apertodns --txt-delete example.apertodns.com _acme-challenge
|
|
118
|
+
|
|
119
|
+
# Use with API key for automation
|
|
120
|
+
apertodns --api-key apertodns_live_xxx... --txt-set example.apertodns.com _acme-challenge "token" --json
|
|
121
|
+
```
|
|
122
|
+
|
|
103
123
|
### Token Management
|
|
104
124
|
|
|
105
125
|
| Command | Description |
|
|
@@ -359,12 +379,12 @@ APERTODNS_API_KEY=apertodns_live_xxx... apertodns --domains --json
|
|
|
359
379
|
- **Website**: [apertodns.com](https://apertodns.com)
|
|
360
380
|
- **Dashboard**: [apertodns.com/dashboard](https://apertodns.com/dashboard)
|
|
361
381
|
- **Documentation**: [apertodns.com/docs](https://apertodns.com/docs)
|
|
362
|
-
- **Issues**: [GitHub Issues](https://github.com/
|
|
382
|
+
- **Issues**: [GitHub Issues](https://github.com/apertodns/apertodns/issues)
|
|
363
383
|
|
|
364
384
|
## Support
|
|
365
385
|
|
|
366
386
|
Need help?
|
|
367
|
-
- Open an issue on [GitHub](https://github.com/
|
|
387
|
+
- Open an issue on [GitHub](https://github.com/apertodns/apertodns/issues)
|
|
368
388
|
- Email: support@apertodns.com
|
|
369
389
|
|
|
370
390
|
## License
|
package/index.js
CHANGED
|
@@ -184,6 +184,19 @@ const daemonInterval = getOption("--interval") ? parseInt(getOption("--interval"
|
|
|
184
184
|
const showMyIp = args.includes("--my-ip") || args.includes("--ip") || subcommand === "ip" || subcommand === "my-ip";
|
|
185
185
|
const logout = args.includes("--logout") || subcommand === "logout";
|
|
186
186
|
|
|
187
|
+
// TXT record commands (ACME DNS-01 challenges)
|
|
188
|
+
const txtSetIdx = args.indexOf("--txt-set");
|
|
189
|
+
const txtSetArgs = txtSetIdx !== -1 ? {
|
|
190
|
+
hostname: args[txtSetIdx + 1],
|
|
191
|
+
name: args[txtSetIdx + 2],
|
|
192
|
+
value: args[txtSetIdx + 3]
|
|
193
|
+
} : null;
|
|
194
|
+
const txtDeleteIdx = args.indexOf("--txt-delete");
|
|
195
|
+
const txtDeleteArgs = txtDeleteIdx !== -1 ? {
|
|
196
|
+
hostname: args[txtDeleteIdx + 1],
|
|
197
|
+
name: args[txtDeleteIdx + 2]
|
|
198
|
+
} : null;
|
|
199
|
+
|
|
187
200
|
// JSON output helper
|
|
188
201
|
const jsonOutput = (data) => {
|
|
189
202
|
if (showJson) {
|
|
@@ -214,6 +227,10 @@ ${chalk.bold("GESTIONE DOMINI:")}
|
|
|
214
227
|
${cyan("--delete-domain")} Elimina un dominio (interattivo)
|
|
215
228
|
${cyan("--test")} <domain> Testa risoluzione DNS di un dominio
|
|
216
229
|
|
|
230
|
+
${chalk.bold("TXT RECORDS (ACME DNS-01):")}
|
|
231
|
+
${cyan("--txt-set")} <host> <name> <val> Imposta record TXT
|
|
232
|
+
${cyan("--txt-delete")} <host> <name> Elimina record TXT
|
|
233
|
+
|
|
217
234
|
${chalk.bold("GESTIONE TOKEN:")}
|
|
218
235
|
${cyan("--enable")} <id> Attiva un token
|
|
219
236
|
${cyan("--disable")} <id> Disattiva un token
|
|
@@ -361,9 +378,15 @@ const fetchDomains = async () => {
|
|
|
361
378
|
const token = await getAuthToken();
|
|
362
379
|
const spin = !showJson ? spinner("Caricamento domini...").start() : null;
|
|
363
380
|
try {
|
|
381
|
+
const controller = new AbortController();
|
|
382
|
+
const timeout = setTimeout(() => controller.abort(), 15000);
|
|
383
|
+
|
|
364
384
|
const res = await fetch(`${API_BASE}/domains`, {
|
|
365
|
-
headers: getAuthHeaders(token)
|
|
385
|
+
headers: getAuthHeaders(token),
|
|
386
|
+
signal: controller.signal
|
|
366
387
|
});
|
|
388
|
+
clearTimeout(timeout);
|
|
389
|
+
|
|
367
390
|
spin?.stop();
|
|
368
391
|
if (!res.ok) {
|
|
369
392
|
const err = await res.json().catch(() => ({}));
|
|
@@ -371,7 +394,7 @@ const fetchDomains = async () => {
|
|
|
371
394
|
}
|
|
372
395
|
return await res.json();
|
|
373
396
|
} catch (err) {
|
|
374
|
-
spin?.fail("Errore caricamento domini");
|
|
397
|
+
spin?.fail(err.name === 'AbortError' ? "Timeout caricamento domini" : "Errore caricamento domini");
|
|
375
398
|
throw err;
|
|
376
399
|
}
|
|
377
400
|
};
|
|
@@ -636,6 +659,127 @@ const testDnsResolution = async (domain) => {
|
|
|
636
659
|
}
|
|
637
660
|
};
|
|
638
661
|
|
|
662
|
+
// ==================== TXT RECORDS ====================
|
|
663
|
+
|
|
664
|
+
const IETF_BASE = "https://api.apertodns.com/.well-known/apertodns/v1";
|
|
665
|
+
|
|
666
|
+
const setTxtRecord = async (hostname, name, value) => {
|
|
667
|
+
if (!hostname || !name || !value) {
|
|
668
|
+
if (showJson) {
|
|
669
|
+
console.log(JSON.stringify({ error: "Uso: --txt-set <hostname> <name> <value>" }));
|
|
670
|
+
} else {
|
|
671
|
+
console.log(red("\n❌ Uso: --txt-set <hostname> <name> <value>"));
|
|
672
|
+
console.log(gray(" Esempio: --txt-set mio.apertodns.com _acme-challenge abc123\n"));
|
|
673
|
+
}
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
const token = await getAuthToken();
|
|
678
|
+
const spin = !showJson ? spinner(`Impostazione TXT ${name}.${hostname}...`).start() : null;
|
|
679
|
+
|
|
680
|
+
try {
|
|
681
|
+
const controller = new AbortController();
|
|
682
|
+
const timeout = setTimeout(() => controller.abort(), 15000);
|
|
683
|
+
|
|
684
|
+
const res = await fetch(`${IETF_BASE}/update`, {
|
|
685
|
+
method: "POST",
|
|
686
|
+
headers: {
|
|
687
|
+
"Content-Type": "application/json",
|
|
688
|
+
...getAuthHeaders(token)
|
|
689
|
+
},
|
|
690
|
+
body: JSON.stringify({
|
|
691
|
+
hostname,
|
|
692
|
+
txt: {
|
|
693
|
+
name,
|
|
694
|
+
value,
|
|
695
|
+
action: "set"
|
|
696
|
+
}
|
|
697
|
+
}),
|
|
698
|
+
signal: controller.signal
|
|
699
|
+
});
|
|
700
|
+
clearTimeout(timeout);
|
|
701
|
+
|
|
702
|
+
const data = await res.json();
|
|
703
|
+
|
|
704
|
+
if (res.ok && data.success !== false) {
|
|
705
|
+
spin?.succeed(`TXT record impostato: ${name}.${hostname}`);
|
|
706
|
+
if (showJson) {
|
|
707
|
+
console.log(JSON.stringify({ success: true, hostname, txt: { name, value, action: "set" }, response: data }, null, 2));
|
|
708
|
+
} else {
|
|
709
|
+
console.log(gray(` Nome: ${cyan(name)}`));
|
|
710
|
+
console.log(gray(` Valore: ${cyan(value)}`));
|
|
711
|
+
console.log();
|
|
712
|
+
}
|
|
713
|
+
} else {
|
|
714
|
+
spin?.fail(`Errore: ${data.error || data.message || 'TXT non supportato'}`);
|
|
715
|
+
if (showJson) {
|
|
716
|
+
console.log(JSON.stringify({ error: data.error || data.message }));
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
} catch (err) {
|
|
720
|
+
spin?.fail(err.name === 'AbortError' ? "Timeout" : err.message);
|
|
721
|
+
if (showJson) {
|
|
722
|
+
console.log(JSON.stringify({ error: err.message }));
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
const deleteTxtRecord = async (hostname, name) => {
|
|
728
|
+
if (!hostname || !name) {
|
|
729
|
+
if (showJson) {
|
|
730
|
+
console.log(JSON.stringify({ error: "Uso: --txt-delete <hostname> <name>" }));
|
|
731
|
+
} else {
|
|
732
|
+
console.log(red("\n❌ Uso: --txt-delete <hostname> <name>"));
|
|
733
|
+
console.log(gray(" Esempio: --txt-delete mio.apertodns.com _acme-challenge\n"));
|
|
734
|
+
}
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
const token = await getAuthToken();
|
|
739
|
+
const spin = !showJson ? spinner(`Eliminazione TXT ${name}.${hostname}...`).start() : null;
|
|
740
|
+
|
|
741
|
+
try {
|
|
742
|
+
const controller = new AbortController();
|
|
743
|
+
const timeout = setTimeout(() => controller.abort(), 15000);
|
|
744
|
+
|
|
745
|
+
const res = await fetch(`${IETF_BASE}/update`, {
|
|
746
|
+
method: "POST",
|
|
747
|
+
headers: {
|
|
748
|
+
"Content-Type": "application/json",
|
|
749
|
+
...getAuthHeaders(token)
|
|
750
|
+
},
|
|
751
|
+
body: JSON.stringify({
|
|
752
|
+
hostname,
|
|
753
|
+
txt: {
|
|
754
|
+
name,
|
|
755
|
+
action: "delete"
|
|
756
|
+
}
|
|
757
|
+
}),
|
|
758
|
+
signal: controller.signal
|
|
759
|
+
});
|
|
760
|
+
clearTimeout(timeout);
|
|
761
|
+
|
|
762
|
+
const data = await res.json();
|
|
763
|
+
|
|
764
|
+
if (res.ok && data.success !== false) {
|
|
765
|
+
spin?.succeed(`TXT record eliminato: ${name}.${hostname}`);
|
|
766
|
+
if (showJson) {
|
|
767
|
+
console.log(JSON.stringify({ success: true, hostname, txt: { name, action: "delete" }, response: data }, null, 2));
|
|
768
|
+
}
|
|
769
|
+
} else {
|
|
770
|
+
spin?.fail(`Errore: ${data.error || data.message || 'TXT non supportato'}`);
|
|
771
|
+
if (showJson) {
|
|
772
|
+
console.log(JSON.stringify({ error: data.error || data.message }));
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
} catch (err) {
|
|
776
|
+
spin?.fail(err.name === 'AbortError' ? "Timeout" : err.message);
|
|
777
|
+
if (showJson) {
|
|
778
|
+
console.log(JSON.stringify({ error: err.message }));
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
};
|
|
782
|
+
|
|
639
783
|
// ==================== TOKENS ====================
|
|
640
784
|
|
|
641
785
|
const fetchTokens = async () => {
|
|
@@ -1799,6 +1943,8 @@ const interactiveMode = async () => {
|
|
|
1799
1943
|
const main = async () => {
|
|
1800
1944
|
try {
|
|
1801
1945
|
if (logout) await runLogout();
|
|
1946
|
+
else if (txtSetArgs) await setTxtRecord(txtSetArgs.hostname, txtSetArgs.name, txtSetArgs.value);
|
|
1947
|
+
else if (txtDeleteArgs) await deleteTxtRecord(txtDeleteArgs.hostname, txtDeleteArgs.name);
|
|
1802
1948
|
else if (showMyIp) await showMyIpCommand();
|
|
1803
1949
|
else if (runDaemon) await runDaemonMode();
|
|
1804
1950
|
else if (enableTokenId) await updateTokenState(enableTokenId, true);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apertodns",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"description": "ApertoDNS CLI - Dynamic DNS management from your terminal. Manage domains, tokens, API keys and DNS updates with style.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -30,15 +30,15 @@
|
|
|
30
30
|
"qnap",
|
|
31
31
|
"router"
|
|
32
32
|
],
|
|
33
|
-
"author": "
|
|
33
|
+
"author": "Andrea Ferro <support@apertodns.com>",
|
|
34
34
|
"license": "MIT",
|
|
35
35
|
"repository": {
|
|
36
36
|
"type": "git",
|
|
37
|
-
"url": "https://github.com/
|
|
37
|
+
"url": "https://github.com/apertodns/apertodns.git"
|
|
38
38
|
},
|
|
39
39
|
"homepage": "https://apertodns.com",
|
|
40
40
|
"bugs": {
|
|
41
|
-
"url": "https://github.com/
|
|
41
|
+
"url": "https://github.com/apertodns/apertodns/issues"
|
|
42
42
|
},
|
|
43
43
|
"engines": {
|
|
44
44
|
"node": ">=18.0.0"
|