@underpostnet/underpost 2.97.1 → 2.98.0

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.
Files changed (63) hide show
  1. package/README.md +2 -2
  2. package/cli.md +3 -1
  3. package/conf.js +2 -0
  4. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  5. package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
  6. package/package.json +1 -1
  7. package/scripts/rocky-pwa.sh +200 -0
  8. package/src/api/core/core.service.js +0 -5
  9. package/src/api/default/default.service.js +7 -5
  10. package/src/api/document/document.model.js +1 -1
  11. package/src/api/document/document.router.js +5 -0
  12. package/src/api/document/document.service.js +176 -128
  13. package/src/api/file/file.model.js +112 -4
  14. package/src/api/file/file.ref.json +42 -0
  15. package/src/api/file/file.service.js +380 -32
  16. package/src/api/user/user.model.js +38 -1
  17. package/src/api/user/user.router.js +96 -63
  18. package/src/api/user/user.service.js +81 -48
  19. package/src/cli/db.js +424 -166
  20. package/src/cli/index.js +8 -0
  21. package/src/cli/repository.js +1 -1
  22. package/src/cli/run.js +1 -0
  23. package/src/cli/ssh.js +10 -10
  24. package/src/client/components/core/Account.js +327 -36
  25. package/src/client/components/core/AgGrid.js +3 -0
  26. package/src/client/components/core/Auth.js +11 -3
  27. package/src/client/components/core/Chat.js +2 -2
  28. package/src/client/components/core/Content.js +161 -80
  29. package/src/client/components/core/Css.js +30 -0
  30. package/src/client/components/core/CssCore.js +16 -12
  31. package/src/client/components/core/FileExplorer.js +813 -49
  32. package/src/client/components/core/Input.js +207 -12
  33. package/src/client/components/core/LogIn.js +42 -20
  34. package/src/client/components/core/Modal.js +138 -24
  35. package/src/client/components/core/Panel.js +71 -32
  36. package/src/client/components/core/PanelForm.js +262 -77
  37. package/src/client/components/core/PublicProfile.js +888 -0
  38. package/src/client/components/core/Responsive.js +15 -7
  39. package/src/client/components/core/Router.js +117 -15
  40. package/src/client/components/core/SearchBox.js +322 -116
  41. package/src/client/components/core/SignUp.js +26 -7
  42. package/src/client/components/core/SocketIo.js +6 -3
  43. package/src/client/components/core/Translate.js +148 -0
  44. package/src/client/components/core/Validator.js +15 -0
  45. package/src/client/components/core/windowGetDimensions.js +6 -6
  46. package/src/client/components/default/MenuDefault.js +59 -12
  47. package/src/client/components/default/RoutesDefault.js +1 -0
  48. package/src/client/services/core/core.service.js +163 -1
  49. package/src/client/services/default/default.management.js +454 -76
  50. package/src/client/services/default/default.service.js +13 -6
  51. package/src/client/services/file/file.service.js +43 -16
  52. package/src/client/services/user/user.service.js +13 -9
  53. package/src/client/sw/default.sw.js +107 -184
  54. package/src/db/DataBaseProvider.js +1 -1
  55. package/src/db/mongo/MongooseDB.js +1 -1
  56. package/src/index.js +1 -1
  57. package/src/mailer/MailerProvider.js +4 -4
  58. package/src/runtime/express/Express.js +2 -1
  59. package/src/runtime/lampp/Lampp.js +2 -2
  60. package/src/server/auth.js +3 -6
  61. package/src/server/data-query.js +449 -0
  62. package/src/server/object-layer.js +0 -3
  63. package/src/ws/IoInterface.js +2 -2
