apertodns 1.0.1 → 1.2.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.
Files changed (4) hide show
  1. package/README.md +190 -9
  2. package/index.js +817 -206
  3. package/package.json +58 -49
  4. package/utils.js +151 -14
package/package.json CHANGED
@@ -1,51 +1,60 @@
1
1
  {
2
- "name": "apertodns",
3
- "version": "1.0.1",
4
- "description": "ApertoDNS CLI - Dynamic DNS management from your terminal. Manage domains, tokens, and DNS updates.",
5
- "main": "index.js",
6
- "type": "module",
7
- "bin": {
8
- "apertodns": "./index.js"
9
- },
10
- "scripts": {
11
- "start": "node index.js"
12
- },
13
- "keywords": [
14
- "ddns",
15
- "cli",
16
- "dns",
17
- "apertodns",
18
- "dynamic-dns",
19
- "ipv4",
20
- "ipv6",
21
- "domain",
22
- "terminal"
23
- ],
24
- "author": "Aperto Network <info@apertodns.com>",
25
- "license": "MIT",
26
- "repository": {
27
- "type": "git",
28
- "url": "https://github.com/apertonetwork/apertodns.git"
29
- },
30
- "homepage": "https://apertodns.com",
31
- "bugs": {
32
- "url": "https://github.com/apertonetwork/apertodns/issues"
33
- },
34
- "engines": {
35
- "node": ">=18.0.0"
36
- },
37
- "files": [
38
- "index.js",
39
- "utils.js",
40
- "README.md"
41
- ],
42
- "dependencies": {
43
- "chalk": "^5.3.0",
44
- "cli-spinners": "^3.3.0",
45
- "cli-table3": "^0.6.5",
46
- "figlet": "^1.6.0",
47
- "inquirer": "^9.3.8",
48
- "node-fetch": "^3.3.2",
49
- "ora": "^9.0.0"
50
- }
2
+ "name": "apertodns",
3
+ "version": "1.2.0",
4
+ "description": "ApertoDNS CLI - Dynamic DNS management from your terminal. Manage domains, tokens, API keys and DNS updates with style.",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "apertodns": "./index.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node index.js",
12
+ "test": "node index.js --version"
13
+ },
14
+ "keywords": [
15
+ "ddns",
16
+ "cli",
17
+ "dns",
18
+ "apertodns",
19
+ "dynamic-dns",
20
+ "dyndns",
21
+ "dyndns2",
22
+ "ipv4",
23
+ "ipv6",
24
+ "domain",
25
+ "terminal",
26
+ "api-keys",
27
+ "webhooks",
28
+ "nas",
29
+ "synology",
30
+ "qnap",
31
+ "router"
32
+ ],
33
+ "author": "Aperto Network <info@apertodns.com>",
34
+ "license": "MIT",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "https://github.com/1r0n3d3v3l0per/apertodns.git"
38
+ },
39
+ "homepage": "https://apertodns.com",
40
+ "bugs": {
41
+ "url": "https://github.com/1r0n3d3v3l0per/apertodns/issues"
42
+ },
43
+ "engines": {
44
+ "node": ">=18.0.0"
45
+ },
46
+ "files": [
47
+ "index.js",
48
+ "utils.js",
49
+ "README.md"
50
+ ],
51
+ "dependencies": {
52
+ "chalk": "^5.3.0",
53
+ "cli-spinners": "^3.3.0",
54
+ "cli-table3": "^0.6.5",
55
+ "figlet": "^1.6.0",
56
+ "inquirer": "^9.3.8",
57
+ "node-fetch": "^3.3.2",
58
+ "ora": "^9.0.0"
59
+ }
51
60
  }
package/utils.js CHANGED
@@ -1,35 +1,172 @@
1
1
  import fetch from "node-fetch";
2
2
  import fs from "fs";
3
3
  import path from "path";
4
+ import os from "os";
4
5
  import { fileURLToPath } from "url";
5
6
 
6
7
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
- const dataDir = path.resolve(__dirname, ".data");
8
- const ipPath = path.join(dataDir, "last_ip_ironDNS.txt");
9
8
 
