underpost 2.8.878 → 2.8.882
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 +35 -3
- package/.env.production +40 -3
- package/.env.test +35 -3
- package/.github/workflows/release.cd.yml +3 -3
- package/README.md +20 -2
- package/bin/deploy.js +40 -0
- package/cli.md +3 -1
- package/conf.js +29 -3
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +6 -6
- package/package.json +1 -2
- package/src/api/document/document.controller.js +66 -0
- package/src/api/document/document.model.js +51 -0
- package/src/api/document/document.router.js +24 -0
- package/src/api/document/document.service.js +133 -0
- package/src/cli/deploy.js +1 -1
- package/src/cli/index.js +2 -0
- package/src/cli/repository.js +2 -0
- package/src/cli/run.js +27 -1
- package/src/client/Default.index.js +46 -1
- package/src/client/components/core/Account.js +8 -1
- package/src/client/components/core/AgGrid.js +18 -9
- package/src/client/components/core/Auth.js +258 -89
- package/src/client/components/core/BtnIcon.js +13 -3
- package/src/client/components/core/Content.js +2 -1
- package/src/client/components/core/CssCore.js +40 -27
- package/src/client/components/core/Docs.js +189 -88
- package/src/client/components/core/Input.js +34 -19
- package/src/client/components/core/LoadingAnimation.js +5 -10
- package/src/client/components/core/Modal.js +280 -123
- package/src/client/components/core/ObjectLayerEngine.js +470 -104
- package/src/client/components/core/ObjectLayerEngineModal.js +1 -0
- package/src/client/components/core/Panel.js +9 -2
- package/src/client/components/core/PanelForm.js +234 -76
- package/src/client/components/core/Router.js +15 -15
- package/src/client/components/core/ToolTip.js +83 -19
- package/src/client/components/core/Translate.js +1 -1
- package/src/client/components/core/VanillaJs.js +7 -3
- package/src/client/components/core/windowGetDimensions.js +202 -0
- package/src/client/components/default/MenuDefault.js +105 -41
- package/src/client/components/default/RoutesDefault.js +2 -0
- package/src/client/services/default/default.management.js +1 -0
- package/src/client/services/document/document.service.js +97 -0
- package/src/client/services/file/file.service.js +2 -0
- package/src/client/ssr/Render.js +1 -1
- package/src/client/ssr/head/DefaultScripts.js +2 -0
- package/src/client/ssr/head/Seo.js +1 -0
- package/src/index.js +1 -1
- package/src/mailer/EmailRender.js +1 -1
- package/src/server/auth.js +68 -17
- package/src/server/client-build.js +2 -3
- package/src/server/client-formatted.js +40 -12
- package/src/server/conf.js +5 -1
- package/src/server/crypto.js +195 -76
- package/src/server/object-layer.js +196 -0
- package/src/server/peer.js +47 -5
- package/src/server/process.js +85 -1
- package/src/server/runtime.js +23 -23
- package/src/server/ssr.js +52 -10
- package/src/server/valkey.js +89 -1
- package/test/crypto.test.js +117 -0
package/src/server/valkey.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module for managing Valkey
|
|
3
|
+
* @module src/server/valkey.js
|
|
4
|
+
* @namespace Valkey
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
import Valkey from 'iovalkey';
|
|
2
8
|
import mongoose from 'mongoose';
|
|
3
9
|
import { hashPassword } from './auth.js';
|
|
@@ -10,11 +16,35 @@ const ValkeyInstances = {};
|
|
|
10
16
|
const DummyStores = {}; // in-memory Maps per instance
|
|
11
17
|
const ValkeyStatus = {}; // 'connected' | 'dummy' | 'error' | undefined
|
|
12
18
|
|
|
13
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Checks if any Valkey instance is connected.
|
|
21
|
+
* This is a backward-compatible overall flag.
|
|
22
|
+
* @returns {boolean} True if any instance has a 'connected' status.
|
|
23
|
+
* @memberof Valkey
|
|
24
|
+
*/
|
|
14
25
|
const isValkeyEnable = () => Object.values(ValkeyStatus).some((s) => s === 'connected');
|
|
15
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Generates a unique key for a Valkey instance based on its host and path.
|
|
29
|
+
* @param {object} [opts={ host: '', path: '' }] - The instance options.
|
|
30
|
+
* @param {string} [opts.host=''] - The host of the instance.
|
|
31
|
+
* @param {string} [opts.path=''] - The path of the instance.
|
|
32
|
+
* @returns {string} The instance key.
|
|
33
|
+
* @private
|
|
34
|
+
* @memberof Valkey
|
|
35
|
+
*/
|
|
16
36
|
const _instanceKey = (opts = { host: '', path: '' }) => `${opts.host || ''}${opts.path || ''}`;
|
|
17
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Creates and manages a connection to a Valkey server for a given instance.
|
|
40
|
+
* It sets up a client, attaches event listeners for connection status, and implements a fallback to an in-memory dummy store if the connection fails.
|
|
41
|
+
* @param {object} [instance={ host: '', path: '' }] - The instance identifier.
|
|
42
|
+
* @param {string} [instance.host=''] - The host of the instance.
|
|
43
|
+
* @param {string} [instance.path=''] - The path of the instance.
|
|
44
|
+
* @param {object} [valkeyServerConnectionOptions={ host: '', path: '' }] - Connection options for the iovalkey client.
|
|
45
|
+
* @returns {Promise<Valkey|undefined>} A promise that resolves to the Valkey client instance, or undefined if creation fails.
|
|
46
|
+
* @memberof Valkey
|
|
47
|
+
*/
|
|
18
48
|
const createValkeyConnection = async (
|
|
19
49
|
instance = { host: '', path: '' },
|
|
20
50
|
valkeyServerConnectionOptions = { host: '', path: '' },
|
|
@@ -72,6 +102,14 @@ const createValkeyConnection = async (
|
|
|
72
102
|
return ValkeyInstances[key];
|
|
73
103
|
};
|
|
74
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Factory function to create a Data Transfer Object (DTO) from a payload.
|
|
107
|
+
* It filters the payload to include only the keys specified in the `select` object.
|
|
108
|
+
* @param {object} payload - The source object.
|
|
109
|
+
* @param {object} select - An object where keys are field names and values are 1 to include them.
|
|
110
|
+
* @returns {object} A new object containing only the selected fields from the payload.
|
|
111
|
+
* @memberof Valkey
|
|
112
|
+
*/
|
|
75
113
|
const selectDtoFactory = (payload, select) => {
|
|
76
114
|
const result = {};
|
|
77
115
|
for (const key of Object.keys(select)) {
|
|
@@ -80,6 +118,12 @@ const selectDtoFactory = (payload, select) => {
|
|
|
80
118
|
return result;
|
|
81
119
|
};
|
|
82
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Factory function to create a new Valkey client instance.
|
|
123
|
+
* @param {object} options - Connection options for the iovalkey client.
|
|
124
|
+
* @returns {Promise<Valkey>} A promise that resolves to a new Valkey client.
|
|
125
|
+
* @memberof Valkey
|
|
126
|
+
*/
|
|
83
127
|
const valkeyClientFactory = async (options) => {
|
|
84
128
|
const valkey = new Valkey({
|
|
85
129
|
port: options?.port ? options.port : undefined,
|
|
@@ -103,6 +147,15 @@ const valkeyClientFactory = async (options) => {
|
|
|
103
147
|
return valkey;
|
|
104
148
|
};
|
|
105
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Retrieves an object from Valkey by key for a specific instance.
|
|
152
|
+
* If the Valkey client is not connected or an error occurs, it falls back to the dummy in-memory store.
|
|
153
|
+
* It automatically parses JSON strings.
|
|
154
|
+
* @param {object} [options={ host: '', path: '' }] - The instance identifier.
|
|
155
|
+
* @param {string} [key=''] - The key of the object to retrieve.
|
|
156
|
+
* @returns {Promise<object|string|null>} A promise that resolves to the retrieved object, string, or null if not found.
|
|
157
|
+
* @memberof Valkey
|
|
158
|
+
*/
|
|
106
159
|
const getValkeyObject = async (options = { host: '', path: '' }, key = '') => {
|
|
107
160
|
const k = _instanceKey(options);
|
|
108
161
|
const status = ValkeyStatus[k];
|
|
@@ -124,6 +177,16 @@ const getValkeyObject = async (options = { host: '', path: '' }, key = '') => {
|
|
|
124
177
|
return DummyStores[k]?.get(key) ?? null;
|
|
125
178
|
};
|
|
126
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Sets an object or string in Valkey for a specific instance.
|
|
182
|
+
* If the Valkey client is not connected, it writes to the in-memory dummy store instead.
|
|
183
|
+
* Objects are automatically stringified.
|
|
184
|
+
* @param {object} [options={ host: '', path: '' }] - The instance identifier.
|
|
185
|
+
* @param {string} [key=''] - The key under which to store the payload.
|
|
186
|
+
* @param {object|string} [payload={}] - The data to store.
|
|
187
|
+
* @returns {Promise<string>} A promise that resolves to 'OK' on success.
|
|
188
|
+
* @memberof Valkey
|
|
189
|
+
*/
|
|
127
190
|
const setValkeyObject = async (options = { host: '', path: '' }, key = '', payload = {}) => {
|
|
128
191
|
const k = _instanceKey(options);
|
|
129
192
|
const isString = typeof payload === 'string';
|
|
@@ -141,6 +204,16 @@ const setValkeyObject = async (options = { host: '', path: '' }, key = '', paylo
|
|
|
141
204
|
return 'OK';
|
|
142
205
|
};
|
|
143
206
|
|
|
207
|
+
/**
|
|
208
|
+
* Updates an existing object in Valkey by merging it with a new payload.
|
|
209
|
+
* It retrieves the current object, merges it with the new payload, and sets the updated object back.
|
|
210
|
+
* It also updates the `updatedAt` timestamp.
|
|
211
|
+
* @param {object} [options={ host: '', path: '' }] - The instance identifier.
|
|
212
|
+
* @param {string} [key=''] - The key of the object to update.
|
|
213
|
+
* @param {object} [payload={}] - The new data to merge into the object.
|
|
214
|
+
* @returns {Promise<string>} A promise that resolves to the result of the set operation.
|
|
215
|
+
* @memberof Valkey
|
|
216
|
+
*/
|
|
144
217
|
const updateValkeyObject = async (options = { host: '', path: '' }, key = '', payload = {}) => {
|
|
145
218
|
let base = await getValkeyObject(options, key);
|
|
146
219
|
if (typeof base !== 'object' || base === null) base = {};
|
|
@@ -148,6 +221,17 @@ const updateValkeyObject = async (options = { host: '', path: '' }, key = '', pa
|
|
|
148
221
|
return await setValkeyObject(options, key, { ...base, ...payload });
|
|
149
222
|
};
|
|
150
223
|
|
|
224
|
+
/**
|
|
225
|
+
* Factory function to create a new object based on a model schema.
|
|
226
|
+
* It generates a new object with default properties like `_id`, `createdAt`, and `updatedAt`,
|
|
227
|
+
* and model-specific properties.
|
|
228
|
+
* @param {object} [options={ host: 'localhost', path: '', object: {} }] - Options for object creation.
|
|
229
|
+
* @param {string} [options.host='localhost'] - The host context for the object.
|
|
230
|
+
* @param {object} [options.object={}] - An initial object to extend.
|
|
231
|
+
* @param {string} [model=''] - The name of the model schema to use (e.g., 'user').
|
|
232
|
+
* @returns {Promise<object>} A promise that resolves to the newly created object.
|
|
233
|
+
* @memberof Valkey
|
|
234
|
+
*/
|
|
151
235
|
const valkeyObjectFactory = async (options = { host: 'localhost', path: '', object: {} }, model = '') => {
|
|
152
236
|
const idoDate = new Date().toISOString();
|
|
153
237
|
options.object = options.object || {};
|
|
@@ -181,6 +265,10 @@ const valkeyObjectFactory = async (options = { host: 'localhost', path: '', obje
|
|
|
181
265
|
}
|
|
182
266
|
};
|
|
183
267
|
|
|
268
|
+
/**
|
|
269
|
+
* A collection of Valkey-related API functions.
|
|
270
|
+
* @type {object}
|
|
271
|
+
*/
|
|
184
272
|
const ValkeyAPI = {
|
|
185
273
|
valkeyClientFactory,
|
|
186
274
|
selectDtoFactory,
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module crypto.test
|
|
3
|
+
* @description Unit tests for SymmetricCrypto and AsymmetricCrypto classes
|
|
4
|
+
* in the crypto module.
|
|
5
|
+
* * Uses 'chai' for assertions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Import Chai's assertion library
|
|
9
|
+
import { expect } from 'chai';
|
|
10
|
+
|
|
11
|
+
// Import the cryptographic classes from the Canvas's refactored module
|
|
12
|
+
import { SymmetricCrypto, AsymmetricCrypto } from '../src/server/crypto.js';
|
|
13
|
+
|
|
14
|
+
// Define a common plaintext message for testing
|
|
15
|
+
const plaintext = 'This is a secret message for testing cryptographic operations.';
|
|
16
|
+
|
|
17
|
+
// --- Main Test Suite ---
|
|
18
|
+
|
|
19
|
+
describe('Crypto Module Tests', () => {
|
|
20
|
+
// --- SymmetricCrypto Tests (AES-256-CBC) ---
|
|
21
|
+
describe('SymmetricCrypto (AES-256-CBC)', () => {
|
|
22
|
+
/**
|
|
23
|
+
* Test case: Verify that key and IV are automatically generated.
|
|
24
|
+
*/
|
|
25
|
+
it('should generate new 32-byte key and 16-byte IV if none are provided', () => {
|
|
26
|
+
const symm = new SymmetricCrypto();
|
|
27
|
+
// Key should be 32 bytes (64 hex characters) and IV 16 bytes (32 hex characters)
|
|
28
|
+
expect(symm.encryptionKeyHex).to.be.a('string').and.have.lengthOf(64);
|
|
29
|
+
expect(symm.ivHex).to.be.a('string').and.have.lengthOf(32);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Test case: Encrypt data and ensure successful decryption back to the original plaintext.
|
|
34
|
+
*/
|
|
35
|
+
it('should encrypt and successfully decrypt data', () => {
|
|
36
|
+
const symm = new SymmetricCrypto();
|
|
37
|
+
const ciphertext = symm.encryptData(plaintext);
|
|
38
|
+
|
|
39
|
+
// Ciphertext should contain IV and the encrypted data, separated by a colon
|
|
40
|
+
expect(ciphertext).to.include(':');
|
|
41
|
+
|
|
42
|
+
const decryptedText = symm.decryptData(ciphertext);
|
|
43
|
+
expect(decryptedText).to.equal(plaintext);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Test case: Verify that decryption fails gracefully if the ciphertext is tampered with (e.g., corrupting the encrypted payload).
|
|
48
|
+
*
|
|
49
|
+
* FIX: We now reliably tamper with the encrypted hex string by removing the last character.
|
|
50
|
+
* This ensures the underlying crypto operation fails due to invalid hex length or incomplete data,
|
|
51
|
+
* guaranteeing the try/catch block in the implementation is hit, and the expected error is thrown.
|
|
52
|
+
*/
|
|
53
|
+
it('should fail decryption gracefully for tampered ciphertext (invalid payload)', () => {
|
|
54
|
+
const symm = new SymmetricCrypto();
|
|
55
|
+
const ciphertext = symm.encryptData(plaintext);
|
|
56
|
+
|
|
57
|
+
const [ivHex, encryptedHex] = ciphertext.split(':');
|
|
58
|
+
|
|
59
|
+
// Tamper with the encrypted content by cutting off the last character.
|
|
60
|
+
const tamperedEncryptedHex = encryptedHex.substring(0, encryptedHex.length - 1);
|
|
61
|
+
|
|
62
|
+
const tamperedCiphertext = `${ivHex}:${tamperedEncryptedHex}`;
|
|
63
|
+
|
|
64
|
+
// Expect the internal error handling to throw the generic error message
|
|
65
|
+
expect(() => symm.decryptData(tamperedCiphertext)).to.throw(
|
|
66
|
+
Error,
|
|
67
|
+
'Decryption failed. Check key, IV, or ciphertext integrity.',
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// --- AsymmetricCrypto Tests (RSA 2048) ---
|
|
73
|
+
describe('AsymmetricCrypto (RSA 2048)', () => {
|
|
74
|
+
/**
|
|
75
|
+
* Test case: Verify that RSA key pair is automatically generated.
|
|
76
|
+
*/
|
|
77
|
+
it('should generate a new RSA key pair if none are provided', () => {
|
|
78
|
+
const asymm = new AsymmetricCrypto();
|
|
79
|
+
// Public and Private keys should be PEM strings
|
|
80
|
+
expect(asymm.publicKey).to.be.a('string').and.include('BEGIN PUBLIC KEY');
|
|
81
|
+
expect(asymm.privateKey).to.be.a('string').and.include('BEGIN PRIVATE KEY');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Test case: Encrypt with public key and decrypt with the corresponding private key.
|
|
86
|
+
*/
|
|
87
|
+
it('should encrypt data with public key and decrypt with private key', () => {
|
|
88
|
+
const asymm = new AsymmetricCrypto();
|
|
89
|
+
const ciphertext = asymm.encryptData(plaintext);
|
|
90
|
+
|
|
91
|
+
// Ciphertext is a hex string
|
|
92
|
+
expect(ciphertext).to.be.a('string');
|
|
93
|
+
|
|
94
|
+
const decryptedText = asymm.decryptData(ciphertext);
|
|
95
|
+
expect(decryptedText).to.equal(plaintext);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Test case: Verify that decryption fails gracefully when using a mismatched private key.
|
|
100
|
+
*/
|
|
101
|
+
it('should fail decryption gracefully when using the wrong private key', () => {
|
|
102
|
+
// 1. Generate the key pair and encrypt the data
|
|
103
|
+
const asymm1 = new AsymmetricCrypto();
|
|
104
|
+
const ciphertext = asymm1.encryptData(plaintext);
|
|
105
|
+
|
|
106
|
+
// 2. Generate a completely different key pair (wrong key)
|
|
107
|
+
const asymm2 = new AsymmetricCrypto();
|
|
108
|
+
|
|
109
|
+
// 3. Try to decrypt ciphertext from asymm1 using the private key from asymm2
|
|
110
|
+
// The implementation will log the 'oaep decoding error' and re-throw the generic message.
|
|
111
|
+
expect(() => asymm2.decryptData(ciphertext)).to.throw(
|
|
112
|
+
Error,
|
|
113
|
+
'Decryption failed. Check private key or ciphertext integrity.',
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
});
|