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,5 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Document service module for handling document CRUD operations.
|
|
3
|
+
* Provides REST API handlers for document management with security-aware
|
|
4
|
+
* public/private document access control, search, and file cleanup integration.
|
|
5
|
+
*
|
|
6
|
+
* @module src/api/document/document.service.js
|
|
7
|
+
* @namespace DocumentService
|
|
8
|
+
*/
|
|
9
|
+
|
|
1
10
|
import { loggerFactory } from '../../server/logger.js';
|
|
2
|
-
import {
|
|
11
|
+
import { DataBaseProviderService } from '../../db/DataBaseProvider.js';
|
|
3
12
|
import { DocumentDto } from './document.model.js';
|
|
4
13
|
import { uniqueArray } from '../../client/components/core/CommonJs.js';
|
|
5
14
|
import { getBearerToken, verifyJWT } from '../../server/auth.js';
|
|
@@ -8,10 +17,26 @@ import { FileCleanup } from '../file/file.service.js';
|
|
|
8
17
|
|
|
9
18
|
const logger = loggerFactory(import.meta);
|
|
10
19
|
|
|
11
|
-
|
|
12
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Document Service for handling REST API document operations.
|
|
22
|
+
* Implements a security model for public/private document access control
|
|
23
|
+
* with support for high-query typeahead search, tag filtering, and pagination.
|
|
24
|
+
* @namespace DocumentService
|
|
25
|
+
*/
|
|
26
|
+
class DocumentService {
|
|
27
|
+
/**
|
|
28
|
+
* POST - Create a new document.
|
|
29
|
+
* @async
|
|
30
|
+
* @function post
|
|
31
|
+
* @memberof DocumentService
|
|
32
|
+
* @param {Object} req - Express request object.
|
|
33
|
+
* @param {Object} res - Express response object.
|
|
34
|
+
* @param {Object} options - Request options containing host and path.
|
|
35
|
+
* @returns {Promise<Object>} Created document object.
|
|
36
|
+
*/
|
|
37
|
+
static post = async (req, res, options) => {
|
|
13
38
|
/** @type {import('./document.model.js').DocumentModel} */
|
|
14
|
-
const Document =
|
|
39
|
+
const Document = DataBaseProviderService.getModel("Document", options);
|
|
15
40
|
|
|
16
41
|
switch (req.params.id) {
|
|
17
42
|
default:
|
|
@@ -25,14 +50,28 @@ const DocumentService = {
|
|
|
25
50
|
|
|
26
51
|
return await new Document(req.body).save();
|
|
27
52
|
}
|
|
28
|
-
}
|
|
29
|
-
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* GET - Retrieve documents.
|
|
57
|
+
* Supports public high-query search, public tag-filtered listing,
|
|
58
|
+
* and authenticated user document retrieval with pagination.
|
|
59
|
+
* @async
|
|
60
|
+
* @function get
|
|
61
|
+
* @memberof DocumentService
|
|
62
|
+
* @param {Object} req - Express request object.
|
|
63
|
+
* @param {Object} res - Express response object.
|
|
64
|
+
* @param {Object} options - Request options containing host and path.
|
|
65
|
+
* @returns {Promise<Object>} Document data with optional pagination metadata.
|
|
66
|
+
* @throws {Error} If search query is invalid or document not found.
|
|
67
|
+
*/
|
|
68
|
+
static get = async (req, res, options) => {
|
|
30
69
|
/** @type {import('./document.model.js').DocumentModel} */
|
|
31
|
-
const Document =
|
|
70
|
+
const Document = DataBaseProviderService.getModel("Document", options);
|
|
32
71
|
/** @type {import('../user/user.model.js').UserModel} */
|
|
33
|
-
const User =
|
|
72
|
+
const User = DataBaseProviderService.getModel("User", options);
|
|
34
73
|
/** @type {import('../file/file.model.js').FileModel} */
|
|
35
|
-
const File =
|
|
74
|
+
const File = DataBaseProviderService.getModel("File", options);
|
|
36
75
|
|
|
37
76
|
// High-query endpoint for typeahead search
|
|
38
77
|
//
|
|
@@ -436,12 +475,24 @@ const DocumentService = {
|
|
|
436
475
|
};
|
|
437
476
|
}
|
|
438
477
|
}
|
|
439
|
-
}
|
|
440
|
-
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* DELETE - Remove a document and its associated files.
|
|
482
|
+
* @async
|
|
483
|
+
* @function delete
|
|
484
|
+
* @memberof DocumentService
|
|
485
|
+
* @param {Object} req - Express request object.
|
|
486
|
+
* @param {Object} res - Express response object.
|
|
487
|
+
* @param {Object} options - Request options containing host and path.
|
|
488
|
+
* @returns {Promise<Object>} Deleted document object.
|
|
489
|
+
* @throws {Error} If document not found or user not authorized.
|
|
490
|
+
*/
|
|
491
|
+
static delete = async (req, res, options) => {
|
|
441
492
|
/** @type {import('./document.model.js').DocumentModel} */
|
|
442
|
-
const Document =
|
|
493
|
+
const Document = DataBaseProviderService.getModel("Document", options);
|
|
443
494
|
/** @type {import('../file/file.model.js').FileModel} */
|
|
444
|
-
const File =
|
|
495
|
+
const File = DataBaseProviderService.getModel("File", options);
|
|
445
496
|
|
|
446
497
|
switch (req.params.id) {
|
|
447
498
|
default: {
|
|
@@ -460,12 +511,25 @@ const DocumentService = {
|
|
|
460
511
|
return await Document.findByIdAndDelete(req.params.id);
|
|
461
512
|
}
|
|
462
513
|
}
|
|
463
|
-
}
|
|
464
|
-
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* PUT - Update a document.
|
|
518
|
+
* Cleans up replaced files and handles tag-based isPublic extraction.
|
|
519
|
+
* @async
|
|
520
|
+
* @function put
|
|
521
|
+
* @memberof DocumentService
|
|
522
|
+
* @param {Object} req - Express request object.
|
|
523
|
+
* @param {Object} res - Express response object.
|
|
524
|
+
* @param {Object} options - Request options containing host and path.
|
|
525
|
+
* @returns {Promise<Object>} Updated document object.
|
|
526
|
+
* @throws {Error} If document not found.
|
|
527
|
+
*/
|
|
528
|
+
static put = async (req, res, options) => {
|
|
465
529
|
/** @type {import('./document.model.js').DocumentModel} */
|
|
466
|
-
const Document =
|
|
530
|
+
const Document = DataBaseProviderService.getModel("Document", options);
|
|
467
531
|
/** @type {import('../file/file.model.js').FileModel} */
|
|
468
|
-
const File =
|
|
532
|
+
const File = DataBaseProviderService.getModel("File", options);
|
|
469
533
|
|
|
470
534
|
switch (req.params.id) {
|
|
471
535
|
default: {
|
|
@@ -500,10 +564,23 @@ const DocumentService = {
|
|
|
500
564
|
return await Document.findByIdAndUpdate(req.params.id, req.body, { returnDocument: 'after' });
|
|
501
565
|
}
|
|
502
566
|
}
|
|
503
|
-
}
|
|
504
|
-
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* PATCH - Partially update a document.
|
|
571
|
+
* Supports toggle-public and copy-share-link operations.
|
|
572
|
+
* @async
|
|
573
|
+
* @function patch
|
|
574
|
+
* @memberof DocumentService
|
|
575
|
+
* @param {Object} req - Express request object.
|
|
576
|
+
* @param {Object} res - Express response object.
|
|
577
|
+
* @param {Object} options - Request options containing host and path.
|
|
578
|
+
* @returns {Promise<Object>} Updated document or result object.
|
|
579
|
+
* @throws {Error} If document not found or invalid patch endpoint.
|
|
580
|
+
*/
|
|
581
|
+
static patch = async (req, res, options) => {
|
|
505
582
|
/** @type {import('./document.model.js').DocumentModel} */
|
|
506
|
-
const Document =
|
|
583
|
+
const Document = DataBaseProviderService.getModel("Document", options);
|
|
507
584
|
|
|
508
585
|
if (req.path.includes('/toggle-public')) {
|
|
509
586
|
const document = await Document.findById(req.params.id);
|
|
@@ -550,7 +627,7 @@ const DocumentService = {
|
|
|
550
627
|
}
|
|
551
628
|
|
|
552
629
|
throw new Error('Invalid patch endpoint');
|
|
553
|
-
}
|
|
554
|
-
}
|
|
630
|
+
};
|
|
631
|
+
}
|
|
555
632
|
|
|
556
|
-
export { DocumentService };
|
|
633
|
+
export { DocumentService };
|
|
@@ -2,21 +2,27 @@ import { adminGuard } from '../../server/auth.js';
|
|
|
2
2
|
import { loggerFactory } from '../../server/logger.js';
|
|
3
3
|
import { FileController } from './file.controller.js';
|
|
4
4
|
import express from 'express';
|
|
5
|
+
|
|
5
6
|
const logger = loggerFactory(import.meta);
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
router
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
8
|
+
class FileRouter {
|
|
9
|
+
/**
|
|
10
|
+
* @param {import('../types.js').RouterOptions} options
|
|
11
|
+
* @returns {import('express').Router}
|
|
12
|
+
*/
|
|
13
|
+
static router(options) {
|
|
14
|
+
const router = express.Router();
|
|
15
|
+
router.post(`/:id`, options.authMiddleware, async (req, res) => await FileController.post(req, res, options));
|
|
16
|
+
router.post(`/`, options.authMiddleware, async (req, res) => await FileController.post(req, res, options));
|
|
17
|
+
router.get(`/blob/:id`, async (req, res) => await FileController.get(req, res, options));
|
|
18
|
+
router.get(`/:id`, async (req, res) => await FileController.get(req, res, options));
|
|
19
|
+
router.get(`/`, async (req, res) => await FileController.get(req, res, options));
|
|
20
|
+
router.delete(`/:id`, options.authMiddleware, adminGuard, async (req, res) => await FileController.delete(req, res, options));
|
|
21
|
+
router.delete(`/`, options.authMiddleware, adminGuard, async (req, res) => await FileController.delete(req, res, options));
|
|
22
|
+
return router;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
19
25
|
|
|
20
|
-
const ApiRouter = FileRouter;
|
|
26
|
+
const ApiRouter = (options) => FileRouter.router(options);
|
|
21
27
|
|
|
22
28
|
export { ApiRouter, FileRouter };
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @namespace FileServiceServer
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { DataBaseProviderService } from '../../db/DataBaseProvider.js';
|
|
10
10
|
import { getBearerToken, jwtVerify } from '../../server/auth.js';
|
|
11
11
|
import { loggerFactory } from '../../server/logger.js';
|
|
12
12
|
import crypto from 'crypto';
|
|
@@ -266,7 +266,9 @@ class FileCleanup {
|
|
|
266
266
|
const newFileId = newData[field];
|
|
267
267
|
|
|
268
268
|
// If field has old file and new data changes or removes it
|
|
269
|
-
|
|
269
|
+
// newFileId === null means client explicitly wants to remove the file
|
|
270
|
+
// newFileId === undefined means field was not included in request (no change)
|
|
271
|
+
if (oldFileId && newFileId !== undefined && (newFileId === null || String(oldFileId) !== String(newFileId))) {
|
|
270
272
|
try {
|
|
271
273
|
const file = await File.findOne({ _id: oldFileId });
|
|
272
274
|
if (file) {
|
|
@@ -345,7 +347,7 @@ class FileService {
|
|
|
345
347
|
}
|
|
346
348
|
|
|
347
349
|
/** @type {import('./file.model.js').FileModel} */
|
|
348
|
-
const File =
|
|
350
|
+
const File = DataBaseProviderService.getModel("File", options);
|
|
349
351
|
|
|
350
352
|
const uploadedFiles = await FileFactory.upload(req, File);
|
|
351
353
|
return FileServiceDto.toMetadataArray(uploadedFiles);
|
|
@@ -367,11 +369,11 @@ class FileService {
|
|
|
367
369
|
*/
|
|
368
370
|
static get = async (req, res, options) => {
|
|
369
371
|
/** @type {import('./file.model.js').FileModel} */
|
|
370
|
-
const File =
|
|
372
|
+
const File = DataBaseProviderService.getModel("File", options);
|
|
371
373
|
/** @type {import('../document/document.model.js').DocumentModel} */
|
|
372
|
-
const Document =
|
|
374
|
+
const Document = DataBaseProviderService.getModel("Document", options);
|
|
373
375
|
/** @type {import('../user/user.model.js').User} */
|
|
374
|
-
const User =
|
|
376
|
+
const User = DataBaseProviderService.getModel("User", options);
|
|
375
377
|
|
|
376
378
|
const isFileAuthorized = async (fileId) => {
|
|
377
379
|
try {
|
|
@@ -482,7 +484,7 @@ class FileService {
|
|
|
482
484
|
*/
|
|
483
485
|
static delete = async (req, res, options) => {
|
|
484
486
|
/** @type {import('./file.model.js').FileModel} */
|
|
485
|
-
const File =
|
|
487
|
+
const File = DataBaseProviderService.getModel("File", options);
|
|
486
488
|
|
|
487
489
|
const result = await File.findByIdAndDelete(req.params.id);
|
|
488
490
|
|
|
@@ -4,18 +4,23 @@ import express from 'express';
|
|
|
4
4
|
|
|
5
5
|
const logger = loggerFactory(import.meta);
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
router
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
7
|
+
class TestRouter {
|
|
8
|
+
/**
|
|
9
|
+
* @param {import('../types.js').RouterOptions} options
|
|
10
|
+
* @returns {import('express').Router}
|
|
11
|
+
*/
|
|
12
|
+
static router(options) {
|
|
13
|
+
const router = express.Router();
|
|
14
|
+
router.post(`/:id`, async (req, res) => await TestController.post(req, res, options));
|
|
15
|
+
router.post(`/`, options.authMiddleware, async (req, res) => await TestController.post(req, res, options));
|
|
16
|
+
router.get(`/:id`, async (req, res) => await TestController.get(req, res, options));
|
|
17
|
+
router.get(`/`, async (req, res) => await TestController.get(req, res, options));
|
|
18
|
+
router.delete(`/:id`, async (req, res) => await TestController.delete(req, res, options));
|
|
19
|
+
router.delete(`/`, async (req, res) => await TestController.delete(req, res, options));
|
|
20
|
+
return router;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
18
23
|
|
|
19
|
-
const ApiRouter = TestRouter;
|
|
24
|
+
const ApiRouter = (options) => TestRouter.router(options);
|
|
20
25
|
|
|
21
26
|
export { ApiRouter, TestRouter };
|
package/src/api/types.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module src/api/types
|
|
3
|
+
* @description Shared type definitions for the Express router layer.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Injected dependencies and runtime configuration for every API router instance.
|
|
8
|
+
* Keyed per-host/path by the multi-tenant engine (see src/runtime/express/Express.js).
|
|
9
|
+
*
|
|
10
|
+
* @typedef {Object} RouterOptions
|
|
11
|
+
* @property {import('express').Application} app - Express application instance.
|
|
12
|
+
* @property {string} host - Per-runtime host identifier (e.g. "nexodev.org").
|
|
13
|
+
* @property {string} path - Per-runtime path prefix (e.g. "/" or "/cyberia").
|
|
14
|
+
* @property {string} apiPath - Computed base URL for the API layer.
|
|
15
|
+
* @property {string[]} origins - Allowed origins for CORS validation.
|
|
16
|
+
* @property {import('express').RequestHandler} authMiddleware - Dynamically generated JWT auth middleware (keyed by host+path).
|
|
17
|
+
* @property {Object} [db] - DataBaseProviderService configuration or instance reference.
|
|
18
|
+
* @property {Object} [mailer] - MailerProvider configuration or instance reference.
|
|
19
|
+
* @property {Object} [png] - Cached mailer image buffers (populated by UserRouter on first load).
|
|
20
|
+
* @property {Record<string, Buffer>} [png.buffer] - Map of image key to raw PNG buffer.
|
|
21
|
+
* @property {Function} [png.header] - Sets CORS/Content-Type response headers for PNG responses.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
export { };
|
|
@@ -22,12 +22,13 @@ const _guestTtlMs = () => {
|
|
|
22
22
|
*/
|
|
23
23
|
const buildGuestUser = (options) => {
|
|
24
24
|
const now = new Date().toISOString();
|
|
25
|
-
const
|
|
25
|
+
const objectId = new mongoose.Types.ObjectId().toString();
|
|
26
26
|
const role = 'guest';
|
|
27
|
+
const username = `${role}${objectId.slice(-5)}`;
|
|
27
28
|
return {
|
|
28
|
-
_id: `${role}${
|
|
29
|
-
username
|
|
30
|
-
email: `${
|
|
29
|
+
_id: `${role}${objectId}`,
|
|
30
|
+
username,
|
|
31
|
+
email: `${username}@${options.host || 'localhost'}`,
|
|
31
32
|
password: hashPassword(process.env.JWT_SECRET),
|
|
32
33
|
role,
|
|
33
34
|
emailConfirmed: false,
|