@@ -0,0 +1,449 @@
1
+ /**
2
+ * Module for parsing data query parameters into Mongoose query options.
3
+ * Supports AG Grid filterModel/sortModel as well as simple legacy parameters.
4
+ * @module src/server/data-query.js
5
+ * @namespace DataQuery
6
+ */
7
+
8
+ export const DataQuery = {
9
+ /**
10
+ * Parse request query parameters into Mongoose query options
11
+ * @param {Object} params - The request query parameters (req.query)
12
+ * @param {string|Object} [params.filterModel] - AG Grid filterModel as JSON string or object
13
+ * @param {string|Object} [params.sortModel] - AG Grid sortModel as JSON string or object
14
+ * @param {number|string} [params.page=1] - Page number for pagination
15
+ * @param {number|string} [params.limit=10] - Items per page for pagination
16
+ * @param {string} [params.sort] - Simple sort field (legacy)
17
+ * @param {string|boolean} [params.asc] - Simple sort direction (legacy, '1'/'true' for asc)
18
+ * @param {string} [params.order] - Simple order string, e.g. "field1:asc,field2:desc" (legacy)
19
+ * @param {Object} [params.query] - Default query object to merge with filters
20
+ * @memberof DataQuery
21
+ * @returns {Object} { query, sort, skip, limit, page }
22
+ */
23
+ parse: (params = {}) => {
24
+ let { filterModel, sortModel, page, limit, sort: sortParam, asc, order, query: defaultQuery } = params;
25
+
26
+ // === 1. Pagination ===
27
+ page = parseInt(page, 10) || 1;
28
+ limit = parseInt(limit, 10) || 10;
29
+ const skip = (page - 1) * limit;
30
+
31
+ // === 2. Sorting ===
32
+ const sort = DataQuery._parseSort(sortModel, sortParam, asc, order);
33
+
34
+ // === 3. Filtering ===
35
+ const query = DataQuery._parseFilter(filterModel, defaultQuery);
36
+
37
+ return { query, sort, skip, limit, page };
38
+ },
39
+
40
+ /**
41
+ * Parse sort parameters from AG Grid sortModel or simple sort params
42
+ * @method
43
+ * @param {string|Object} sortModel - AG Grid sortModel as JSON string or object
44
+ * @param {string} sortParam - Simple sort field (legacy)
45
+ * @param {string|boolean} asc - Simple sort direction (legacy)
46
+ * @param {string} order - Simple order string (legacy)
47
+ * @return {Object} sort object for Mongoose
48
+ * @memberof DataQuery
49
+ */
50
+ _parseSort: (sortModel, sortParam, asc, order) => {
51
+ const sort = {};
52
+
53
+ // Parse sortModel from string if needed
54
+ if (typeof sortModel === 'string' && sortModel.trim()) {
55
+ try {
56
+ sortModel = JSON.parse(sortModel);
57
+ } catch (e) {
58
+ console.warn('DataQuery: Failed to parse sortModel JSON:', e.message);
59
+ sortModel = null;
60
+ }
61
+ }
62
+
63
+ // AG Grid sortModel format: [{ colId: 'field', sort: 'asc' | 'desc' }]
64
+ if (Array.isArray(sortModel) && sortModel.length > 0) {
65
+ sortModel.forEach((sortItem) => {
66
+ if (sortItem && sortItem.colId && sortItem.sort) {
67
+ sort[sortItem.colId] = sortItem.sort === 'asc' ? 1 : -1;
68
+ }
69
+ });
70
+ return sort;
71
+ }
72
+
73
+ // Simple sort params (legacy support)
74
+ if (sortParam && typeof sortParam === 'string') {
75
+ const direction = asc === '1' || asc === 'true' || asc === true ? 1 : -1;
76
+ sort[sortParam] = direction;
77
+ return sort;
78
+ }
79
+
80
+ // Order param format: "field1:asc,field2:desc"
81
+ if (order && typeof order === 'string') {
82
+ const orderParts = order.split(',');
83
+ orderParts.forEach((part) => {
84
+ const [field, dir] = part.split(':');
85
+ if (field && field.trim()) {
86
+ sort[field.trim()] = dir === 'desc' ? -1 : 1;
87
+ }
88
+ });
89
+ return sort;
90
+ }
91
+
92
+ return sort;
93
+ },
94
+
95
+ /**
96
+ * Parse filter parameters from AG Grid filterModel
97
+ * @method
98
+ * @param {string|Object} filterModel - AG Grid filterModel as JSON string or object
99
+ * @param {Object} defaultQuery - Default query object to merge with filters
100
+ * @return {Object} query object for Mongoose
101
+ * @memberof DataQuery
102
+ */
103
+ _parseFilter: (filterModel, defaultQuery) => {
104
+ let query = defaultQuery ? { ...defaultQuery } : {};
105
+
106
+ // Parse filterModel from string if needed
107
+ if (typeof filterModel === 'string' && filterModel.trim()) {
108
+ try {
109
+ filterModel = JSON.parse(filterModel);
110
+ } catch (e) {
111
+ console.warn('DataQuery: Failed to parse filterModel JSON:', e.message);
112
+ filterModel = null;
113
+ }
114
+ }
115
+
116
+ if (!filterModel || typeof filterModel !== 'object' || Array.isArray(filterModel)) {
117
+ return query;
118
+ }
119
+
120
+ // Process each filter in the filterModel
121
+ Object.entries(filterModel).forEach(([field, filter]) => {
122
+ if (!field || !filter) return;
123
+ const fieldQuery = DataQuery._parseFieldFilter(field, filter);
124
+ if (fieldQuery) {
125
+ query = { ...query, ...fieldQuery };
126
+ }
127
+ });
128
+
129
+ return query;
130
+ },
131
+
132
+ /**
133
+ * Parse a single field filter
134
+ * @method
135
+ * @param {string} field - The field name
136
+ * @param {Object} filter - The filter object
137
+ * @return {Object|null} query condition for the field or null if invalid
138
+ * @memberof DataQuery
139
+ */
140
+ _parseFieldFilter: (field, filter) => {
141
+ if (!filter || !filter.filterType) {
142
+ return null;
143
+ }
144
+
145
+ const { filterType } = filter;
146
+
147
+ switch (filterType) {
148
+ case 'text':
149
+ return DataQuery._parseTextFilter(field, filter);
150
+ case 'number':
151
+ return DataQuery._parseNumberFilter(field, filter);
152
+ case 'date':
153
+ return DataQuery._parseDateFilter(field, filter);
154
+ case 'set':
155
+ return DataQuery._parseSetFilter(field, filter);
156
+ case 'multi':
157
+ return DataQuery._parseMultiFilter(field, filter);
158
+ default:
159
+ return null;
160
+ }
161
+ },
162
+
163
+ /**
164
+ * Parse text filter
165
+ * @method
166
+ * @param {string} field - The field name
167
+ * @param {Object} filter - The filter object
168
+ * @return {Object|null} query condition for the text field or null if invalid
169
+ * @memberof DataQuery
170
+ */
171
+ _parseTextFilter: (field, filter) => {
172
+ const { type, filter: filterValue } = filter;
173
+
174
+ if (filterValue === null || filterValue === undefined || filterValue === '') {
175
+ // Handle blank/notBlank without a filter value
176
+ if (type === 'blank' || type === 'notBlank') {
177
+ const query = {};
178
+ if (type === 'blank') {
179
+ query[field] = { $in: [null, ''] };
180
+ } else {
181
+ query[field] = { $nin: [null, ''], $exists: true };
182
+ }
183
+ return query;
184
+ }
185
+ return null;
186
+ }
187
+
188
+ const query = {};
189
+ // Escape special regex characters for safety
190
+ const escapeRegex = (str) => String(str).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
191
+
192
+ switch (type) {
193
+ case 'equals':
194
+ query[field] = filterValue;
195
+ break;
196
+ case 'notEqual':
197
+ query[field] = { $ne: filterValue };
198
+ break;
199
+ case 'contains':
200
+ query[field] = { $regex: escapeRegex(filterValue), $options: 'i' };
201
+ break;
202
+ case 'notContains':
203
+ query[field] = { $not: { $regex: escapeRegex(filterValue), $options: 'i' } };
204
+ break;
205
+ case 'startsWith':
206
+ query[field] = { $regex: `^${escapeRegex(filterValue)}`, $options: 'i' };
207
+ break;
208
+ case 'endsWith':
209
+ query[field] = { $regex: `${escapeRegex(filterValue)}$`, $options: 'i' };
210
+ break;
211
+ case 'blank':
212
+ query[field] = { $in: [null, ''] };
213
+ break;
214
+ case 'notBlank':
215
+ query[field] = { $nin: [null, ''], $exists: true };
216
+ break;
217
+ default:
218
+ query[field] = { $regex: escapeRegex(filterValue), $options: 'i' };
219
+ }
220
+
221
+ return query;
222
+ },
223
+
224
+ /**
225
+ * Parse number filter
226
+ * @method
227
+ * @param {string} field - The field name
228
+ * @param {Object} filter - The filter object
229
+ * @return {Object|null} query condition for the number field or null if invalid
230
+ * @memberof DataQuery
231
+ */
232
+ _parseNumberFilter: (field, filter) => {
233
+ const { type, filter: filterValue, filterTo } = filter;
234
+
235
+ if (filterValue === null || filterValue === undefined) {
236
+ return null;
237
+ }
238
+
239
+ const query = {};
240
+ const numValue = parseFloat(filterValue);
241
+
242
+ if (isNaN(numValue)) {
243
+ return null;
244
+ }
245
+
246
+ switch (type) {
247
+ case 'equals':
248
+ query[field] = numValue;
249
+ break;
250
+ case 'notEqual':
251
+ query[field] = { $ne: numValue };
252
+ break;
253
+ case 'lessThan':
254
+ query[field] = { $lt: numValue };
255
+ break;
256
+ case 'lessThanOrEqual':
257
+ query[field] = { $lte: numValue };
258
+ break;
259
+ case 'greaterThan':
260
+ query[field] = { $gt: numValue };
261
+ break;
262
+ case 'greaterThanOrEqual':
263
+ query[field] = { $gte: numValue };
264
+ break;
265
+ case 'inRange':
266
+ if (filterTo !== null && filterTo !== undefined) {
267
+ const numTo = parseFloat(filterTo);
268
+ if (!isNaN(numTo)) {
269
+ query[field] = { $gte: numValue, $lte: numTo };
270
+ }
271
+ }
272
+ break;
273
+ case 'blank':
274
+ query[field] = { $in: [null, undefined] };
275
+ break;
276
+ case 'notBlank':
277
+ query[field] = { $nin: [null, undefined], $exists: true };
278
+ break;
279
+ default:
280
+ query[field] = numValue;
281
+ }
282
+
283
+ return query;
284
+ },
285
+
286
+ /**
287
+ * Parse date filter
288
+ * @method
289
+ * @param {string} field - The field name
290
+ * @param {Object} filter - The filter object
291
+ * @return {Object|null} query condition for the date field or null if invalid
292
+ * @memberof DataQuery
293
+ */
294
+ _parseDateFilter: (field, filter) => {
295
+ const { type, dateFrom, dateTo } = filter;
296
+
297
+ // Handle blank/notBlank without dates
298
+ if (type === 'blank' || type === 'notBlank') {
299
+ const query = {};
300
+ if (type === 'blank') {
301
+ query[field] = { $in: [null, undefined] };
302
+ } else {
303
+ query[field] = { $nin: [null, undefined], $exists: true };
304
+ }
305
+ return query;
306
+ }
307
+
308
+ if (!dateFrom && !dateTo) {
309
+ return null;
310
+ }
311
+
312
+ const query = {};
313
+
314
+ const parseDate = (dateStr) => {
315
+ if (!dateStr) return null;
316
+ const date = new Date(dateStr);
317
+ return isNaN(date.getTime()) ? null : date;
318
+ };
319
+
320
+ const fromDate = parseDate(dateFrom);
321
+ const toDate = parseDate(dateTo);
322
+
323
+ if (!fromDate && !toDate) {
324
+ return null;
325
+ }
326
+
327
+ switch (type) {
328
+ case 'equals':
329
+ if (fromDate) {
330
+ // Match the entire day
331
+ const startOfDay = new Date(fromDate);
332
+ startOfDay.setHours(0, 0, 0, 0);
333
+ const endOfDay = new Date(fromDate);
334
+ endOfDay.setHours(23, 59, 59, 999);
335
+ query[field] = { $gte: startOfDay, $lte: endOfDay };
336
+ }
337
+ break;
338
+ case 'notEqual':
339
+ if (fromDate) {
340
+ const startOfDay = new Date(fromDate);
341
+ startOfDay.setHours(0, 0, 0, 0);
342
+ const endOfDay = new Date(fromDate);
343
+ endOfDay.setHours(23, 59, 59, 999);
344
+ query[field] = { $not: { $gte: startOfDay, $lte: endOfDay } };
345
+ }
346
+ break;
347
+ case 'lessThan':
348
+ if (fromDate) {
349
+ query[field] = { $lt: fromDate };
350
+ }
351
+ break;
352
+ case 'lessThanOrEqual':
353
+ if (fromDate) {
354
+ query[field] = { $lte: fromDate };
355
+ }
356
+ break;
357
+ case 'greaterThan':
358
+ if (fromDate) {
359
+ query[field] = { $gt: fromDate };
360
+ }
361
+ break;
362
+ case 'greaterThanOrEqual':
363
+ if (fromDate) {
364
+ query[field] = { $gte: fromDate };
365
+ }
366
+ break;
367
+ case 'inRange':
368
+ if (fromDate && toDate) {
369
+ // For inRange, set toDate to end of day
370
+ const endOfToDate = new Date(toDate);
371
+ endOfToDate.setHours(23, 59, 59, 999);
372
+ query[field] = { $gte: fromDate, $lte: endOfToDate };
373
+ } else if (fromDate) {
374
+ query[field] = { $gte: fromDate };
375
+ } else if (toDate) {
376
+ const endOfToDate = new Date(toDate);
377
+ endOfToDate.setHours(23, 59, 59, 999);
378
+ query[field] = { $lte: endOfToDate };
379
+ }
380
+ break;
381
+ case 'blank':
382
+ query[field] = { $in: [null, undefined] };
383
+ break;
384
+ case 'notBlank':
385
+ query[field] = { $nin: [null, undefined], $exists: true };
386
+ break;
387
+ default:
388
+ if (fromDate) {
389
+ query[field] = fromDate;
390
+ }
391
+ }
392
+
393
+ return query;
394
+ },
395
+
396
+ /**
397
+ * Parse set filter
398
+ * @method
399
+ * @param {string} field - The field name
400
+ * @param {Object} filter - The filter object
401
+ * @return {Object|null} query condition for the set field or null if invalid
402
+ * @memberof DataQuery
403
+ */
404
+ _parseSetFilter: (field, filter) => {
405
+ const { values } = filter;
406
+
407
+ if (!Array.isArray(values) || values.length === 0) {
408
+ return null;
409
+ }
410
+
411
+ return { [field]: { $in: values } };
412
+ },
413
+
414
+ /**
415
+ * Parse multi filter (combines multiple filters with AND/OR)
416
+ * @method
417
+ * @param {string} field - The field name
418
+ * @param {Object} filter - The multi filter object
419
+ * @return {Object|null} query condition for the multi filter or null if invalid
420
+ * @memberof DataQuery
421
+ */
422
+ _parseMultiFilter: (field, filter) => {
423
+ const { filterModels, operator } = filter;
424
+
425
+ if (!Array.isArray(filterModels) || filterModels.length === 0) {
426
+ return null;
427
+ }
428
+
429
+ const conditions = filterModels
430
+ .map((subFilter) => DataQuery._parseFieldFilter(field, subFilter))
431
+ .filter((condition) => condition !== null);
432
+
433
+ if (conditions.length === 0) {
434
+ return null;
435
+ }
436
+
437
+ if (conditions.length === 1) {
438
+ return conditions[0];
439
+ }
440
+
441
+ // Combine conditions with AND or OR
442
+ if (operator === 'OR') {
443
+ return { $or: conditions };
444
+ } else {
445
+ // AND operator (default)
446
+ return { $and: conditions };
447
+ }
448
+ },
449
+ };
@@ -4,7 +4,6 @@
4
4
  * @namespace CyberiaObjectLayer
