underpost 2.8.871 → 2.8.873
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.development +2 -1
- package/.env.production +2 -1
- package/.env.test +2 -1
- package/.github/workflows/ghpkg.ci.yml +1 -1
- package/.github/workflows/npmpkg.ci.yml +1 -1
- package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -1
- package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
- package/.github/workflows/release.cd.yml +2 -2
- package/README.md +66 -36
- package/bin/build.js +4 -0
- package/bin/deploy.js +4 -0
- package/cli.md +88 -87
- package/conf.js +2 -1
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +138 -0
- package/manifests/deployment/dd-test-development/proxy.yaml +26 -0
- package/package.json +6 -3
- package/src/api/core/core.router.js +2 -1
- package/src/api/default/default.controller.js +6 -1
- package/src/api/default/default.router.js +6 -2
- package/src/api/default/default.service.js +10 -1
- package/src/api/file/file.router.js +2 -1
- package/src/api/test/test.router.js +1 -1
- package/src/api/user/postman_collection.json +216 -0
- package/src/api/user/user.controller.js +25 -60
- package/src/api/user/user.model.js +29 -7
- package/src/api/user/user.router.js +6 -3
- package/src/api/user/user.service.js +80 -32
- package/src/cli/baremetal.js +33 -3
- package/src/cli/cloud-init.js +11 -0
- package/src/cli/deploy.js +5 -2
- package/src/cli/index.js +1 -0
- package/src/cli/lxd.js +7 -0
- package/src/cli/repository.js +1 -0
- package/src/cli/run.js +18 -5
- package/src/cli/ssh.js +20 -6
- package/src/client/components/core/Account.js +2 -1
- package/src/client/components/core/AgGrid.js +30 -8
- package/src/client/components/core/Auth.js +98 -55
- package/src/client/components/core/CalendarCore.js +3 -4
- package/src/client/components/core/CommonJs.js +1 -2
- package/src/client/components/core/Content.js +2 -1
- package/src/client/components/core/Css.js +2 -1
- package/src/client/components/core/CssCore.js +2 -1
- package/src/client/components/core/Docs.js +4 -4
- package/src/client/components/core/FileExplorer.js +3 -3
- package/src/client/components/core/JoyStick.js +2 -2
- package/src/client/components/core/LoadingAnimation.js +2 -2
- package/src/client/components/core/LogIn.js +16 -23
- package/src/client/components/core/LogOut.js +5 -1
- package/src/client/components/core/Logger.js +4 -1
- package/src/client/components/core/Modal.js +17 -27
- package/src/client/components/core/ObjectLayerEngineModal.js +2 -1
- package/src/client/components/core/Pagination.js +207 -0
- package/src/client/components/core/Panel.js +3 -11
- package/src/client/components/core/PanelForm.js +6 -15
- package/src/client/components/core/Recover.js +2 -2
- package/src/client/components/core/Router.js +205 -33
- package/src/client/components/core/SignUp.js +1 -2
- package/src/client/components/core/Stream.js +1 -1
- package/src/client/components/core/VanillaJs.js +0 -83
- package/src/client/components/core/Worker.js +2 -2
- package/src/client/components/default/LogInDefault.js +0 -6
- package/src/client/components/default/LogOutDefault.js +0 -16
- package/src/client/components/default/MenuDefault.js +4 -3
- package/src/client/components/default/RoutesDefault.js +3 -2
- package/src/client/services/core/core.service.js +6 -2
- package/src/client/services/default/default.management.js +115 -18
- package/src/client/services/default/default.service.js +9 -4
- package/src/client/services/user/user.management.js +6 -0
- package/src/client/services/user/user.service.js +11 -4
- package/src/client/ssr/head/DefaultScripts.js +1 -0
- package/src/index.js +24 -2
- package/src/runtime/lampp/Lampp.js +89 -2
- package/src/runtime/xampp/Xampp.js +48 -1
- package/src/server/auth.js +518 -155
- package/src/server/conf.js +19 -1
- package/src/server/runtime.js +62 -221
- package/src/server/ssl.js +1 -2
- package/src/server/ssr.js +85 -0
- package/src/server/valkey.js +2 -1
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import { loggerFactory } from '../../server/logger.js';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
hashPassword,
|
|
4
|
+
verifyPassword,
|
|
5
|
+
verifyJWT,
|
|
6
|
+
createSessionAndUserToken,
|
|
7
|
+
createUserAndSession,
|
|
8
|
+
refreshSessionAndToken,
|
|
9
|
+
hashToken,
|
|
10
|
+
jwtSign,
|
|
11
|
+
getBearerToken,
|
|
12
|
+
validatePasswordMiddleware,
|
|
13
|
+
} from '../../server/auth.js';
|
|
3
14
|
import { MailerProvider } from '../../mailer/MailerProvider.js';
|
|
4
15
|
import { CoreWsMailerManagement } from '../../ws/core/management/core.ws.mailer.js';
|
|
5
16
|
import { CoreWsEmit } from '../../ws/core/core.ws.emit.js';
|
|
@@ -27,8 +38,8 @@ const UserService = {
|
|
|
27
38
|
|
|
28
39
|
if (!user) throw new Error('Email address does not exist');
|
|
29
40
|
|
|
30
|
-
const token =
|
|
31
|
-
const payloadToken =
|
|
41
|
+
const token = jwtSign({ email: req.body.email }, options, 15);
|
|
42
|
+
const payloadToken = jwtSign({ email: req.body.email }, options, 15);
|
|
32
43
|
const id = `${options.host}${options.path}`;
|
|
33
44
|
const translate = MailerProvider.instance[id].translateTemplates.recoverEmail;
|
|
34
45
|
const recoverUrl = `${process.env.NODE_ENV === 'development' ? 'http://' : 'https://'}${req.body.hostname}${
|
|
@@ -65,7 +76,7 @@ const UserService = {
|
|
|
65
76
|
if (req.path.startsWith('/mailer') && req.params.id === 'verify-email') {
|
|
66
77
|
if (!validator.isEmail(req.body.email)) throw { message: 'invalid email' };
|
|
67
78
|
|
|
68
|
-
const token =
|
|
79
|
+
const token = jwtSign({ email: req.body.email }, options, 15);
|
|
69
80
|
const id = `${options.host}${options.path}`;
|
|
70
81
|
const user = await User.findById(req.auth.user._id);
|
|
71
82
|
|
|
@@ -145,8 +156,13 @@ const UserService = {
|
|
|
145
156
|
runValidators: true,
|
|
146
157
|
},
|
|
147
158
|
);
|
|
159
|
+
|
|
160
|
+
const { jwtid } = await createSessionAndUserToken(user, User, req, res, options);
|
|
148
161
|
return {
|
|
149
|
-
token:
|
|
162
|
+
token: jwtSign(
|
|
163
|
+
UserDto.auth.payload(user, jwtid, req.ip, req.headers['user-agent'], options.host, options.path),
|
|
164
|
+
options,
|
|
165
|
+
),
|
|
150
166
|
user,
|
|
151
167
|
};
|
|
152
168
|
} else throw new Error(accountLocketMessage());
|
|
@@ -193,33 +209,24 @@ const UserService = {
|
|
|
193
209
|
} catch (error) {
|
|
194
210
|
logger.error(error, { params: req.params, body: req.body });
|
|
195
211
|
}
|
|
196
|
-
throw new Error(`
|
|
212
|
+
throw new Error(`Invalid credentials. Remaining attempts: ${5 - user.failedLoginAttempts}`);
|
|
197
213
|
}
|
|
198
|
-
} else throw new Error('
|
|
214
|
+
} else throw new Error('Invalid credentials');
|
|
199
215
|
|
|
200
216
|
case 'guest': {
|
|
201
217
|
const user = await ValkeyAPI.valkeyObjectFactory(options, 'user');
|
|
202
218
|
await ValkeyAPI.setValkeyObject(options, user.email, user);
|
|
203
219
|
return {
|
|
204
|
-
token:
|
|
220
|
+
token: jwtSign(
|
|
221
|
+
UserDto.auth.payload(user, null, req.ip, req.headers['user-agent'], options.host, options.path),
|
|
222
|
+
options,
|
|
223
|
+
),
|
|
205
224
|
user: selectDtoFactory(user, UserDto.select.get()),
|
|
206
225
|
};
|
|
207
226
|
}
|
|
208
227
|
|
|
209
228
|
default: {
|
|
210
|
-
|
|
211
|
-
if (validatePassword.status === 'error') throw new Error(validatePassword.message);
|
|
212
|
-
req.body.password = await hashPassword(req.body.password);
|
|
213
|
-
req.body.role = req.body.role === 'guest' ? 'guest' : 'user';
|
|
214
|
-
req.body.profileImageId = await options.getDefaultProfileImageId(File);
|
|
215
|
-
const { _id } = await new User(req.body).save();
|
|
216
|
-
if (_id) {
|
|
217
|
-
const user = await User.findOne({ _id }).select(UserDto.select.get());
|
|
218
|
-
return {
|
|
219
|
-
token: hashJWT({ user: UserDto.auth.payload(user) }),
|
|
220
|
-
user,
|
|
221
|
-
};
|
|
222
|
-
} else throw new Error('failed to create user');
|
|
229
|
+
return await createUserAndSession(req, res, User, File, options);
|
|
223
230
|
}
|
|
224
231
|
}
|
|
225
232
|
},
|
|
@@ -239,7 +246,7 @@ const UserService = {
|
|
|
239
246
|
if (req.path.startsWith('/recover')) {
|
|
240
247
|
let payload;
|
|
241
248
|
try {
|
|
242
|
-
payload = verifyJWT(req.params.id);
|
|
249
|
+
payload = verifyJWT(req.params.id, options);
|
|
243
250
|
} catch (error) {
|
|
244
251
|
logger.error(error, { 'req.params.id': req.params.id });
|
|
245
252
|
options.png.header(res);
|
|
@@ -254,7 +261,7 @@ const UserService = {
|
|
|
254
261
|
_id,
|
|
255
262
|
{ recoverTimeOut: new Date(+new Date() + 1000 * 60 * 15) },
|
|
256
263
|
{ runValidators: true },
|
|
257
|
-
);
|
|
264
|
+
);
|
|
258
265
|
options.png.header(res);
|
|
259
266
|
return options.png.buffer['recover'];
|
|
260
267
|
} else {
|
|
@@ -266,7 +273,7 @@ const UserService = {
|
|
|
266
273
|
if (req.path.startsWith('/mailer')) {
|
|
267
274
|
let payload;
|
|
268
275
|
try {
|
|
269
|
-
payload = verifyJWT(req.params.id);
|
|
276
|
+
payload = verifyJWT(req.params.id, options);
|
|
270
277
|
} catch (error) {
|
|
271
278
|
logger.error(error, { 'req.params.id': req.params.id });
|
|
272
279
|
options.png.header(res);
|
|
@@ -294,8 +301,24 @@ const UserService = {
|
|
|
294
301
|
}
|
|
295
302
|
|
|
296
303
|
switch (req.params.id) {
|
|
297
|
-
case 'all':
|
|
298
|
-
|
|
304
|
+
case 'all': {
|
|
305
|
+
if (req.auth.user.role === 'admin') {
|
|
306
|
+
const page = parseInt(req.query.page) || 1;
|
|
307
|
+
const limit = parseInt(req.query.limit) || 10;
|
|
308
|
+
const skip = (page - 1) * limit;
|
|
309
|
+
|
|
310
|
+
const data = await User.find().select(UserDto.select.get()).skip(skip).limit(limit);
|
|
311
|
+
const total = await User.countDocuments();
|
|
312
|
+
|
|
313
|
+
return {
|
|
314
|
+
data,
|
|
315
|
+
page,
|
|
316
|
+
limit,
|
|
317
|
+
total,
|
|
318
|
+
totalPages: Math.ceil(total / limit),
|
|
319
|
+
};
|
|
320
|
+
} else throw new Error(`Invalid token user id`);
|
|
321
|
+
}
|
|
299
322
|
|
|
300
323
|
case 'auth': {
|
|
301
324
|
let user;
|
|
@@ -307,6 +330,8 @@ const UserService = {
|
|
|
307
330
|
_id: req.auth.user._id,
|
|
308
331
|
});
|
|
309
332
|
|
|
333
|
+
if (!user) throw new Error('user not found');
|
|
334
|
+
|
|
310
335
|
const file = await File.findOne({ _id: user.profileImageId });
|
|
311
336
|
|
|
312
337
|
if (!file && !(await ValkeyAPI.getValkeyObject(options, req.auth.user.email))) {
|
|
@@ -318,11 +343,20 @@ const UserService = {
|
|
|
318
343
|
},
|
|
319
344
|
);
|
|
320
345
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
346
|
+
|
|
347
|
+
const guestUser = await ValkeyAPI.getValkeyObject(options, req.auth.user.email);
|
|
348
|
+
if (guestUser)
|
|
349
|
+
return {
|
|
350
|
+
user: selectDtoFactory(guestUser, UserDto.select.get()),
|
|
351
|
+
token: getBearerToken(req),
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
return {
|
|
355
|
+
token: await refreshSessionAndToken(req, res, User, options),
|
|
356
|
+
user: await User.findOne({
|
|
357
|
+
_id: req.auth.user._id,
|
|
358
|
+
}).select(UserDto.select.get()),
|
|
359
|
+
};
|
|
326
360
|
}
|
|
327
361
|
|
|
328
362
|
default: {
|
|
@@ -346,6 +380,20 @@ const UserService = {
|
|
|
346
380
|
delete: async (req, res, options) => {
|
|
347
381
|
/** @type {import('./user.model.js').UserModel} */
|
|
348
382
|
const User = DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models.User;
|
|
383
|
+
|
|
384
|
+
if (req.params.id === 'logout') {
|
|
385
|
+
const refreshToken = req.cookies?.refreshToken;
|
|
386
|
+
if (refreshToken) {
|
|
387
|
+
const hashedToken = hashToken(refreshToken);
|
|
388
|
+
await User.updateOne(
|
|
389
|
+
{ 'activeSessions.tokenHash': hashedToken },
|
|
390
|
+
{ $pull: { activeSessions: { tokenHash: hashedToken } } },
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
res.clearCookie('refreshToken');
|
|
394
|
+
return { message: 'Logged out successfully' };
|
|
395
|
+
}
|
|
396
|
+
|
|
349
397
|
switch (req.params.id) {
|
|
350
398
|
default: {
|
|
351
399
|
const user = await User.findOne({
|
|
@@ -401,7 +449,7 @@ const UserService = {
|
|
|
401
449
|
}
|
|
402
450
|
|
|
403
451
|
if (req.path.startsWith('/recover')) {
|
|
404
|
-
const payload = verifyJWT(req.params.id);
|
|
452
|
+
const payload = verifyJWT(req.params.id, options);
|
|
405
453
|
const user = await User.findOne({
|
|
406
454
|
email: payload.email,
|
|
407
455
|
});
|
package/src/cli/baremetal.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Baremetal module for managing the generation and deployment of cloud-init configuration files
|
|
3
|
+
* and associated scripts for baremetal provisioning.
|
|
4
|
+
* @module src/cli/baremetal.js
|
|
5
|
+
* @namespace UnderpostBaremetal
|
|
6
|
+
*/
|
|
7
|
+
|
|
1
8
|
import { getNpmRootPath, getUnderpostRootPath } from '../server/conf.js';
|
|
2
9
|
import { openTerminal, pbcopy, shellExec } from '../server/process.js';
|
|
3
10
|
import dotenv from 'dotenv';
|
|
@@ -40,6 +47,7 @@ class UnderpostBaremetal {
|
|
|
40
47
|
* @param {boolean} [options.nfsUnmount=false] - Flag to unmount the NFS root filesystem.
|
|
41
48
|
* @param {boolean} [options.nfsSh=false] - Flag to chroot into the NFS environment for shell access.
|
|
42
49
|
* @param {string} [options.logs=''] - Specifies which logs to display ('dhcp', 'cloud', 'machine', 'cloud-config').
|
|
50
|
+
* @memberof UnderpostBaremetal
|
|
43
51
|
* @returns {void}
|
|
44
52
|
*/
|
|
45
53
|
async callback(
|
|
@@ -576,6 +584,7 @@ menuentry '${menuentryStr}' {
|
|
|
576
584
|
* @param {object} params.maas - MAAS configuration details.
|
|
577
585
|
* @param {string} params.networkInterfaceName - The name of the network interface.
|
|
578
586
|
* @returns {Promise<void>} A promise that resolves when commissioning is initiated or after a delay.
|
|
587
|
+
* @memberof UnderpostBaremetal
|
|
579
588
|
*/
|
|
580
589
|
async commissionMonitor({ macAddress, nfsHostPath, underpostRoot, hostname, maas, networkInterfaceName }) {
|
|
581
590
|
{
|
|
@@ -725,6 +734,7 @@ menuentry '${menuentryStr}' {
|
|
|
725
734
|
* This is necessary for cross-architecture execution within a chroot environment.
|
|
726
735
|
* @param {object} params - The parameters for the function.
|
|
727
736
|
* @param {string} params.nfsHostPath - The path to the NFS root filesystem on the host.
|
|
737
|
+
* @memberof UnderpostBaremetal
|
|
728
738
|
* @returns {void}
|
|
729
739
|
*/
|
|
730
740
|
mountBinfmtMisc({ nfsHostPath }) {
|
|
@@ -747,6 +757,7 @@ menuentry '${menuentryStr}' {
|
|
|
747
757
|
* @description Deletes all specified machines from MAAS.
|
|
748
758
|
* @param {object} params - The parameters for the function.
|
|
749
759
|
* @param {Array<object>} params.machines - An array of machine objects, each with a `system_id`.
|
|
760
|
+
* @memberof UnderpostBaremetal
|
|
750
761
|
* @returns {Array<object>} An empty array after machines are removed.
|
|
751
762
|
*/
|
|
752
763
|
removeMachines({ machines }) {
|
|
@@ -761,6 +772,7 @@ menuentry '${menuentryStr}' {
|
|
|
761
772
|
* @description Clears all observed discoveries in MAAS and optionally forces a new scan.
|
|
762
773
|
* @param {object} params - The parameters for the function.
|
|
763
774
|
* @param {boolean} params.force - If true, forces a new discovery scan after clearing.
|
|
775
|
+
* @memberof UnderpostBaremetal
|
|
764
776
|
* @returns {void}
|
|
765
777
|
*/
|
|
766
778
|
clearDiscoveries({ force }) {
|
|
@@ -776,6 +788,7 @@ menuentry '${menuentryStr}' {
|
|
|
776
788
|
* This is used to wait for the target machine to report its MAC address.
|
|
777
789
|
* @param {object} params - The parameters for the function.
|
|
778
790
|
* @param {string} params.nfsHostPath - The NFS host path where the MAC file is expected.
|
|
791
|
+
* @memberof UnderpostBaremetal
|
|
779
792
|
* @returns {Promise<void>} A promise that resolves when the MAC file is found or after a delay.
|
|
780
793
|
*/
|
|
781
794
|
async macMonitor({ nfsHostPath }) {
|
|
@@ -794,6 +807,7 @@ menuentry '${menuentryStr}' {
|
|
|
794
807
|
* for cross-architecture execution within a chroot environment.
|
|
795
808
|
* @param {object} params - The parameters for the function.
|
|
796
809
|
* @param {string} params.nfsHostPath - The path to the NFS root filesystem on the host.
|
|
810
|
+
* @memberof UnderpostBaremetal
|
|
797
811
|
* @param {'arm64'|'amd64'} params.debootstrapArch - The target architecture of the debootstrap environment.
|
|
798
812
|
* @returns {void}
|
|
799
813
|
*/
|
|
@@ -825,6 +839,7 @@ menuentry '${menuentryStr}' {
|
|
|
825
839
|
* @param {string} params.nfsHostPath - The path to the NFS root filesystem on the host.
|
|
826
840
|
* @param {'arm64'|'amd64'} params.debootstrapArch - The target architecture of the debootstrap environment.
|
|
827
841
|
* @param {object} params.callbackMetaData - Metadata about the callback, including runner host architecture.
|
|
842
|
+
* @memberof UnderpostBaremetal
|
|
828
843
|
* @param {string[]} params.steps - An array of shell commands to execute.
|
|
829
844
|
* @returns {void}
|
|
830
845
|
*/
|
|
@@ -860,6 +875,7 @@ EOF`);
|
|
|
860
875
|
* This helps in visualizing and debugging the execution flow of provisioning steps.
|
|
861
876
|
* @param {string[]} [steps=[]] - An array of shell commands.
|
|
862
877
|
* @param {boolean} [yaml=true] - If true, formats the output as YAML list items.
|
|
878
|
+
* @memberof UnderpostBaremetal
|
|
863
879
|
* @returns {string} The formatted string of commands.
|
|
864
880
|
*/
|
|
865
881
|
stepsRender(steps = [], yaml = true) {
|
|
@@ -892,6 +908,7 @@ EOF`);
|
|
|
892
908
|
* @param {string} params.workflowId - The identifier for the workflow configuration.
|
|
893
909
|
* @param {boolean} [params.mount] - If true, attempts to mount the NFS paths.
|
|
894
910
|
* @param {boolean} [params.unmount] - If true, attempts to unmount the NFS paths.
|
|
911
|
+
* @memberof UnderpostBaremetal
|
|
895
912
|
* @returns {{isMounted: boolean}} An object indicating whether any NFS path is currently mounted.
|
|
896
913
|
*/
|
|
897
914
|
nfsMountCallback({ hostname, workflowId, mount, unmount }) {
|
|
@@ -929,7 +946,8 @@ EOF`);
|
|
|
929
946
|
* @method getHostArch
|
|
930
947
|
* @description Determines the architecture of the host machine.
|
|
931
948
|
* This is crucial for cross-compilation and selecting the correct QEMU binaries.
|
|
932
|
-
* @
|
|
949
|
+
* @memberof UnderpostBaremetal
|
|
950
|
+
* @returns {{alias: 'amd64'|'arm64', name: 'x86_64'|'aarch64'}} The host architecture.
|
|
933
951
|
* @throws {Error} If the host architecture is unsupported.
|
|
934
952
|
*/
|
|
935
953
|
getHostArch() {
|
|
@@ -944,12 +962,16 @@ EOF`);
|
|
|
944
962
|
* @property {object} systemProvisioningFactory
|
|
945
963
|
* @description A factory object containing functions for system provisioning based on OS type.
|
|
946
964
|
* Each OS type (e.g., 'ubuntu') provides methods for base system setup, user creation,
|
|
947
|
-
* timezone configuration, and keyboard layout settings.
|
|
965
|
+
* timezone configuration, and keyboard layout settings. *
|
|
966
|
+
* @memberof UnderpostBaremetal
|
|
967
|
+
* @namespace UnderpostBaremetal.systemProvisioningFactory
|
|
948
968
|
*/
|
|
949
969
|
systemProvisioningFactory: {
|
|
950
970
|
/**
|
|
951
971
|
* @property {object} ubuntu
|
|
952
972
|
* @description Provisioning steps for Ubuntu-based systems.
|
|
973
|
+
* @memberof UnderpostBaremetal.systemProvisioningFactory
|
|
974
|
+
* @namespace UnderpostBaremetal.systemProvisioningFactory.ubuntu
|
|
953
975
|
*/
|
|
954
976
|
ubuntu: {
|
|
955
977
|
/**
|
|
@@ -959,6 +981,7 @@ EOF`);
|
|
|
959
981
|
* kernel modules, cloud-init, SSH server, and other core utilities.
|
|
960
982
|
* @param {object} params - The parameters for the function.
|
|
961
983
|
* @param {string} params.kernelLibVersion - The specific kernel library version to install.
|
|
984
|
+
* @memberof UnderpostBaremetal.systemProvisioningFactory.ubuntu
|
|
962
985
|
* @returns {string[]} An array of shell commands.
|
|
963
986
|
*/
|
|
964
987
|
base: ({ kernelLibVersion }) => [
|
|
@@ -991,6 +1014,7 @@ SOURCES`,
|
|
|
991
1014
|
* @method user
|
|
992
1015
|
* @description Generates shell commands for creating a root user and configuring SSH access.
|
|
993
1016
|
* This is a critical security step for initial access to the provisioned system.
|
|
1017
|
+
* @memberof UnderpostBaremetal.systemProvisioningFactory.ubuntu
|
|
994
1018
|
* @returns {string[]} An array of shell commands.
|
|
995
1019
|
*/
|
|
996
1020
|
user: () => [
|
|
@@ -1014,6 +1038,7 @@ SOURCES`,
|
|
|
1014
1038
|
* @param {string} params.timezone - The timezone string (e.g., 'America/New_York').
|
|
1015
1039
|
* @param {string} params.chronyConfPath - The path to the Chrony configuration file.
|
|
1016
1040
|
* @param {string} [alias='chrony'] - The alias for the chrony service.
|
|
1041
|
+
* @memberof UnderpostBaremetal.systemProvisioningFactory.ubuntu
|
|
1017
1042
|
* @returns {string[]} An array of shell commands.
|
|
1018
1043
|
*/
|
|
1019
1044
|
timezone: ({ timezone, chronyConfPath }, alias = 'chrony') => [
|
|
@@ -1081,6 +1106,7 @@ logdir /var/log/chrony
|
|
|
1081
1106
|
* @method keyboard
|
|
1082
1107
|
* @description Generates shell commands for configuring the keyboard layout.
|
|
1083
1108
|
* This ensures correct input behavior on the provisioned system.
|
|
1109
|
+
* @memberof UnderpostBaremetal.systemProvisioningFactory.ubuntu
|
|
1084
1110
|
* @returns {string[]} An array of shell commands.
|
|
1085
1111
|
*/
|
|
1086
1112
|
keyboard: () => [
|
|
@@ -1099,6 +1125,7 @@ logdir /var/log/chrony
|
|
|
1099
1125
|
* This is crucial for allowing baremetal machines to boot via NFS.
|
|
1100
1126
|
* @param {object} params - The parameters for the function.
|
|
1101
1127
|
* @param {string} params.nfsHostPath - The path to be exported by the NFS server.
|
|
1128
|
+
* @memberof UnderpostBaremetal
|
|
1102
1129
|
* @param {string} [params.subnet='192.168.1.0/24'] - The subnet allowed to access the NFS export.
|
|
1103
1130
|
* @returns {void}
|
|
1104
1131
|
*/
|
|
@@ -1169,6 +1196,7 @@ udp-port = 32766
|
|
|
1169
1196
|
* @param {string} params.clientIp - The static IP address for the client device.
|
|
1170
1197
|
* @param {string} params.subnet - The subnet mask for the client device.
|
|
1171
1198
|
* @param {string} params.gateway - The gateway IP address for the client device.
|
|
1199
|
+
* @memberof UnderpostBaremetal
|
|
1172
1200
|
* @returns {string} The generated boot configuration content.
|
|
1173
1201
|
* @throws {Error} If an invalid workflow ID is provided.
|
|
1174
1202
|
*/
|
|
@@ -1229,12 +1257,14 @@ GATEWAY=${gateway}`;
|
|
|
1229
1257
|
* @property {object} workflowsConfig
|
|
1230
1258
|
* @description Configuration for different baremetal provisioning workflows.
|
|
1231
1259
|
* Each workflow defines specific parameters like system provisioning type,
|
|
1232
|
-
* kernel version, Chrony settings, debootstrap image details, and NFS mounts.
|
|
1260
|
+
* kernel version, Chrony settings, debootstrap image details, and NFS mounts. *
|
|
1261
|
+
* @memberof UnderpostBaremetal
|
|
1233
1262
|
*/
|
|
1234
1263
|
workflowsConfig: {
|
|
1235
1264
|
/**
|
|
1236
1265
|
* @property {object} rpi4mb
|
|
1237
1266
|
* @description Configuration for the Raspberry Pi 4 Model B workflow.
|
|
1267
|
+
* @memberof UnderpostBaremetal.workflowsConfig
|
|
1238
1268
|
*/
|
|
1239
1269
|
rpi4mb: {
|
|
1240
1270
|
menuentryStr: 'UNDERPOST.NET UEFI/GRUB/MAAS RPi4 commissioning (ARM64)',
|
package/src/cli/cloud-init.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloud-init module for managing the generation and deployment of cloud-init configuration files
|
|
3
|
+
* and associated scripts for baremetal provisioning.
|
|
4
|
+
* @module src/cli/cloud-init.js
|
|
5
|
+
* @namespace UnderpostCloudInit
|
|
6
|
+
*/
|
|
7
|
+
|
|
1
8
|
import dotenv from 'dotenv';
|
|
2
9
|
import { shellExec } from '../server/process.js';
|
|
3
10
|
import fs from 'fs-extra';
|
|
@@ -15,6 +22,7 @@ const logger = loggerFactory(import.meta);
|
|
|
15
22
|
* and associated scripts for baremetal provisioning. This class provides methods
|
|
16
23
|
* to build various shell scripts and a cloud-init configuration file tailored
|
|
17
24
|
* for MAAS (Metal as a Service) integration.
|
|
25
|
+
* @memberof UnderpostCloudInit
|
|
18
26
|
*/
|
|
19
27
|
class UnderpostCloudInit {
|
|
20
28
|
static API = {
|
|
@@ -30,6 +38,7 @@ class UnderpostCloudInit {
|
|
|
30
38
|
* @param {object} params.callbackMetaData - Metadata about the callback, used for dynamic configuration.
|
|
31
39
|
* @param {boolean} params.dev - Development mode flag.
|
|
32
40
|
* @returns {void}
|
|
41
|
+
* @memberof UnderpostCloudInit
|
|
33
42
|
*/
|
|
34
43
|
buildTools({ workflowId, nfsHostPath, hostname, callbackMetaData, dev }) {
|
|
35
44
|
// Destructure workflow configuration for easier access.
|
|
@@ -281,6 +290,7 @@ curl -X POST \\
|
|
|
281
290
|
* @param {object} [authCredentials={}] - Optional MAAS authentication credentials.
|
|
282
291
|
* @param {string} [path='/etc/cloud/cloud.cfg.d/90_maas.cfg'] - The target path for the cloud-init configuration file.
|
|
283
292
|
* @returns {string} The generated cloud-init configuration content.
|
|
293
|
+
* @memberof UnderpostCloudInit
|
|
284
294
|
*/
|
|
285
295
|
configFactory(
|
|
286
296
|
{
|
|
@@ -494,6 +504,7 @@ EOF_MAAS_CFG`;
|
|
|
494
504
|
* This method parses the output of `maas apikey` to extract the consumer key,
|
|
495
505
|
* consumer secret, token key, and token secret.
|
|
496
506
|
* @returns {object} An object containing the MAAS authentication credentials.
|
|
507
|
+
* @memberof UnderpostCloudInit
|
|
497
508
|
* @throws {Error} If the MAAS API key format is invalid.
|
|
498
509
|
*/
|
|
499
510
|
authCredentialsFactory() {
|
package/src/cli/deploy.js
CHANGED
|
@@ -62,7 +62,7 @@ class UnderpostDeploy {
|
|
|
62
62
|
)
|
|
63
63
|
.join('');
|
|
64
64
|
},
|
|
65
|
-
deploymentYamlPartsFactory({ deployId, env, suffix, resources, replicas }) {
|
|
65
|
+
deploymentYamlPartsFactory({ deployId, env, suffix, resources, replicas, image }) {
|
|
66
66
|
return `apiVersion: apps/v1
|
|
67
67
|
kind: Deployment
|
|
68
68
|
metadata:
|
|
@@ -81,7 +81,7 @@ spec:
|
|
|
81
81
|
spec:
|
|
82
82
|
containers:
|
|
83
83
|
- name: ${deployId}-${env}-${suffix}
|
|
84
|
-
image: localhost/rockylinux9-underpost:${Underpost.version}
|
|
84
|
+
image: ${image ?? `localhost/rockylinux9-underpost:${Underpost.version}`}
|
|
85
85
|
# resources:
|
|
86
86
|
# requests:
|
|
87
87
|
# memory: "${resources.requests.memory}"
|
|
@@ -118,6 +118,7 @@ spec:
|
|
|
118
118
|
async buildManifest(deployList, env, options) {
|
|
119
119
|
const resources = UnderpostDeploy.API.resourcesFactory();
|
|
120
120
|
const replicas = options.replicas;
|
|
121
|
+
const image = options.image;
|
|
121
122
|
|
|
122
123
|
for (const _deployId of deployList.split(',')) {
|
|
123
124
|
const deployId = _deployId.trim();
|
|
@@ -144,6 +145,7 @@ ${UnderpostDeploy.API.deploymentYamlPartsFactory({
|
|
|
144
145
|
suffix: deploymentVersion,
|
|
145
146
|
resources,
|
|
146
147
|
replicas,
|
|
148
|
+
image,
|
|
147
149
|
}).replace('{{ports}}', buildKindPorts(fromPort, toPort))}
|
|
148
150
|
`;
|
|
149
151
|
}
|
|
@@ -241,6 +243,7 @@ spec:
|
|
|
241
243
|
expose: false,
|
|
242
244
|
cert: false,
|
|
243
245
|
versions: '',
|
|
246
|
+
image: '',
|
|
244
247
|
traffic: '',
|
|
245
248
|
replicas: '',
|
|
246
249
|
node: '',
|
package/src/cli/index.js
CHANGED
|
@@ -164,6 +164,7 @@ program
|
|
|
164
164
|
'Builds Kubernetes YAML manifests, including deployments, services, proxies, and secrets.',
|
|
165
165
|
)
|
|
166
166
|
.option('--replicas <replicas>', 'Sets a custom number of replicas for deployments.')
|
|
167
|
+
.option('--image <image>', 'Sets a custom image for deployments.')
|
|
167
168
|
.option('--versions <deployment-versions>', 'A comma-separated list of custom deployment versions.')
|
|
168
169
|
.option('--traffic <traffic-versions>', 'A comma-separated list of custom deployment traffic weights.')
|
|
169
170
|
.option('--disable-update-deployment', 'Disables updates to deployments.')
|
package/src/cli/lxd.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LXD module for managing LXD virtual machines and networks.
|
|
3
|
+
* @module src/cli/lxd.js
|
|
4
|
+
* @namespace UnderpostLxd
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
import { getNpmRootPath } from '../server/conf.js';
|
|
2
8
|
import { getLocalIPv4Address } from '../server/dns.js';
|
|
3
9
|
import { pbcopy, shellExec } from '../server/process.js';
|
|
@@ -32,6 +38,7 @@ class UnderpostLxd {
|
|
|
32
38
|
* @param {string} [options.deleteExpose=''] - Delete exposed ports from a VM (format: 'vmName:port1,port2').
|
|
33
39
|
* @param {string} [options.test=''] - Test health, status and network connectivity for a VM.
|
|
34
40
|
* @param {string} [options.autoExposeK8sPorts=''] - Automatically expose common Kubernetes ports for the VM.
|
|
41
|
+
* @memberof UnderpostLxd
|
|
35
42
|
*/
|
|
36
43
|
async callback(
|
|
37
44
|
options = {
|
package/src/cli/repository.js
CHANGED
|
@@ -49,6 +49,7 @@ class UnderpostRepository {
|
|
|
49
49
|
},
|
|
50
50
|
) {
|
|
51
51
|
if (commitType === 'reset') {
|
|
52
|
+
pbcopy(shellExec(`git --no-pager log -1 --pretty=%B`, { stdout: true }));
|
|
52
53
|
shellExec(`cd ${repoPath} && git reset --soft HEAD~${isNaN(parseInt(subModule)) ? 1 : parseInt(subModule)}`);
|
|
53
54
|
return;
|
|
54
55
|
}
|
package/src/cli/run.js
CHANGED
|
@@ -128,8 +128,10 @@ class UnderpostRun {
|
|
|
128
128
|
}
|
|
129
129
|
},
|
|
130
130
|
'ssh-deploy': (path, options = UnderpostRun.DEFAULT_OPTION) => {
|
|
131
|
+
actionInitLog();
|
|
131
132
|
const baseCommand = options.dev || true ? 'node bin' : 'underpost';
|
|
132
133
|
shellCd('/home/dd/engine');
|
|
134
|
+
shellExec(`node bin/build dd-${path.split('engine-')[1]} conf`);
|
|
133
135
|
shellExec(`git reset`);
|
|
134
136
|
shellExec(`${baseCommand} cmt . --empty cd ssh-${path}`);
|
|
135
137
|
shellExec(`${baseCommand} push . underpostnet/engine`);
|
|
@@ -148,13 +150,24 @@ class UnderpostRun {
|
|
|
148
150
|
let [deployId, subConf] = path.split(',');
|
|
149
151
|
shellExec(`npm run dev-api ${deployId} ${subConf}`);
|
|
150
152
|
},
|
|
151
|
-
|
|
153
|
+
sync: (path, options = UnderpostRun.DEFAULT_OPTION) => {
|
|
154
|
+
const env = options.dev ? 'development' : 'production';
|
|
152
155
|
const baseCommand = options.dev || true ? 'node bin' : 'underpost';
|
|
153
|
-
|
|
154
|
-
|
|
156
|
+
shellExec(`${baseCommand} run clean`);
|
|
157
|
+
const defaultPaht = ['dd', 1, ``, 'kind-control-plane'];
|
|
158
|
+
let [deployId, replicas, image, node] = path ? path.split(',') : defaultPaht;
|
|
155
159
|
deployId = deployId ?? defaultPaht[0];
|
|
156
|
-
|
|
157
|
-
|
|
160
|
+
replicas = replicas ?? defaultPaht[1];
|
|
161
|
+
image = image ?? defaultPaht[2];
|
|
162
|
+
node = node ?? defaultPaht[3];
|
|
163
|
+
shellExec(
|
|
164
|
+
`${baseCommand} deploy --kubeadm --build-manifest --sync --info-router --replicas ${
|
|
165
|
+
replicas ?? 1
|
|
166
|
+
} --node ${node}${image ? ` --image ${image}` : ''} ${deployId} ${env}`,
|
|
167
|
+
);
|
|
168
|
+
},
|
|
169
|
+
'ls-deployments': async (path, options = UnderpostRun.DEFAULT_OPTION) => {
|
|
170
|
+
console.table(await UnderpostDeploy.API.get(path, 'deployments'));
|
|
158
171
|
},
|
|
159
172
|
monitor: (path, options = UnderpostRun.DEFAULT_OPTION) => {
|
|
160
173
|
const pid = getTerminalPid();
|
package/src/cli/ssh.js
CHANGED
|
@@ -1,23 +1,37 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* SSH module for managing SSH key generation and connection setup.
|
|
3
|
+
* @module src/cli/ssh.js
|
|
4
|
+
* @namespace UnderpostSSH
|
|
5
|
+
*/
|
|
6
|
+
|
|
2
7
|
import { shellExec } from '../server/process.js';
|
|
3
8
|
|
|
9
|
+
/**
|
|
10
|
+
* @class UnderpostSSH
|
|
11
|
+
* @description Manages SSH key generation and connection setup.
|
|
12
|
+
* @memberof UnderpostSSH
|
|
13
|
+
*/
|
|
4
14
|
class UnderpostSSH {
|
|
5
15
|
static API = {
|
|
6
16
|
/**
|
|
7
17
|
* @method callback
|
|
8
|
-
* @
|
|
9
|
-
*
|
|
10
|
-
*
|
|
18
|
+
* @description Manages SSH key generation and connection setup based on the default deployment ID.
|
|
19
|
+
* This function will either generate a new SSH key pair or import an existing one,
|
|
20
|
+
* then initiate the SSH connection process.
|
|
21
|
+
* @param {object} [options={ generate: false }] - Options for the SSH callback.
|
|
22
|
+
* @param {boolean} [options.generate=false] - If true, generates a new SSH key pair. Otherwise, it imports the existing one.
|
|
23
|
+
* @memberof UnderpostSSH
|
|
24
|
+
* @returns {Promise<void>}
|
|
11
25
|
*/
|
|
12
26
|
callback: async (
|
|
13
27
|
options = {
|
|
14
28
|
generate: false,
|
|
15
29
|
},
|
|
16
30
|
) => {
|
|
17
|
-
//
|
|
31
|
+
// Example usage for importing an existing key:
|
|
18
32
|
// node bin/deploy ssh root@<host> <password> import
|
|
19
33
|
|
|
20
|
-
//
|
|
34
|
+
// Example usage for generating a new key:
|
|
21
35
|
// node bin/deploy ssh root@<host> <password>
|
|
22
36
|
|
|
23
37
|
shellExec(
|
|
@@ -10,7 +10,8 @@ import { Modal } from './Modal.js';
|
|
|
10
10
|
import { NotificationManager } from './NotificationManager.js';
|
|
11
11
|
import { Translate } from './Translate.js';
|
|
12
12
|
import { Validator } from './Validator.js';
|
|
13
|
-
import { append,
|
|
13
|
+
import { append, htmls, s } from './VanillaJs.js';
|
|
14
|
+
import { getProxyPath } from './Router.js';
|
|
14
15
|
|
|
15
16
|
const Account = {
|
|
16
17
|
UpdateEvent: {},
|