underpost 2.8.884 → 2.8.886
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/.env.production +3 -0
- package/.github/workflows/ghpkg.ci.yml +1 -1
- package/.github/workflows/npmpkg.ci.yml +1 -1
- package/.github/workflows/publish.ci.yml +5 -5
- package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -1
- package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
- package/CHANGELOG.md +145 -1
- package/Dockerfile +1 -1
- package/README.md +5 -121
- package/bin/build.js +18 -9
- package/bin/deploy.js +102 -197
- package/bin/file.js +4 -6
- package/cli.md +16 -12
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +54 -54
- package/manifests/deployment/dd-test-development/proxy.yaml +4 -4
- package/manifests/lxd/underpost-setup.sh +5 -5
- package/package.json +3 -3
- package/scripts/ssl.sh +164 -0
- package/src/cli/baremetal.js +7 -7
- package/src/cli/cloud-init.js +1 -1
- package/src/cli/cluster.js +31 -3
- package/src/cli/cron.js +9 -1
- package/src/cli/db.js +64 -2
- package/src/cli/deploy.js +189 -4
- package/src/cli/env.js +43 -0
- package/src/cli/fs.js +96 -2
- package/src/cli/image.js +15 -0
- package/src/cli/index.js +17 -4
- package/src/cli/monitor.js +33 -2
- package/src/cli/repository.js +95 -2
- package/src/cli/run.js +315 -51
- package/src/cli/script.js +32 -0
- package/src/cli/secrets.js +34 -0
- package/src/cli/test.js +42 -1
- package/src/client/components/core/Css.js +16 -8
- package/src/client/components/core/Docs.js +5 -13
- package/src/client/components/core/Modal.js +48 -29
- package/src/client/components/core/Router.js +6 -3
- package/src/client/components/core/Worker.js +205 -118
- package/src/client/components/core/windowGetDimensions.js +229 -162
- package/src/client/components/default/MenuDefault.js +1 -0
- package/src/client.dev.js +6 -3
- package/src/db/DataBaseProvider.js +65 -12
- package/src/db/mariadb/MariaDB.js +39 -6
- package/src/db/mongo/MongooseDB.js +51 -133
- package/src/index.js +2 -2
- package/src/mailer/EmailRender.js +58 -9
- package/src/mailer/MailerProvider.js +99 -25
- package/src/runtime/express/Express.js +32 -38
- package/src/runtime/lampp/Dockerfile +1 -1
- package/src/server/auth.js +9 -28
- package/src/server/backup.js +20 -0
- package/src/server/client-build-live.js +23 -12
- package/src/server/client-build.js +136 -91
- package/src/server/client-dev-server.js +35 -8
- package/src/server/client-icons.js +19 -0
- package/src/server/conf.js +543 -80
- package/src/server/dns.js +184 -42
- package/src/server/downloader.js +65 -24
- package/src/server/object-layer.js +260 -162
- package/src/server/peer.js +3 -9
- package/src/server/proxy.js +93 -76
- package/src/server/runtime.js +15 -21
- package/src/server/ssr.js +4 -4
- package/src/server/start.js +39 -0
- package/src/server/tls.js +251 -0
- package/src/server/valkey.js +11 -10
- package/src/ws/IoInterface.js +133 -39
- package/src/ws/IoServer.js +80 -31
- package/src/ws/core/core.ws.connection.js +50 -16
- package/src/ws/core/core.ws.emit.js +47 -8
- package/src/ws/core/core.ws.server.js +62 -10
- package/manifests/maas/lxd-preseed.yaml +0 -32
- package/src/server/ssl.js +0 -108
- /package/{manifests/maas → scripts}/device-scan.sh +0 -0
- /package/{manifests/maas → scripts}/gpu-diag.sh +0 -0
- /package/{manifests/maas → scripts}/maas-setup.sh +0 -0
- /package/{manifests/maas → scripts}/nat-iptables.sh +0 -0
- /package/{manifests/maas → scripts}/nvim.sh +0 -0
- /package/{manifests/maas → scripts}/snap-clean.sh +0 -0
- /package/{manifests/maas → scripts}/ssh-cluster-info.sh +0 -0
package/src/server/dns.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provides a comprehensive set of DNS and IP management utilities,
|
|
3
|
+
* primarily focused on dynamic DNS (DDNS) updates and network checks.
|
|
4
|
+
* @module src/server/dns.js
|
|
5
|
+
* @namespace DnsManager
|
|
6
|
+
*/
|
|
1
7
|
import axios from 'axios';
|
|
2
8
|
import dotenv from 'dotenv';
|
|
3
9
|
import fs from 'fs';
|
|
@@ -13,52 +19,109 @@ dotenv.config();
|
|
|
13
19
|
|
|
14
20
|
const logger = loggerFactory(import.meta);
|
|
15
21
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Main class for handling DNS and IP related operations.
|
|
24
|
+
* All utility methods are implemented as static to serve as a namespace container.
|
|
25
|
+
* @class Dns
|
|
26
|
+
* @augments Dns
|
|
27
|
+
* @memberof DnsManager
|
|
28
|
+
*/
|
|
29
|
+
class Dns {
|
|
30
|
+
/**
|
|
31
|
+
* Retrieves the current public IP address (IPv4 or IPv6).
|
|
32
|
+
* @async
|
|
33
|
+
* @static
|
|
34
|
+
* @memberof DnsManager
|
|
35
|
+
* @returns {Promise<string>} The public IP address.
|
|
36
|
+
*/
|
|
37
|
+
static async getPublicIp() {
|
|
38
|
+
return await publicIp();
|
|
39
|
+
}
|
|
23
40
|
|
|
24
|
-
|
|
25
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Retrieves the current public IPv4 address.
|
|
43
|
+
* @async
|
|
44
|
+
* @static
|
|
45
|
+
* @memberof DnsManager
|
|
46
|
+
* @returns {Promise<string>} The public IPv4 address.
|
|
47
|
+
*/
|
|
48
|
+
static async getPublicIpv4() {
|
|
49
|
+
return await publicIpv4();
|
|
50
|
+
}
|
|
26
51
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Retrieves the current public IPv6 address.
|
|
54
|
+
* @async
|
|
55
|
+
* @static
|
|
56
|
+
* @memberof DnsManager
|
|
57
|
+
* @returns {Promise<string>} The public IPv6 address.
|
|
58
|
+
*/
|
|
59
|
+
static async getPublicIpv6() {
|
|
60
|
+
return await publicIpv6();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Checks for active internet connection by performing a DNS lookup on a specified domain.
|
|
65
|
+
* @static
|
|
66
|
+
* @memberof DnsManager
|
|
67
|
+
* @param {string} [domain='google.com'] The domain to check the connection against.
|
|
68
|
+
* @returns {Promise<boolean>} True if connected, false otherwise.
|
|
69
|
+
*/
|
|
70
|
+
static isInternetConnection(domain = 'google.com') {
|
|
71
|
+
return new Promise((resolve) => dns.lookup(domain, {}, (err) => resolve(err ? false : true)));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Gets the local device's IPv4 address by determining the active network interface.
|
|
76
|
+
* This relies on shell execution (`ip route`) and is primarily intended for Linux environments.
|
|
77
|
+
* @static
|
|
78
|
+
* @memberof DnsManager
|
|
79
|
+
* @returns {string} The local IPv4 address.
|
|
80
|
+
*/
|
|
81
|
+
static getLocalIPv4Address() {
|
|
82
|
+
// Determine the default network interface name using shell command
|
|
83
|
+
const interfaceName = shellExec(`ip route | grep default | cut -d ' ' -f 5`, {
|
|
32
84
|
stdout: true,
|
|
33
85
|
silent: true,
|
|
34
86
|
disableLog: true,
|
|
35
|
-
}).trim()
|
|
36
|
-
].find((i) => i.family === 'IPv4').address;
|
|
87
|
+
}).trim();
|
|
37
88
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
89
|
+
// Find the IPv4 address associated with the determined interface
|
|
90
|
+
const networkInfo = os.networkInterfaces()[interfaceName];
|
|
91
|
+
|
|
92
|
+
if (!networkInfo) {
|
|
93
|
+
logger.error(`Could not find network interface: ${interfaceName}`);
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const ipv4 = networkInfo.find((i) => i.family === 'IPv4');
|
|
98
|
+
|
|
99
|
+
if (!ipv4) {
|
|
100
|
+
logger.error(`Could not find IPv4 address for interface: ${interfaceName}`);
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return ipv4.address;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Performs the dynamic DNS update logic.
|
|
109
|
+
* It checks if the public IP has changed and, if so, updates the configured DNS records.
|
|
110
|
+
* @async
|
|
111
|
+
* @static
|
|
112
|
+
* @memberof DnsManager
|
|
113
|
+
* @param {string} deployList Comma-separated string of deployment IDs to process.
|
|
114
|
+
* @returns {Promise<void>}
|
|
115
|
+
*/
|
|
116
|
+
static async callback(deployList) {
|
|
117
|
+
const isOnline = await Dns.isInternetConnection();
|
|
55
118
|
|
|
56
119
|
if (!isOnline) return;
|
|
57
120
|
|
|
58
121
|
let testIp;
|
|
59
122
|
|
|
60
123
|
try {
|
|
61
|
-
testIp = await
|
|
124
|
+
testIp = await Dns.getPublicIpv4();
|
|
62
125
|
} catch (error) {
|
|
63
126
|
logger.error(error, { testIp, stack: error.stack });
|
|
64
127
|
}
|
|
@@ -66,52 +129,97 @@ class Dns {
|
|
|
66
129
|
const currentIp = UnderpostRootEnv.API.get('ip');
|
|
67
130
|
|
|
68
131
|
if (validator.isIP(testIp) && currentIp !== testIp) {
|
|
69
|
-
logger.info(`
|
|
132
|
+
logger.info(`New IP detected`, testIp);
|
|
70
133
|
UnderpostRootEnv.API.set('monitor-input', 'pause');
|
|
71
134
|
|
|
72
135
|
for (const _deployId of deployList.split(',')) {
|
|
73
136
|
const deployId = _deployId.trim();
|
|
74
137
|
const privateCronConfPath = `./engine-private/conf/${deployId}/conf.cron.json`;
|
|
75
138
|
const confCronPath = fs.existsSync(privateCronConfPath) ? privateCronConfPath : './conf/conf.cron.json';
|
|
139
|
+
|
|
140
|
+
if (!fs.existsSync(confCronPath)) {
|
|
141
|
+
logger.warn(`Cron config file not found for deployId: ${deployId} at ${confCronPath}`);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
|
|
76
145
|
const confCronData = JSON.parse(fs.readFileSync(confCronPath, 'utf8'));
|
|
146
|
+
|
|
147
|
+
if (!confCronData.records) {
|
|
148
|
+
logger.warn(`'records' field missing in cron config for deployId: ${deployId}`);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Iterate through DNS record types (A, AAAA, etc.)
|
|
77
153
|
for (const recordType of Object.keys(confCronData.records)) {
|
|
78
154
|
switch (recordType) {
|
|
79
155
|
case 'A':
|
|
156
|
+
// Process A records for IPv4 update
|
|
80
157
|
for (const dnsProvider of confCronData.records[recordType]) {
|
|
81
158
|
if (typeof Dns.services.updateIp[dnsProvider.dns] === 'function')
|
|
82
159
|
await Dns.services.updateIp[dnsProvider.dns]({ ...dnsProvider, ip: testIp });
|
|
83
160
|
}
|
|
84
161
|
break;
|
|
85
162
|
|
|
163
|
+
// Add other record types (e.g., AAAA) here if needed
|
|
86
164
|
default:
|
|
87
165
|
break;
|
|
88
166
|
}
|
|
89
167
|
}
|
|
168
|
+
|
|
169
|
+
// Verify the IP update externally
|
|
90
170
|
try {
|
|
91
171
|
const ipUrlTest = `https://${process.env.DEFAULT_DEPLOY_HOST}`;
|
|
92
172
|
const response = await axios.get(ipUrlTest);
|
|
93
173
|
const verifyIp = response.request.socket.remoteAddress;
|
|
94
174
|
logger.info(ipUrlTest + ' verify ip', verifyIp);
|
|
95
175
|
if (verifyIp === testIp) {
|
|
96
|
-
logger.info('
|
|
176
|
+
logger.info('IP updated successfully and verified', testIp);
|
|
97
177
|
UnderpostRootEnv.API.set('ip', testIp);
|
|
98
178
|
UnderpostRootEnv.API.delete('monitor-input');
|
|
99
|
-
} else
|
|
179
|
+
} else {
|
|
180
|
+
logger.error('IP not updated or verification failed', { expected: testIp, received: verifyIp });
|
|
181
|
+
}
|
|
100
182
|
} catch (error) {
|
|
101
|
-
logger.error(
|
|
102
|
-
|
|
183
|
+
logger.error('Error during IP update verification step', {
|
|
184
|
+
error: error.message,
|
|
185
|
+
stack: error.stack,
|
|
186
|
+
testIp,
|
|
187
|
+
});
|
|
103
188
|
}
|
|
104
189
|
}
|
|
105
190
|
}
|
|
106
|
-
}
|
|
191
|
+
}
|
|
107
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Internal collection of external DNS service update functions.
|
|
195
|
+
* @static
|
|
196
|
+
* @memberof DnsManager
|
|
197
|
+
* @property {object} updateIp - Functions keyed by DNS provider name to update A/AAAA records.
|
|
198
|
+
*/
|
|
108
199
|
static services = {
|
|
109
200
|
updateIp: {
|
|
201
|
+
/**
|
|
202
|
+
* Updates the IP address for a dondominio.com DNS record.
|
|
203
|
+
* @memberof DnsManager
|
|
204
|
+
* @param {object} options
|
|
205
|
+
* @param {string} options.user - The dondominio DDNS username.
|
|
206
|
+
* @param {string} options.api_key - The dondominio DDNS password/API key.
|
|
207
|
+
* @param {string} options.host - The hostname to update.
|
|
208
|
+
* @param {string} options.dns - The name of the DNS provider ('dondominio').
|
|
209
|
+
* @param {string} options.ip - The new IPv4 address to set.
|
|
210
|
+
* @returns {Promise<boolean>} True on success, false on failure.
|
|
211
|
+
*/
|
|
110
212
|
dondominio: (options) => {
|
|
111
213
|
const { user, api_key, host, dns, ip } = options;
|
|
112
214
|
const url = `https://dondns.dondominio.com/json/?user=${user}&password=${api_key}&host=${host}&ip=${ip}`;
|
|
113
215
|
logger.info(`${dns} update ip url`, url);
|
|
114
|
-
|
|
216
|
+
|
|
217
|
+
// Prevent live IP update in non-production environments
|
|
218
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
219
|
+
logger.warn('Skipping dondominio update in non-production environment.');
|
|
220
|
+
return Promise.resolve(false);
|
|
221
|
+
}
|
|
222
|
+
|
|
115
223
|
return new Promise((resolve) => {
|
|
116
224
|
axios
|
|
117
225
|
.get(url)
|
|
@@ -120,15 +228,49 @@ class Dns {
|
|
|
120
228
|
return resolve(true);
|
|
121
229
|
})
|
|
122
230
|
.catch((error) => {
|
|
123
|
-
logger.error(error, `${dns} update ip error`);
|
|
231
|
+
logger.error(error, `${dns} update ip error: ${error.message}`);
|
|
124
232
|
return resolve(false);
|
|
125
233
|
});
|
|
126
234
|
});
|
|
127
235
|
},
|
|
236
|
+
// Add other DNS provider update functions here
|
|
128
237
|
},
|
|
129
238
|
};
|
|
130
239
|
}
|
|
131
240
|
|
|
241
|
+
/**
|
|
242
|
+
* @namespace Dns
|
|
243
|
+
* @description Exported IP object for backward compatibility, mapping to Dns static methods.
|
|
244
|
+
*/
|
|
245
|
+
const ip = {
|
|
246
|
+
public: {
|
|
247
|
+
/** @type {function(): Promise<string>} */
|
|
248
|
+
get: Dns.getPublicIp,
|
|
249
|
+
/** @type {function(): Promise<string>} */
|
|
250
|
+
ipv4: Dns.getPublicIpv4,
|
|
251
|
+
/** @type {function(): Promise<string>} */
|
|
252
|
+
ipv6: Dns.getPublicIpv6,
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* @function isInternetConnection
|
|
258
|
+
* @memberof DnsManager
|
|
259
|
+
* @description Exported function for backward compatibility.
|
|
260
|
+
* @param {string} [domain='google.com']
|
|
261
|
+
* @returns {Promise<boolean>}
|
|
262
|
+
*/
|
|
263
|
+
const isInternetConnection = Dns.isInternetConnection;
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* @function getLocalIPv4Address
|
|
267
|
+
* @memberof DnsManager
|
|
268
|
+
* @description Exported function for backward compatibility.
|
|
269
|
+
* @returns {string}
|
|
270
|
+
*/
|
|
271
|
+
const getLocalIPv4Address = Dns.getLocalIPv4Address;
|
|
272
|
+
|
|
273
|
+
// Export the class as default and all original identifiers for backward compatibility.
|
|
132
274
|
export default Dns;
|
|
133
275
|
|
|
134
276
|
export { Dns, ip, isInternetConnection, getLocalIPv4Address };
|
package/src/server/downloader.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provides a utility class for downloading files from a URL and saving them to the local filesystem.
|
|
3
|
+
* @module src/server/downloader.js
|
|
4
|
+
* @namespace Downloader
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
import axios from 'axios';
|
|
2
8
|
import fs from 'fs';
|
|
3
9
|
import { loggerFactory } from './logger.js';
|
|
@@ -6,29 +12,64 @@ dotenv.config();
|
|
|
6
12
|
|
|
7
13
|
const logger = loggerFactory(import.meta);
|
|
8
14
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Main class for handling file downloading operations.
|
|
17
|
+
* All utility methods are implemented as static to serve as a namespace container.
|
|
18
|
+
* @class Downloader
|
|
19
|
+
* @augments Downloader
|
|
20
|
+
* @memberof Downloader
|
|
21
|
+
*/
|
|
22
|
+
class Downloader {
|
|
23
|
+
/**
|
|
24
|
+
* Downloads a file from a given URL and pipes the stream to a local file path.
|
|
25
|
+
* @static
|
|
26
|
+
* @memberof Downloader
|
|
27
|
+
* @param {string} url The URL of the file to download.
|
|
28
|
+
* @param {string} fullPath The full local path where the file should be saved.
|
|
29
|
+
* @param {object} [options] Axios request configuration options.
|
|
30
|
+
* @param {string} [options.method='get'] HTTP method.
|
|
31
|
+
* @param {string} [options.responseType='stream'] Expected response type.
|
|
32
|
+
* @returns {Promise<string>} Resolves with the full path of the saved file on success.
|
|
33
|
+
* @memberof Downloader
|
|
34
|
+
*/
|
|
35
|
+
static downloadFile(url, fullPath, options = { method: 'get', responseType: 'stream' }) {
|
|
36
|
+
return new Promise((resolve, reject) =>
|
|
37
|
+
axios({
|
|
38
|
+
url,
|
|
39
|
+
...options,
|
|
27
40
|
})
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
41
|
+
.then((response) => {
|
|
42
|
+
// Create a write stream to save the file to the specified path
|
|
43
|
+
const writer = fs.createWriteStream(fullPath);
|
|
44
|
+
response.data.pipe(writer);
|
|
45
|
+
writer.on('finish', () => {
|
|
46
|
+
logger.info('Download complete. File saved at', fullPath);
|
|
47
|
+
return resolve(fullPath);
|
|
48
|
+
});
|
|
49
|
+
writer.on('error', (error) => {
|
|
50
|
+
logger.error(error, 'Error downloading the file');
|
|
51
|
+
// Cleanup incomplete file if possible
|
|
52
|
+
if (fs.existsSync(fullPath)) fs.unlinkSync(fullPath);
|
|
53
|
+
return reject(error);
|
|
54
|
+
});
|
|
55
|
+
})
|
|
56
|
+
.catch((error) => {
|
|
57
|
+
logger.error(error, 'Error in the request');
|
|
58
|
+
return reject(error);
|
|
59
|
+
}),
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export default Downloader;
|
|
33
65
|
|
|
34
|
-
|
|
66
|
+
/**
|
|
67
|
+
* @function downloadFile
|
|
68
|
+
* @description Backward compatibility export for `Downloader.downloadFile`.
|
|
69
|
+
* @param {string} url The URL of the file to download.
|
|
70
|
+
* @param {string} fullPath The full local path where the file should be saved.
|
|
71
|
+
* @param {object} [options] Axios request configuration options.
|
|
72
|
+
* @returns {Promise<string>} Resolves with the full path of the saved file on success.
|
|
73
|
+
* @memberof Downloader
|
|
74
|
+
*/
|
|
75
|
+
export const downloadFile = Downloader.downloadFile;
|