underpost 3.2.8 → 3.2.10
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/.github/workflows/npmpkg.ci.yml +1 -0
- package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
- package/.github/workflows/release.cd.yml +1 -0
- package/.vscode/settings.json +10 -5
- package/CHANGELOG.md +223 -2
- package/CLI-HELP.md +36 -7
- package/README.md +38 -9
- package/bin/build.js +27 -11
- package/bin/deploy.js +20 -21
- package/bin/file.js +32 -13
- package/bin/index.js +2 -1
- package/bin/vs.js +1 -1
- package/bump.config.js +26 -0
- package/conf.js +20 -4
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +2 -2
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +2 -2
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +4 -2
- package/manifests/kind-config-dev.yaml +8 -0
- package/manifests/mongodb/pv-pvc.yaml +44 -8
- package/manifests/mongodb/statefulset.yaml +55 -68
- package/package.json +40 -25
- package/scripts/k3s-node-setup.sh +30 -11
- package/scripts/nat-iptables.sh +103 -18
- package/src/api/core/core.router.js +19 -14
- package/src/api/core/core.service.js +5 -5
- package/src/api/default/default.router.js +22 -18
- package/src/api/default/default.service.js +5 -5
- package/src/api/document/document.router.js +28 -23
- package/src/api/document/document.service.js +100 -23
- package/src/api/file/file.router.js +19 -13
- package/src/api/file/file.service.js +9 -7
- package/src/api/test/test.router.js +17 -12
- package/src/api/types.js +24 -0
- package/src/api/user/guest.service.js +5 -4
- package/src/api/user/user.router.js +297 -288
- package/src/api/user/user.service.js +100 -35
- package/src/cli/baremetal.js +20 -11
- package/src/cli/cluster.js +243 -55
- package/src/cli/db.js +106 -62
- package/src/cli/deploy.js +297 -154
- package/src/cli/fs.js +19 -3
- package/src/cli/index.js +37 -9
- package/src/cli/ipfs.js +4 -6
- package/src/cli/kubectl.js +4 -1
- package/src/cli/lxd.js +217 -135
- package/src/cli/release.js +289 -131
- package/src/cli/repository.js +91 -34
- package/src/cli/run.js +297 -56
- package/src/cli/test.js +9 -3
- package/src/client/Default.index.js +9 -3
- package/src/client/components/core/Auth.js +19 -5
- package/src/client/components/core/Docs.js +6 -34
- package/src/client/components/core/FileExplorer.js +6 -6
- package/src/client/components/core/Modal.js +65 -2
- package/src/client/components/core/PanelForm.js +56 -52
- package/src/client/components/core/Recover.js +4 -4
- package/src/client/components/core/Worker.js +170 -350
- package/src/client/services/default/default.management.js +20 -25
- package/src/client/services/user/guest.service.js +10 -3
- package/src/client/sw/core.sw.js +174 -112
- package/src/db/DataBaseProvider.js +120 -20
- package/src/db/mongo/MongoBootstrap.js +587 -0
- package/src/db/mongo/MongooseDB.js +126 -22
- package/src/index.js +1 -1
- package/src/runtime/express/Express.js +2 -2
- package/src/runtime/wp/Wp.js +8 -5
- package/src/server/auth.js +2 -2
- package/src/server/client-build-docs.js +1 -1
- package/src/server/client-build.js +94 -129
- package/src/server/conf.js +20 -65
- package/src/server/data-query.js +32 -20
- package/src/server/dns.js +22 -0
- package/src/server/process.js +180 -19
- package/src/server/runtime.js +1 -1
- package/src/server/start.js +26 -7
- package/src/server/valkey.js +9 -2
- package/src/ws/IoInterface.js +16 -16
- package/src/ws/core/channels/core.ws.chat.js +11 -11
- package/src/ws/core/channels/core.ws.mailer.js +29 -29
- package/src/ws/core/channels/core.ws.stream.js +19 -19
- package/src/ws/core/core.ws.connection.js +8 -8
- package/src/ws/core/core.ws.server.js +6 -5
- package/src/ws/default/channels/default.ws.main.js +10 -10
- package/src/ws/default/default.ws.connection.js +4 -4
- package/src/ws/default/default.ws.server.js +4 -3
- package/typedoc.json +10 -1
- package/src/client/ssr/email/DefaultRecoverEmail.js +0 -21
- package/src/client/ssr/email/DefaultVerifyEmail.js +0 -17
- /package/src/client/ssr/{offline → views}/Maintenance.js +0 -0
- /package/src/client/ssr/{offline → views}/NoNetworkConnection.js +0 -0
- /package/src/client/ssr/{pages → views}/Test.js +0 -0
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User service module for handling user account operations.
|
|
3
|
+
* Provides REST API handlers for authentication, registration, profile management,
|
|
4
|
+
* email verification, password recovery, and guest user lifecycle management.
|
|
5
|
+
*
|
|
6
|
+
* @module src/api/user/user.service.js
|
|
7
|
+
* @namespace UserService
|
|
8
|
+
*/
|
|
9
|
+
|
|
1
10
|
import { loggerFactory } from '../../server/logger.js';
|
|
2
11
|
import { DataQuery } from '../../server/data-query.js';
|
|
3
12
|
import {
|
|
@@ -15,21 +24,40 @@ import { MailerProvider } from '../../mailer/MailerProvider.js';
|
|
|
15
24
|
import { CoreWsEmitter } from '../../ws/core/core.ws.emit.js';
|
|
16
25
|
import { CoreWsMailerChannel } from '../../ws/core/channels/core.ws.mailer.js';
|
|
17
26
|
import validator from 'validator';
|
|
18
|
-
import {
|
|
27
|
+
import { DataBaseProviderService } from '../../db/DataBaseProvider.js';
|
|
19
28
|
import { FileFactory, FileCleanup } from '../file/file.service.js';
|
|
20
29
|
import { UserDto } from './user.model.js';
|
|
21
30
|
import { timer } from '../../client/components/core/CommonJs.js';
|
|
22
31
|
import { GuestService } from './guest.service.js';
|
|
32
|
+
import { resolveHostKeyContext } from '../../server/conf.js';
|
|
23
33
|
|
|
24
34
|
const logger = loggerFactory(import.meta);
|
|
25
35
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
36
|
+
/**
|
|
37
|
+
* User Service for handling REST API user operations.
|
|
38
|
+
* Manages authentication, registration, profile CRUD, email verification,
|
|
39
|
+
* password recovery, session management, and guest user lifecycle.
|
|
40
|
+
* @namespace UserService
|
|
41
|
+
*/
|
|
42
|
+
class UserService {
|
|
43
|
+
/**
|
|
44
|
+
* POST - Create or authenticate users.
|
|
45
|
+
* Supports authentication, guest account creation, email verification,
|
|
46
|
+
* and password recovery email sending.
|
|
47
|
+
* @async
|
|
48
|
+
* @function post
|
|
49
|
+
* @memberof UserService
|
|
50
|
+
* @param {Object} req - Express request object.
|
|
51
|
+
* @param {Object} res - Express response object.
|
|
52
|
+
* @param {Object} options - Request options containing host and path.
|
|
53
|
+
* @returns {Promise<Object>} User data with auth token, or status message.
|
|
54
|
+
* @throws {Error} If authentication fails, email is invalid, or email send error.
|
|
55
|
+
*/
|
|
56
|
+
static post = async (req, res, options) => {
|
|
31
57
|
/** @type {import('../file/file.model.js').FileModel} */
|
|
32
|
-
const File =
|
|
58
|
+
const File = DataBaseProviderService.getModel('File', options);
|
|
59
|
+
/** @type {import('./user.model.js').UserModel} */
|
|
60
|
+
const User = DataBaseProviderService.getModel('User', options);
|
|
33
61
|
|
|
34
62
|
if (req.params.id === 'recover-verify-email') {
|
|
35
63
|
const user = await User.findOne({
|
|
@@ -44,11 +72,10 @@ const UserService = {
|
|
|
44
72
|
|
|
45
73
|
const token = jwtSign({ email: req.body.email }, options, 15);
|
|
46
74
|
const payloadToken = jwtSign({ email: req.body.email }, options, 15);
|
|
47
|
-
const id =
|
|
75
|
+
const id = resolveHostKeyContext(options);
|
|
48
76
|
const translate = MailerProvider.instance[id].translateTemplates.recoverEmail;
|
|
49
|
-
const recoverUrl = `${process.env.NODE_ENV === 'development' ? 'http://' : 'https://'}${req.body.hostname}${
|
|
50
|
-
|
|
51
|
-
}recover?payload=${payloadToken}`;
|
|
77
|
+
const recoverUrl = `${process.env.NODE_ENV === 'development' ? 'http://' : 'https://'}${req.body.hostname}${req.body.proxyPath
|
|
78
|
+
}recover?payload=${payloadToken}`;
|
|
52
79
|
const sendResult = await MailerProvider.send({
|
|
53
80
|
id,
|
|
54
81
|
sendOptions: {
|
|
@@ -81,7 +108,7 @@ const UserService = {
|
|
|
81
108
|
if (!validator.isEmail(req.body.email)) throw { message: 'invalid email' };
|
|
82
109
|
|
|
83
110
|
const token = jwtSign({ email: req.body.email }, options, 15);
|
|
84
|
-
const id =
|
|
111
|
+
const id = resolveHostKeyContext(options);
|
|
85
112
|
const user = await User.findById(req.auth.user._id);
|
|
86
113
|
|
|
87
114
|
if (user.emailConfirmed) throw new Error('email already confirmed');
|
|
@@ -130,10 +157,9 @@ const UserService = {
|
|
|
130
157
|
});
|
|
131
158
|
const getMinutesRemaining = () => (-1 * user.failedLoginAttempts - new Date().getTime()) / (1000 * 60);
|
|
132
159
|
const accountLocketMessage = () =>
|
|
133
|
-
`Account locked. Please try again in: ${
|
|
134
|
-
getMinutesRemaining()
|
|
135
|
-
|
|
136
|
-
: `${getMinutesRemaining().toFixed(0)} min`
|
|
160
|
+
`Account locked. Please try again in: ${getMinutesRemaining() < 1
|
|
161
|
+
? `${(getMinutesRemaining() * 60).toFixed(0)} s`
|
|
162
|
+
: `${getMinutesRemaining().toFixed(0)} min`
|
|
137
163
|
}.`;
|
|
138
164
|
|
|
139
165
|
if (user) {
|
|
@@ -231,13 +257,27 @@ const UserService = {
|
|
|
231
257
|
default:
|
|
232
258
|
return await createUserAndSession(req, res, User, options);
|
|
233
259
|
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* GET - Retrieve user data.
|
|
264
|
+
* Supports public profile lookup by username, asset retrieval,
|
|
265
|
+
* email lookup, password recovery flow, email verification,
|
|
266
|
+
* admin user listing, authentication refresh, and profile retrieval.
|
|
267
|
+
* @async
|
|
268
|
+
* @function get
|
|
269
|
+
* @memberof UserService
|
|
270
|
+
* @param {Object} req - Express request object.
|
|
271
|
+
* @param {Object} res - Express response object.
|
|
272
|
+
* @param {Object} options - Request options containing host and path.
|
|
273
|
+
* @returns {Promise<Object>} User data with optional session token.
|
|
274
|
+
* @throws {Error} If user not found, profile is private, or token invalid.
|
|
275
|
+
*/
|
|
276
|
+
static get = async (req, res, options) => {
|
|
239
277
|
/** @type {import('../file/file.model.js').FileModel} */
|
|
240
|
-
const File =
|
|
278
|
+
const File = DataBaseProviderService.getModel('File', options);
|
|
279
|
+
/** @type {import('./user.model.js').UserModel} */
|
|
280
|
+
const User = DataBaseProviderService.getModel('User', options);
|
|
241
281
|
|
|
242
282
|
if (req.path.startsWith('/u/')) {
|
|
243
283
|
// First lookup user by username
|
|
@@ -309,7 +349,7 @@ const UserService = {
|
|
|
309
349
|
{
|
|
310
350
|
const user = await User.findByIdAndUpdate(_id, { emailConfirmed: true }, { runValidators: true });
|
|
311
351
|
}
|
|
312
|
-
const userWsId = CoreWsMailerChannel.getUserWsId(
|
|
352
|
+
const userWsId = CoreWsMailerChannel.getUserWsId(resolveHostKeyContext(options), user._id.toString());
|
|
313
353
|
if (userWsId && CoreWsMailerChannel.client[userWsId]) {
|
|
314
354
|
CoreWsEmitter.emit(CoreWsMailerChannel.channel, CoreWsMailerChannel.client[userWsId], {
|
|
315
355
|
status: 'email-confirmed',
|
|
@@ -384,10 +424,23 @@ const UserService = {
|
|
|
384
424
|
}
|
|
385
425
|
}
|
|
386
426
|
}
|
|
387
|
-
}
|
|
388
|
-
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* DELETE - Remove user accounts or log out sessions.
|
|
431
|
+
* Supports admin bulk deletion, user self-deletion, and session logout.
|
|
432
|
+
* @async
|
|
433
|
+
* @function delete
|
|
434
|
+
* @memberof UserService
|
|
435
|
+
* @param {Object} req - Express request object.
|
|
436
|
+
* @param {Object} res - Express response object.
|
|
437
|
+
* @param {Object} options - Request options containing host and path.
|
|
438
|
+
* @returns {Promise<Object>} Deleted user data or logout status message.
|
|
439
|
+
* @throws {Error} If logout fails, user not found, or token invalid.
|
|
440
|
+
*/
|
|
441
|
+
static delete = async (req, res, options) => {
|
|
389
442
|
/** @type {import('./user.model.js').UserModel} */
|
|
390
|
-
const User =
|
|
443
|
+
const User = DataBaseProviderService.getModel('User', options);
|
|
391
444
|
|
|
392
445
|
if (req.params.id === 'logout') {
|
|
393
446
|
const result = await logoutSession(User, req, res);
|
|
@@ -417,13 +470,25 @@ const UserService = {
|
|
|
417
470
|
}
|
|
418
471
|
}
|
|
419
472
|
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* PUT - Update user data.
|
|
477
|
+
* Supports profile image upload, password recovery, and profile field updates.
|
|
478
|
+
* @async
|
|
479
|
+
* @function put
|
|
480
|
+
* @memberof UserService
|
|
481
|
+
* @param {Object} req - Express request object.
|
|
482
|
+
* @param {Object} res - Express response object.
|
|
483
|
+
* @param {Object} options - Request options containing host and path.
|
|
484
|
+
* @returns {Promise<Object>} Updated user data.
|
|
485
|
+
* @throws {Error} If user not found, token invalid, or invalid file.
|
|
486
|
+
*/
|
|
487
|
+
static put = async (req, res, options) => {
|
|
425
488
|
/** @type {import('../file/file.model.js').FileModel} */
|
|
426
|
-
const File =
|
|
489
|
+
const File = DataBaseProviderService.getModel('File', options);
|
|
490
|
+
/** @type {import('./user.model.js').UserModel} */
|
|
491
|
+
const User = DataBaseProviderService.getModel('User', options);
|
|
427
492
|
|
|
428
493
|
// req.path | req.baseUrl
|
|
429
494
|
|
|
@@ -511,7 +576,7 @@ const UserService = {
|
|
|
511
576
|
}
|
|
512
577
|
}
|
|
513
578
|
}
|
|
514
|
-
}
|
|
515
|
-
}
|
|
579
|
+
};
|
|
580
|
+
}
|
|
516
581
|
|
|
517
|
-
export { UserService };
|
|
582
|
+
export { UserService };
|
package/src/cli/baremetal.js
CHANGED
|
@@ -356,7 +356,7 @@ class UnderpostBaremetal {
|
|
|
356
356
|
|
|
357
357
|
// Build phase (skip if upload-only mode)
|
|
358
358
|
if (options.packerMaasImageBuild) {
|
|
359
|
-
if (shellExec('packer version').code !== 0) {
|
|
359
|
+
if (shellExec('packer version', { silentOnError: true }).code !== 0) {
|
|
360
360
|
throw new Error('Packer is not installed. Please install Packer to proceed.');
|
|
361
361
|
}
|
|
362
362
|
|
|
@@ -424,7 +424,9 @@ rm -rf ${artifacts.join(' ')}`);
|
|
|
424
424
|
const uploadCmd = `${uploadScript} ${process.env.MAAS_ADMIN_USERNAME} "${workflow.maas.name}" "${workflow.maas.title}" "${workflow.maas.architecture}" "${workflow.maas.base_image}" "${workflow.maas.filetype}" "${tarballPath}"`;
|
|
425
425
|
|
|
426
426
|
logger.info(`Uploading to MAAS using: ${uploadScript}`);
|
|
427
|
-
|
|
427
|
+
// silentOnError: caller logs stdout/stderr structure on failure
|
|
428
|
+
// before throwing its own, more informative error.
|
|
429
|
+
const uploadResult = shellExec(uploadCmd, { silentOnError: true });
|
|
428
430
|
if (uploadResult.code !== 0) {
|
|
429
431
|
logger.error(`Upload failed with exit code: ${uploadResult.code}`);
|
|
430
432
|
if (uploadResult.stdout) {
|
|
@@ -2895,9 +2897,16 @@ EOF`);
|
|
|
2895
2897
|
for (const mountPath of mounts[mountCmd]) {
|
|
2896
2898
|
const hostMountPath = `${process.env.NFS_EXPORT_PATH}/${hostname}${mountPath}`;
|
|
2897
2899
|
// Check if the path is already mounted using `mountpoint` command.
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2900
|
+
// `mountpoint` exits 1 when the path is not a mountpoint — silentOnError
|
|
2901
|
+
// prevents ShellExecError so we can inspect stdout/stderr for the string.
|
|
2902
|
+
const mountpointOut = shellExec(`mountpoint ${hostMountPath}`, {
|
|
2903
|
+
silent: true,
|
|
2904
|
+
stdout: true,
|
|
2905
|
+
silentOnError: true,
|
|
2906
|
+
});
|
|
2907
|
+
const isPathMounted = typeof mountpointOut === 'string' && mountpointOut.length > 0
|
|
2908
|
+
? !mountpointOut.match('not a mountpoint') && !mountpointOut.match('No such file')
|
|
2909
|
+
: false;
|
|
2901
2910
|
|
|
2902
2911
|
if (isPathMounted) {
|
|
2903
2912
|
logger.warn('Nfs path already mounted', mountPath);
|
|
@@ -3041,10 +3050,10 @@ udp-port = 32766
|
|
|
3041
3050
|
// Check both /usr/local/bin (compiled) and system paths
|
|
3042
3051
|
let qemuAarch64Path = null;
|
|
3043
3052
|
|
|
3044
|
-
if (shellExec('test -x /usr/local/bin/qemu-system-aarch64').code === 0) {
|
|
3053
|
+
if (shellExec('test -x /usr/local/bin/qemu-system-aarch64', { silentOnError: true }).code === 0) {
|
|
3045
3054
|
qemuAarch64Path = '/usr/local/bin/qemu-system-aarch64';
|
|
3046
|
-
} else if (shellExec('which qemu-system-aarch64').code === 0) {
|
|
3047
|
-
qemuAarch64Path = shellExec('which qemu-system-aarch64').
|
|
3055
|
+
} else if (shellExec('which qemu-system-aarch64', { silentOnError: true }).code === 0) {
|
|
3056
|
+
qemuAarch64Path = shellExec('which qemu-system-aarch64', { stdout: true }).trim();
|
|
3048
3057
|
}
|
|
3049
3058
|
|
|
3050
3059
|
if (!qemuAarch64Path) {
|
|
@@ -3070,10 +3079,10 @@ udp-port = 32766
|
|
|
3070
3079
|
// Check both /usr/local/bin (compiled) and system paths
|
|
3071
3080
|
let qemuX86Path = null;
|
|
3072
3081
|
|
|
3073
|
-
if (shellExec('test -x /usr/local/bin/qemu-system-x86_64').code === 0) {
|
|
3082
|
+
if (shellExec('test -x /usr/local/bin/qemu-system-x86_64', { silentOnError: true }).code === 0) {
|
|
3074
3083
|
qemuX86Path = '/usr/local/bin/qemu-system-x86_64';
|
|
3075
|
-
} else if (shellExec('which qemu-system-x86_64').code === 0) {
|
|
3076
|
-
qemuX86Path = shellExec('which qemu-system-x86_64').
|
|
3084
|
+
} else if (shellExec('which qemu-system-x86_64', { silentOnError: true }).code === 0) {
|
|
3085
|
+
qemuX86Path = shellExec('which qemu-system-x86_64', { stdout: true }).trim();
|
|
3077
3086
|
}
|
|
3078
3087
|
|
|
3079
3088
|
if (!qemuX86Path) {
|