10
- if (!fs.existsSync(dataDir)) {
11
- fs.mkdirSync(dataDir, { recursive: true });
12
- }
9
+ // Get config directory based on platform (XDG compliant)
10
+ export const getConfigDir = () => {
11
+ // Check for XDG config home first (Linux standard)
12
+ if (process.env.XDG_CONFIG_HOME) {
13
+ return path.join(process.env.XDG_CONFIG_HOME, "apertodns");
14
+ }
15
+
16
+ // Platform-specific defaults
17
+ const platform = os.platform();
18
+
19
+ if (platform === "win32") {
20
+ // Windows: use APPDATA
21
+ return path.join(process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming"), "apertodns");
22
+ }
23
+
24
+ // macOS and Linux: use ~/.config/apertodns or ~/.apertodns
25
+ const configDir = path.join(os.homedir(), ".config", "apertodns");
26
+
27
+ // Fallback to ~/.apertodns if .config doesn't exist
28
+ if (!fs.existsSync(path.join(os.homedir(), ".config"))) {
29
+ return path.join(os.homedir(), ".apertodns");
30
+ }
31
+
32
+ return configDir;
33
+ };
34
+
35
+ // Get data directory for cache and temp files
36
+ export const getDataDir = () => {
37
+ const configDir = getConfigDir();
38
+ const dataDir = path.join(configDir, ".data");
39
+
40
+ if (!fs.existsSync(dataDir)) {
41
+ fs.mkdirSync(dataDir, { recursive: true });
42
+ }
43
+
44
+ return dataDir;
45
+ };
46
+
47
+ const dataDir = getDataDir();
48
+ const ipPath = path.join(dataDir, "last_ip.txt");
49
+ const ipv6Path = path.join(dataDir, "last_ipv6.txt");
13
50
 
14
51
  export const log = (msg) => {
15
52
  const time = new Date().toISOString().replace("T", " ").substring(0, 19);
16
53
  console.log(`[${time}] ${msg}`);
17
54
  };
18
55
 
19
- export const getCurrentIP = async (ipService) => {
20
- const res = await fetch(ipService);
21
- return res.text();
56
+ export const getCurrentIP = async (ipService = 'https://api.ipify.org') => {
57
+ try {
58
+ const controller = new AbortController();
59
+ const timeout = setTimeout(() => controller.abort(), 10000);
60
+
61
+ const res = await fetch(ipService, { signal: controller.signal });
62
+ clearTimeout(timeout);
63
+
64
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
65
+ const ip = (await res.text()).trim();
66
+
67
+ // Validate IPv4
68
+ const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
69
+ if (!ipv4Regex.test(ip)) {
70
+ throw new Error("Invalid IPv4 format");
71
+ }
72
+
73
+ return ip;
74
+ } catch (err) {
75
+ // Try fallback services
76
+ const fallbacks = [
77
+ 'https://api.ipify.org',
78
+ 'https://ifconfig.me/ip',
79
+ 'https://icanhazip.com',
80
+ 'https://checkip.amazonaws.com'
81
+ ];
82
+
83
+ for (const fallback of fallbacks) {
84
+ if (fallback === ipService) continue;
85
+
86
+ try {
87
+ const controller = new AbortController();
88
+ const timeout = setTimeout(() => controller.abort(), 5000);
89
+
90
+ const res = await fetch(fallback, { signal: controller.signal });
91
+ clearTimeout(timeout);
92
+
93
+ if (res.ok) {
94
+ const ip = (await res.text()).trim();
95
+ const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
96
+ if (ipv4Regex.test(ip)) {
97
+ return ip;
98
+ }
99
+ }
100
+ } catch {
101
+ continue;
102
+ }
103
+ }
104
+
105
+ throw new Error("Unable to detect IP address");
106
+ }
107
+ };
108
+
109
+ export const getCurrentIPv6 = async (ipService6 = 'https://api6.ipify.org') => {
110
+ try {
111
+ const controller = new AbortController();
112
+ const timeout = setTimeout(() => controller.abort(), 10000);
113
+
114
+ const res = await fetch(ipService6, { signal: controller.signal });
115
+ clearTimeout(timeout);
116
+
117
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
118
+ const ip = (await res.text()).trim();
119
+
120
+ // Basic IPv6 validation (contains colons)
121
+ if (!ip.includes(':')) {
122
+ throw new Error("Invalid IPv6 format");
123
+ }
124
+
125
+ return ip;
126
+ } catch (err) {
127
+ // IPv6 might not be available, return null instead of throwing
128
+ return null;
129
+ }
22
130
  };
23
131
 
24
132
  export const loadLastIP = () => {
25
- return fs.existsSync(ipPath) ? fs.readFileSync(ipPath, "utf-8") : null;
133
+ try {
134
+ return fs.existsSync(ipPath) ? fs.readFileSync(ipPath, "utf-8").trim() : null;
135
+ } catch {
136
+ return null;
137
+ }
26
138
  };
27
139
 
28
140
  export const saveCurrentIP = (ip) => {
29
- fs.writeFileSync(ipPath, ip);
141
+ try {
142
+ fs.writeFileSync(ipPath, ip);
143
+ } catch (err) {
144
+ // Silently fail - non-critical
145
+ }
146
+ };
147
+
148
+ export const loadLastIPv6 = () => {
149
+ try {
150
+ return fs.existsSync(ipv6Path) ? fs.readFileSync(ipv6Path, "utf-8").trim() : null;
151
+ } catch {
152
+ return null;
153
+ }
30
154
  };
31
155
 
32
- export const getCurrentIPv6 = async (ipService6) => {
33
- const res = await fetch(ipService6);
34
- return res.text();
35
- };
156
+ export const saveCurrentIPv6 = (ip) => {
157
+ try {
158
+ fs.writeFileSync(ipv6Path, ip);
159
+ } catch (err) {
160
+ // Silently fail - non-critical
161
+ }
162
+ };
163
+
164
+ // Export package info
165
+ export const getPackageInfo = () => {
166
+ try {
167
+ const pkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, "package.json"), "utf-8"));
168
+ return { name: pkg.name, version: pkg.version };
169
+ } catch {
170
+ return { name: "apertodns", version: "0.0.0" };
171
+ }
172
+ };