5
5
  */
6
6
 
7
- import dotenv from 'dotenv';
8
7
  import fs from 'fs-extra';
9
8
  import { PNG } from 'pngjs';
10
9
  import sharp from 'sharp';
@@ -16,8 +15,6 @@ import { loggerFactory } from '../server/logger.js';
16
15
 
17
16
  const logger = loggerFactory(import.meta);
18
17
 
19
- dotenv.config({ path: `./engine-private/conf/dd-cyberia/.env.production`, override: true });
20
-
21
18
  /**
22
19
  * @typedef {Object} ObjectLayerCallbackPayload
23
20
  * @property {string} path - The full file path to the image.
@@ -28,7 +28,7 @@ const logger = loggerFactory(import.meta);
28
28
  */
29
29
  class IoChannel {
30
30
  /**
31
- * @private
31
+ * @method
32
32
  * @type {ChannelInterface}
33
33
  */
34
34
  #IoInterface;
@@ -86,7 +86,7 @@ class IoChannel {
86
86
  /**
87
87
  * Handles incoming messages on the channel.
88
88
  *
89
- * @private
89
+ * @method
90
90
  * @param {Socket} socket - The Socket.IO socket object.
91
91
  * @param {any[]} args - The raw arguments received from the socket event.
92
92
  * @param {string} wsManagementId - Unique identifier for the WebSocket management context.