data-primals-engine 1.1.4 → 1.1.6
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/node.js.yml +3 -1
- package/README.md +9 -1
- package/client/package-lock.json +11580 -11664
- package/client/package.json +1 -2
- package/client/src/App.jsx +13 -8
- package/client/src/ConditionBuilder.scss +24 -0
- package/client/src/ConditionBuilder2.jsx +56 -0
- package/client/src/DataEditor.jsx +4 -2
- package/client/src/DataLayout.jsx +20 -11
- package/client/src/DataTable.jsx +77 -10
- package/client/src/Dialog.scss +1 -0
- package/client/src/Field.jsx +3 -4
- package/client/src/FlexBuilder.jsx +3 -1
- package/client/src/TourSpotlight.jsx +14 -10
- package/client/src/TutorialsMenu.scss +0 -1
- package/client/src/constants.js +1 -1
- package/client/src/contexts/ModelContext.jsx +3 -8
- package/client/src/core/dom.js +26 -0
- package/client/src/filter.js +40 -1
- package/package.json +4 -5
- package/server.js +3 -2
- package/src/constants.js +15 -1
- package/src/core.js +52 -3
- package/src/engine.js +42 -16
- package/src/i18n.js +75 -2
- package/src/migrate.js +2 -3
- package/src/modules/data.js +15 -4
- package/src/modules/mongodb.js +0 -9
- package/src/packs.js +4 -3
- package/src/migrations/20240522143000-content.js +0 -20
package/client/src/filter.js
CHANGED
|
@@ -7,6 +7,12 @@ export const MONGO_CALC_OPERATORS = {
|
|
|
7
7
|
multi: false,
|
|
8
8
|
isFindOperator: true // Nouvelle propriété pour identifier les opérateurs $find
|
|
9
9
|
},
|
|
10
|
+
'$regexMatch': {
|
|
11
|
+
label: 'Find by regex',
|
|
12
|
+
description: 'Find a string matching a regular expression (Ecmascript)',
|
|
13
|
+
args: 2, // input et regex
|
|
14
|
+
specialStructure: true // Indique une structure spéciale
|
|
15
|
+
},
|
|
10
16
|
$and: { label: 'Et (and)', description: 'Renvoie vrai si toutes les conditions sont vérifiées', args: 1, multi: true },
|
|
11
17
|
$or: { label: 'Ou (or)', description: 'Renvoie vrai si au moins une des conditions est vérifiée.', args: 1, multi: true },
|
|
12
18
|
$not: { label: 'Non (not)', description: 'Inverse la condition (ex: non égal à).', args: 1, multi: true },
|
|
@@ -57,7 +63,7 @@ export const MONGO_CALC_OPERATORS = {
|
|
|
57
63
|
$toBool: { label: 'toBool', multi: false, converter: true },
|
|
58
64
|
$toString: { label: 'toString', multi: false, converter: true },
|
|
59
65
|
$toInt: { label: 'toInt', multi: false, converter: true },
|
|
60
|
-
$toDouble: { label: 'toDouble', multi: false, converter: true }
|
|
66
|
+
$toDouble: { label: 'toDouble', multi: false, converter: true }
|
|
61
67
|
};
|
|
62
68
|
|
|
63
69
|
export const convertInputValue = (value) => {
|
|
@@ -219,4 +225,37 @@ export const buildRootFromExpr = (query) => {
|
|
|
219
225
|
if (parsedRoot.$and || parsedRoot.$or) return parsedRoot;
|
|
220
226
|
|
|
221
227
|
return { $and: [parsedRoot] };
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
export const pagedFilterToMongoConds = (pagedFilters, model) => {
|
|
231
|
+
const modelFilter = pagedFilters?.[model.name] || {};
|
|
232
|
+
const filterKeys = Object.keys(modelFilter);
|
|
233
|
+
|
|
234
|
+
// Si le filtre est vide, on retourne un tableau vide.
|
|
235
|
+
if (filterKeys.length === 0) {
|
|
236
|
+
return [];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Détecte si c'est un filtre avancé (contient des opérateurs logiques de haut niveau comme $and, $or, etc.)
|
|
240
|
+
const isAdvancedFilter = filterKeys.some(key => key.startsWith('$'));
|
|
241
|
+
|
|
242
|
+
if (isAdvancedFilter) {
|
|
243
|
+
// C'est un filtre avancé du ConditionBuilder.
|
|
244
|
+
// Il est déjà une condition MongoDB complète. On le met dans un tableau pour l'insérer dans le $and global.
|
|
245
|
+
// Si le filtre avancé est vide (ex: { $and: [] }), on le retourne quand même pour que le ConditionBuilder puisse s'initialiser correctement.
|
|
246
|
+
return [modelFilter];
|
|
247
|
+
} else {
|
|
248
|
+
// C'est un filtre simple (par colonne).
|
|
249
|
+
// On transforme { champ1: cond1, champ2: cond2 } en [{ champ1: cond1 }, { champ2: cond2 }]
|
|
250
|
+
const c = [];
|
|
251
|
+
filterKeys.forEach(fieldName => {
|
|
252
|
+
if (model.fields.some(f => f.name === fieldName)) {
|
|
253
|
+
const condition = modelFilter[fieldName];
|
|
254
|
+
if (condition && typeof condition === 'object' && Object.keys(condition).length > 0) {
|
|
255
|
+
c.push(condition);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
return c;
|
|
260
|
+
}
|
|
222
261
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "data-primals-engine",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.6",
|
|
4
4
|
"description": "data-primals-engine is the package responsible from handling large amount of data in a practical and performant way. It can handle large amount of data in a practical and performant way. It can also get workflow models working (for automation), and fully supports internationalisation.",
|
|
5
5
|
"main": "src/engine.js",
|
|
6
6
|
"type": "module",
|
|
@@ -44,7 +44,8 @@
|
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"react": ">=18.0.0",
|
|
47
|
-
"react-query": ">=3.0.0"
|
|
47
|
+
"react-query": ">=3.0.0",
|
|
48
|
+
"express": "^5.1.0"
|
|
48
49
|
},
|
|
49
50
|
"dependencies": {
|
|
50
51
|
"@langchain/core": "^0.3.66",
|
|
@@ -60,15 +61,13 @@
|
|
|
60
61
|
"cookie-parser": "^1.4.7",
|
|
61
62
|
"cronstrue": "^3.2.0",
|
|
62
63
|
"csv-parse": "^6.1.0",
|
|
63
|
-
"data-primals-engine": "^1.0.
|
|
64
|
+
"data-primals-engine": "^1.0.14",
|
|
64
65
|
"date-fns": "^4.1.0",
|
|
65
|
-
"express": "^5.1.0",
|
|
66
66
|
"express-csrf-double-submit-cookie": "^2.0.0",
|
|
67
67
|
"express-formidable": "^1.2.0",
|
|
68
68
|
"express-mongo-sanitize": "^2.2.0",
|
|
69
69
|
"express-rate-limit": "^8.0.1",
|
|
70
70
|
"express-session": "^1.18.2",
|
|
71
|
-
"i18next": "^25.3.2",
|
|
72
71
|
"i18next-browser-languagedetector": "^8.2.0",
|
|
73
72
|
"juice": "^11.0.1",
|
|
74
73
|
"mathjs": "^14.6.0",
|
package/server.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import process from "node:process";
|
|
7
7
|
import {Config, Engine, BenchmarkTool, GameObject, Logger} from "./src/index.js";
|
|
8
8
|
import sirv from "sirv";
|
|
9
|
+
import express from "express";
|
|
9
10
|
|
|
10
11
|
Config.Set("modules", ["mongodb", "data", "file", "bucket", "workflow","user", "assistant", "swagger"])
|
|
11
12
|
Config.Set("middlewares", []);
|
|
@@ -14,8 +15,8 @@ const bench = GameObject.Create("Benchmark");
|
|
|
14
15
|
const timer = bench.addComponent(BenchmarkTool);
|
|
15
16
|
timer.start();
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
const engine = await Engine.Create();
|
|
18
|
+
const app = express();
|
|
19
|
+
const engine = await Engine.Create({app});
|
|
19
20
|
|
|
20
21
|
if (process.argv.length === 3) {
|
|
21
22
|
let arg = process.argv[2];
|
package/src/constants.js
CHANGED
|
@@ -64,6 +64,8 @@ export const emailDefaultConfig = {
|
|
|
64
64
|
pass: 'password'
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
export const useAI = true;
|
|
68
|
+
|
|
67
69
|
/**
|
|
68
70
|
* Maximum number of models per user
|
|
69
71
|
* @type {number}
|
|
@@ -212,7 +214,19 @@ export const maxTotalPrivateFilesSize = 250 * megabytes;
|
|
|
212
214
|
*/
|
|
213
215
|
export const timeoutVM = 5000;
|
|
214
216
|
|
|
215
|
-
|
|
217
|
+
/**
|
|
218
|
+
* Default maximum number of data per request
|
|
219
|
+
* @type {number}
|
|
220
|
+
*/
|
|
221
|
+
export const defaultMaxRequestData = 500;
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Database connections pool size for MongoDB access
|
|
225
|
+
* @type {number}
|
|
226
|
+
*/
|
|
227
|
+
export const databasePoolSize = 30;
|
|
228
|
+
|
|
229
|
+
|
|
216
230
|
/**
|
|
217
231
|
* Options for the HTML sanitizer
|
|
218
232
|
* @type {{allowedSchemesByTag: {}, selfClosing: string[], allowedSchemes: string[], enforceHtmlBoundary: boolean, disallowedTagsMode: string, allowProtocolRelative: boolean, allowedAttributes: {a: string[], img: string[], code: string[]}, allowedTags: string[], allowedSchemesAppliedToAttributes: string[]}}
|
package/src/core.js
CHANGED
|
@@ -11,6 +11,11 @@ export function escapeRegex(string) {
|
|
|
11
11
|
|
|
12
12
|
export const isDate = dt => String(new Date(dt)) !== 'Invalid Date'
|
|
13
13
|
|
|
14
|
+
export const safeAssignObject = (obj, key, value) => {
|
|
15
|
+
if( !["__proto__", "constructor"].includes(key)){
|
|
16
|
+
obj[key] = value;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
14
19
|
export function debounce(callback, delay=300){
|
|
15
20
|
var timer;
|
|
16
21
|
return function(){
|
|
@@ -176,9 +181,53 @@ function splitmix32(a) {
|
|
|
176
181
|
|
|
177
182
|
export const getRand = () =>splitmix32(seed);
|
|
178
183
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
184
|
+
const MAX_RANGE_SIZE = 2n ** 64n
|
|
185
|
+
const buffer = new BigUint64Array(512)
|
|
186
|
+
let offset = buffer.length
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Returns a cryptographically secure random integer between min and max, inclusive.
|
|
190
|
+
*
|
|
191
|
+
* @param {number} min - the lowest integer in the desired range (inclusive)
|
|
192
|
+
* @param {number} max - the highest integer in the desired range (inclusive)
|
|
193
|
+
* @returns {number} Random number
|
|
194
|
+
*/
|
|
195
|
+
|
|
196
|
+
export function getRandom(min, max) {
|
|
197
|
+
if (!(Number.isSafeInteger(min) && Number.isSafeInteger(max))) {
|
|
198
|
+
throw Error("min and max must be safe integers")
|
|
199
|
+
}
|
|
200
|
+
if (min > max) {
|
|
201
|
+
throw Error("min must be less than or equal to max")
|
|
202
|
+
}
|
|
203
|
+
const bmin = BigInt(min)
|
|
204
|
+
const rangeSize = BigInt(max) - bmin + 1n
|
|
205
|
+
const rejectionThreshold = MAX_RANGE_SIZE - (MAX_RANGE_SIZE % rangeSize)
|
|
206
|
+
let result;
|
|
207
|
+
do {
|
|
208
|
+
if (offset >= buffer.length) {
|
|
209
|
+
crypto.getRandomValues(buffer)
|
|
210
|
+
offset = 0
|
|
211
|
+
}
|
|
212
|
+
result = buffer[offset++]
|
|
213
|
+
} while (result >= rejectionThreshold)
|
|
214
|
+
return Number(bmin + result % rangeSize)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Returns a cryptographically secure random integer between min and max, inclusive.
|
|
219
|
+
*
|
|
220
|
+
* @param {number} minInclusive - the lowest integer in the desired range (inclusive)
|
|
221
|
+
* @param {number} maxInclusive - the highest integer in the desired range (inclusive)
|
|
222
|
+
* @returns {number} Random number
|
|
223
|
+
*/
|
|
224
|
+
|
|
225
|
+
export function getBrowserRandom(minInclusive, maxInclusive) {
|
|
226
|
+
const randomBuffer = new Uint32Array(1);
|
|
227
|
+
(window.crypto || window.msCrypto).getRandomValues(randomBuffer);
|
|
228
|
+
const r = ( randomBuffer[0] / (0xffffffff + 1) );
|
|
229
|
+
return Math.floor(r * (maxInclusive - minInclusive + 1) + minInclusive);
|
|
230
|
+
}
|
|
182
231
|
|
|
183
232
|
export function randomDate(start, end) {
|
|
184
233
|
return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime()));
|
package/src/engine.js
CHANGED
|
@@ -4,7 +4,7 @@ import fs from 'node:fs'
|
|
|
4
4
|
import express from 'express'
|
|
5
5
|
import {MongoClient as InternalMongoClient} from 'mongodb'
|
|
6
6
|
import process from "process";
|
|
7
|
-
import {cookiesSecret, dbName} from "./constants.js";
|
|
7
|
+
import {cookiesSecret, databasePoolSize, dbName} from "./constants.js";
|
|
8
8
|
import http from "http";
|
|
9
9
|
import cookieParser from "cookie-parser";
|
|
10
10
|
import requestIp from 'request-ip';
|
|
@@ -13,20 +13,41 @@ import {defaultModels} from "./defaultModels.js";
|
|
|
13
13
|
import {DefaultUserProvider} from "./providers.js";
|
|
14
14
|
import formidableMiddleware from 'express-formidable';
|
|
15
15
|
import sirv from "sirv";
|
|
16
|
+
import * as tls from "node:tls";
|
|
16
17
|
|
|
17
18
|
// Constants
|
|
18
19
|
const isProduction = process.env.NODE_ENV === 'production'
|
|
19
20
|
|
|
21
|
+
let caFile, certFile, keyFile;
|
|
22
|
+
try {
|
|
23
|
+
if (process.env.CA_CERT)
|
|
24
|
+
caFile = fs.readFileSync(process.env.CA_CERT || './ca');
|
|
25
|
+
} catch (e) {}
|
|
26
|
+
try {
|
|
27
|
+
if (process.env.CERT)
|
|
28
|
+
certFile = fs.readFileSync(process.env.CERT || '');
|
|
29
|
+
}catch (e) {}
|
|
30
|
+
try{
|
|
31
|
+
if (process.env.CERT_KEY)
|
|
32
|
+
keyFile = fs.readFileSync(process.env.CERT_KEY || './k');
|
|
33
|
+
} catch (e) {}
|
|
34
|
+
|
|
35
|
+
const secureContext = tls.createSecureContext({
|
|
36
|
+
ca: caFile, cert: certFile, key: keyFile
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const isTlsActive = !(!process.env.TLS || ["0", "false"].includes(process.env.TLS.toLowerCase()));
|
|
40
|
+
|
|
20
41
|
// Connection URL
|
|
21
42
|
export const dbUrl = process.env.CI ? 'mongodb://mongodb:27017' : (process.env.MONGO_DB_URL || 'mongodb://127.0.0.1:27017');
|
|
22
|
-
export const MongoClient = new InternalMongoClient(dbUrl, { maxPoolSize:
|
|
43
|
+
export const MongoClient = new InternalMongoClient(dbUrl, { maxPoolSize: databasePoolSize, tls: isTlsActive, secureContext });
|
|
23
44
|
|
|
24
45
|
// Database Name
|
|
25
46
|
export const MongoDatabase = MongoClient.db(dbName);
|
|
26
47
|
|
|
27
48
|
|
|
28
49
|
export const Engine = {
|
|
29
|
-
Create: async (options) => {
|
|
50
|
+
Create: async (options = { app : null}) => {
|
|
30
51
|
const engine = GameObject.Create("Engine");
|
|
31
52
|
console.log("Creating engine", Config.Get('modules'));
|
|
32
53
|
engine.addComponent(Logger);
|
|
@@ -38,8 +59,11 @@ export const Engine = {
|
|
|
38
59
|
engine.getComponent(Logger).info(`Custom UserProvider '${providerInstance.constructor.name}' has been set.`);
|
|
39
60
|
};
|
|
40
61
|
|
|
62
|
+
if (!options.app) {
|
|
63
|
+
options.app = express();
|
|
64
|
+
}
|
|
41
65
|
|
|
42
|
-
const app =
|
|
66
|
+
const { app } = options;
|
|
43
67
|
// Allows you to set port in the project properties.
|
|
44
68
|
app.set('port', process.env.PORT || 3000);
|
|
45
69
|
app.set('engine', engine);
|
|
@@ -106,12 +130,8 @@ export const Engine = {
|
|
|
106
130
|
engine._modules = e;
|
|
107
131
|
return Promise.resolve();
|
|
108
132
|
});
|
|
109
|
-
let server;
|
|
110
133
|
|
|
111
|
-
|
|
112
|
-
single: true,
|
|
113
|
-
dev: process.env.NODE_ENV === 'development'
|
|
114
|
-
}));
|
|
134
|
+
let server;
|
|
115
135
|
|
|
116
136
|
engine.start = async (port, cb) =>{
|
|
117
137
|
// Use connect method to connect to the server
|
|
@@ -132,6 +152,18 @@ export const Engine = {
|
|
|
132
152
|
if (cb)
|
|
133
153
|
await cb();
|
|
134
154
|
|
|
155
|
+
engine.get('/api/health', (req, res) => {
|
|
156
|
+
res.status(200).json({
|
|
157
|
+
status: 'ok',
|
|
158
|
+
timestamp: new Date().toISOString()
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
app.use(sirv('client/dist', {
|
|
163
|
+
single: true,
|
|
164
|
+
dev: process.env.NODE_ENV === 'development'
|
|
165
|
+
}));
|
|
166
|
+
|
|
135
167
|
process.on('uncaughtException', function (exception) {
|
|
136
168
|
console.error(exception);
|
|
137
169
|
fs.appendFile('issues.txt', JSON.stringify({ code: exception.code, message: exception.message, stack: exception.stack }), function (err) {
|
|
@@ -139,7 +171,7 @@ export const Engine = {
|
|
|
139
171
|
throw err;
|
|
140
172
|
}
|
|
141
173
|
});
|
|
142
|
-
process.exit(
|
|
174
|
+
process.exit(1);
|
|
143
175
|
});
|
|
144
176
|
}
|
|
145
177
|
|
|
@@ -171,12 +203,6 @@ export const Engine = {
|
|
|
171
203
|
engine.resetModels = async () => {
|
|
172
204
|
await deleteModels();
|
|
173
205
|
};
|
|
174
|
-
engine.get('/api/health', (req, res) => {
|
|
175
|
-
res.status(200).json({
|
|
176
|
-
status: 'ok',
|
|
177
|
-
timestamp: new Date().toISOString()
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
206
|
return engine;
|
|
181
207
|
}
|
|
182
208
|
}
|
package/src/i18n.js
CHANGED
|
@@ -5,7 +5,13 @@ import LanguageDetector from "i18next-browser-languagedetector";
|
|
|
5
5
|
export const translations = {
|
|
6
6
|
fr: {
|
|
7
7
|
translation: {
|
|
8
|
+
"datatable.advancedFilter.title": "Filtre avancé (MongoDB)",
|
|
9
|
+
"datatable.advancedFilter.desc": "Créez de toute pièce vos filtres avancés, avec les opérateurs d'aggrégation de MongoDB disponibles ici.",
|
|
8
10
|
|
|
11
|
+
"editData": "Éditer dans {{0}}",
|
|
12
|
+
"btns.duplicate": "Dupliquer",
|
|
13
|
+
"btns.edit": "Modifier",
|
|
14
|
+
"btns.delete": "Supprimer",
|
|
9
15
|
"field_return_status_hint": "Le statut actuel de la demande de retour (ex: Demandé, Approuvé, Remboursé).",
|
|
10
16
|
"field_workflowRun_completedAt_hint": "Horodatage de la fin de l'exécution (réussie ou échouée).",
|
|
11
17
|
"field_employee_socialSecurityNumber_hint": "Numéro de sécurité sociale de l'employé.",
|
|
@@ -1235,6 +1241,13 @@ export const translations = {
|
|
|
1235
1241
|
en: {
|
|
1236
1242
|
translation: {
|
|
1237
1243
|
|
|
1244
|
+
"datatable.advancedFilter.title": "Advanced Filter (MongoDB)",
|
|
1245
|
+
"datatable.advancedFilter.desc": "Create your own advanced filters from scratch, using MongoDB's aggregation operators available here.",
|
|
1246
|
+
|
|
1247
|
+
"editData": "Edit in {{0}}",
|
|
1248
|
+
"btns.duplicate": "Duplicate",
|
|
1249
|
+
"btns.edit": "Edit",
|
|
1250
|
+
"btns.delete": "Delete",
|
|
1238
1251
|
"field_workflowRun_completedAt_hint": "Timestamp of when the execution ended (successfully or failed).",
|
|
1239
1252
|
"field_employee_socialSecurityNumber_hint": "Employee's social security number.",
|
|
1240
1253
|
|
|
@@ -2462,6 +2475,13 @@ export const translations = {
|
|
|
2462
2475
|
},
|
|
2463
2476
|
es: {
|
|
2464
2477
|
translation: {
|
|
2478
|
+
"datatable.advancedFilter.title": "Filtro avanzado (MongoDB)",
|
|
2479
|
+
"datatable.advancedFilter.desc": "Cree sus propios filtros avanzados desde cero, utilizando los operadores de agregación de MongoDB disponibles aquí.",
|
|
2480
|
+
|
|
2481
|
+
"editData": "Editar en {{0}}",
|
|
2482
|
+
"btns.duplicate": "Duplicar",
|
|
2483
|
+
"btns.edit": "Editar",
|
|
2484
|
+
"btns.delete": "Eliminar",
|
|
2465
2485
|
"field_return_status_hint": "El estado actual de la solicitud de devolución (ej: Solicitado, Aprobado, Reembolsado).",
|
|
2466
2486
|
"field_workflowRun_completedAt_hint": "Marca de tiempo del final de la ejecución (exitosa o fallida).",
|
|
2467
2487
|
"field_employee_socialSecurityNumber_hint": "Número de seguridad social del empleado.",
|
|
@@ -3724,6 +3744,13 @@ export const translations = {
|
|
|
3724
3744
|
},
|
|
3725
3745
|
pt: {
|
|
3726
3746
|
translation: {
|
|
3747
|
+
"datatable.advancedFilter.title": "Filtro Avançado (MongoDB)",
|
|
3748
|
+
"datatable.advancedFilter.desc": "Crie os seus próprios filtros avançados a partir do zero, utilizando os operadores de agregação do MongoDB disponíveis aqui.",
|
|
3749
|
+
|
|
3750
|
+
"editData": "Editar em {{0}}",
|
|
3751
|
+
"btns.duplicate": "Duplicar",
|
|
3752
|
+
"btns.edit": "Editar",
|
|
3753
|
+
"btns.delete": "Apagar",
|
|
3727
3754
|
"assistant.type": "Digite sua mensagem...",
|
|
3728
3755
|
"assistant.welcome": "Olá! Como posso ajudá-lo com seus dados?",
|
|
3729
3756
|
"assistant.named": "Assistente {{named}}",
|
|
@@ -3789,6 +3816,12 @@ export const translations = {
|
|
|
3789
3816
|
},
|
|
3790
3817
|
de: {
|
|
3791
3818
|
translation: {
|
|
3819
|
+
"datatable.advancedFilter.title": "Erweiterter Filter (MongoDB)",
|
|
3820
|
+
"datatable.advancedFilter.desc": "Erstellen Sie Ihre eigenen erweiterten Filter von Grund auf mit den hier verfügbaren Aggregationsoperatoren von MongoDB.",
|
|
3821
|
+
"editData": "Bearbeiten in {{0}}",
|
|
3822
|
+
"btns.duplicate": "Duplizieren",
|
|
3823
|
+
"btns.edit": "Bearbeiten",
|
|
3824
|
+
"btns.delete": "Löschen",
|
|
3792
3825
|
"field_workflowRun_completedAt_hint": "Zeitstempel des Abschlusses (erfolgreich oder fehlgeschlagen).",
|
|
3793
3826
|
"field_employee_socialSecurityNumber_hint": "Sozialversicherungsnummer des Mitarbeiters.",
|
|
3794
3827
|
"field_shipment_order_hint": "Die mit dieser Sendung verknüpfte Bestellung.",
|
|
@@ -4931,6 +4964,12 @@ export const translations = {
|
|
|
4931
4964
|
},
|
|
4932
4965
|
it: {
|
|
4933
4966
|
translation: {
|
|
4967
|
+
"datatable.advancedFilter.title": "Filtro avanzato (MongoDB)",
|
|
4968
|
+
"datatable.advancedFilter.desc": "Crea i tuoi filtri avanzati da zero, utilizzando gli operatori di aggregazione di MongoDB disponibili qui.",
|
|
4969
|
+
"editData": "Modifica in {{0}}",
|
|
4970
|
+
"btns.duplicate": "Duplica",
|
|
4971
|
+
"btns.edit": "Modifica",
|
|
4972
|
+
"btns.delete": "Elimina",
|
|
4934
4973
|
"field_workflowRun_completedAt_hint": "Timestamp del termine dell'esecuzione (successo o fallimento).",
|
|
4935
4974
|
"field_employee_socialSecurityNumber_hint": "Numero di previdenza sociale del dipendente.",
|
|
4936
4975
|
"field_shipment_order_hint": "L'ordine associato a questa spedizione.",
|
|
@@ -6126,6 +6165,12 @@ export const translations = {
|
|
|
6126
6165
|
},
|
|
6127
6166
|
cs: {
|
|
6128
6167
|
translation: {
|
|
6168
|
+
"datatable.advancedFilter.title": "Rozšířený filtr (MongoDB)",
|
|
6169
|
+
"datatable.advancedFilter.desc": "Vytvořte si vlastní pokročilé filtry od nuly pomocí agregačních operátorů MongoDB dostupných zde.",
|
|
6170
|
+
"editData": "Upravit v {{0}}",
|
|
6171
|
+
"btns.duplicate": "Duplikovat",
|
|
6172
|
+
"btns.edit": "Upravit",
|
|
6173
|
+
"btns.delete": "Smazat",
|
|
6129
6174
|
"field_return_status_hint": "Aktuální stav žádosti o vrácení (např.: Požadováno, Schváleno, Vráceno).",
|
|
6130
6175
|
"field_workflowRun_completedAt_hint": "Časové razítko dokončení provedení (úspěšného nebo neúspěšného).",
|
|
6131
6176
|
"field_employee_socialSecurityNumber_hint": "Rodné číslo zaměstnance.",
|
|
@@ -6966,6 +7011,12 @@ export const translations = {
|
|
|
6966
7011
|
},
|
|
6967
7012
|
ru: {
|
|
6968
7013
|
translation: {
|
|
7014
|
+
"datatable.advancedFilter.title": "Расширенный фильтр (MongoDB)",
|
|
7015
|
+
"datatable.advancedFilter.desc": "Создайте свои собственные расширенные фильтры с нуля, используя операторы агрегации MongoDB, доступные здесь",
|
|
7016
|
+
"editData": "Редактировать в {{0}}",
|
|
7017
|
+
"btns.duplicate": "Дублировать",
|
|
7018
|
+
"btns.edit": "Редактировать",
|
|
7019
|
+
"btns.delete": "Удалить",
|
|
6969
7020
|
"field_workflowRun_completedAt_hint": "Метка времени завершения выполнения (успешного или неудачного).",
|
|
6970
7021
|
"field_employee_socialSecurityNumber_hint": "Номер социального страхования сотрудника.",
|
|
6971
7022
|
"field_shipment_order_hint": "Заказ, связанный с этой отгрузкой.",
|
|
@@ -8161,7 +8212,12 @@ export const translations = {
|
|
|
8161
8212
|
},
|
|
8162
8213
|
ar: {
|
|
8163
8214
|
translation: {
|
|
8164
|
-
|
|
8215
|
+
"datatable.advancedFilter.title": "مرشح متقدم (MongoDB)",
|
|
8216
|
+
"datatable.advancedFilter.desc": "أنشئ مرشحاتك المتقدمة من الصفر، باستخدام عوامل تجميع MongoDB المتوفرة هنا",
|
|
8217
|
+
"editData": "تعديل في {{0}}",
|
|
8218
|
+
"btns.duplicate": "تكرار",
|
|
8219
|
+
"btns.edit": "تعديل",
|
|
8220
|
+
"btns.delete": "حذف",
|
|
8165
8221
|
"field_return_status_hint": "الحالة الحالية لطلب الإرجاع (مثال: مطلوب، معتمد، تم استرداده).",
|
|
8166
8222
|
"field_workflowRun_completedAt_hint": "طابع زمني لانتهاء التنفيذ (نجاحًا أو فشلًا).",
|
|
8167
8223
|
"field_employee_socialSecurityNumber_hint": "رقم الضمان الاجتماعي للموظف.",
|
|
@@ -9407,7 +9463,12 @@ export const translations = {
|
|
|
9407
9463
|
},
|
|
9408
9464
|
sv: {
|
|
9409
9465
|
translation: {
|
|
9410
|
-
|
|
9466
|
+
"datatable.advancedFilter.title": "Avancerat filter (MongoDB)",
|
|
9467
|
+
"datatable.advancedFilter.desc": "Skapa dina egna avancerade filter från grunden med hjälp av MongoDBs aggregeringsoperatorer som finns här.",
|
|
9468
|
+
"editData": "Redigera i {{0}}",
|
|
9469
|
+
"btns.duplicate": "Duplicera",
|
|
9470
|
+
"btns.edit": "Redigera",
|
|
9471
|
+
"btns.delete": "Ta bort",
|
|
9411
9472
|
"field_return_status_hint": "Den aktuella statusen för returförfrågan (t.ex. Begärd, Godkänd, Återbetald).",
|
|
9412
9473
|
"field_workflowRun_completedAt_hint": "Tidsstämpel för när körningen avslutades (lyckad eller misslyckad).",
|
|
9413
9474
|
"field_employee_socialSecurityNumber_hint": "Anställds personnummer.",
|
|
@@ -10551,6 +10612,12 @@ export const translations = {
|
|
|
10551
10612
|
},
|
|
10552
10613
|
el: {
|
|
10553
10614
|
translation: {
|
|
10615
|
+
"datatable.advancedFilter.title": "Προηγμένο Φίλτρο (MongoDB)",
|
|
10616
|
+
"datatable.advancedFilter.desc": "Δημιουργήστε τα δικά σας προηγμένα φίλτρα από την αρχή, χρησιμοποιώντας τους τελεστές συνάθροισης της MongoDB που είναι διαθέσιμοι εδώ.",
|
|
10617
|
+
"editData": "Επεξεργασία στο {{0}}",
|
|
10618
|
+
"btns.duplicate": "Διπλότυπο",
|
|
10619
|
+
"btns.edit": "Επεξεργασία",
|
|
10620
|
+
"btns.delete": "Διαγραφή",
|
|
10554
10621
|
"field_workflowRun_completedAt_hint": "Χρονική σήμανση της ολοκλήρωσης της εκτέλεσης (επιτυχής ή αποτυχημένη).",
|
|
10555
10622
|
"field_employee_socialSecurityNumber_hint": "Ο αριθμός κοινωνικής ασφάλισης του υπαλλήλου.",
|
|
10556
10623
|
"field_shipment_order_hint": "Η παραγγελία που σχετίζεται με αυτήν την αποστολή.",
|
|
@@ -11680,6 +11747,12 @@ export const translations = {
|
|
|
11680
11747
|
},
|
|
11681
11748
|
fa: {
|
|
11682
11749
|
translation: {
|
|
11750
|
+
"datatable.advancedFilter.title": "فیلتر پیشرفته (MongoDB)",
|
|
11751
|
+
"datatable.advancedFilter.desc": "با استفاده از عملگرهای تجمیع MongoDB که در اینجا موجود است، فیلترهای پیشرفته خود را از ابتدا ایجاد کنید.",
|
|
11752
|
+
"editData": "ویرایش در {{0}}",
|
|
11753
|
+
"btns.duplicate": "کپی",
|
|
11754
|
+
"btns.edit": "ویرایش",
|
|
11755
|
+
"btns.delete": "حذف",
|
|
11683
11756
|
"field_return_status_hint": "وضعیت فعلی درخواست مرجوعی (مثال: درخواست شده، تأیید شده، مسترد شده).",
|
|
11684
11757
|
"field_workflowRun_completedAt_hint": "مهر زمانی پایان اجرا (موفق یا ناموفق).",
|
|
11685
11758
|
"field_employee_socialSecurityNumber_hint": "شماره تأمین اجتماعی کارمند.",
|
package/src/migrate.js
CHANGED
|
@@ -3,16 +3,15 @@
|
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import fs from "node:fs/promises";
|
|
5
5
|
import path from "node:path";
|
|
6
|
-
// FIX: Import 'pathToFileURL' instead of 'fileURLToPath'
|
|
7
6
|
import { pathToFileURL } from 'node:url';
|
|
8
7
|
import { Engine, MongoDatabase } from "./engine.js";
|
|
9
8
|
import { Logger } from "./gameObject.js";
|
|
10
9
|
import { Config } from "./config.js";
|
|
11
|
-
import chalk from "chalk";
|
|
10
|
+
import chalk from "chalk";
|
|
12
11
|
|
|
13
12
|
// Configuration de base
|
|
14
13
|
Config.Set("modules", ["mongodb"]);
|
|
15
|
-
const MIGRATIONS_DIR = path.resolve(process.cwd(), '
|
|
14
|
+
const MIGRATIONS_DIR = path.resolve(process.cwd(), 'migrations');
|
|
16
15
|
const MIGRATIONS_COLLECTION = 'migrations_log';
|
|
17
16
|
|
|
18
17
|
const engine = await Engine.Create();
|
package/src/modules/data.js
CHANGED
|
@@ -1635,9 +1635,9 @@ export async function onInit(defaultEngine) {
|
|
|
1635
1635
|
engine.put('/api/model/:id', [middlewareAuthenticator, userInitiator, setTimeoutMiddleware(15000)], async (req, res) => {
|
|
1636
1636
|
const result = await editModel(req.me, req.params.id, req.fields);
|
|
1637
1637
|
if( result.success){
|
|
1638
|
-
return res.status(result.statusCode ||
|
|
1638
|
+
return res.status(result.statusCode || 200).json(result);
|
|
1639
1639
|
}else{
|
|
1640
|
-
return res.status(result.statusCode ||
|
|
1640
|
+
return res.status(result.statusCode || 500).json(result);
|
|
1641
1641
|
}
|
|
1642
1642
|
});
|
|
1643
1643
|
|
|
@@ -4038,9 +4038,18 @@ export const searchData = async ({user, query}) => {
|
|
|
4038
4038
|
{$eq: ["$_model", fi.relation]},
|
|
4039
4039
|
{$eq: ["$_user", user.username]},
|
|
4040
4040
|
fi.multiple ? {
|
|
4041
|
-
$in: [{$toString: "$_id"}, {
|
|
4041
|
+
$in: [{ $toString: "$_id" }, {
|
|
4042
|
+
$map: {
|
|
4043
|
+
input: { $ifNull: ["$$convertedId", []] }, // On utilise le tableau d'IDs, ou un tableau vide s'il est null
|
|
4044
|
+
as: "relationId",
|
|
4045
|
+
in: { $toString: "$$relationId" }
|
|
4046
|
+
}
|
|
4047
|
+
}]
|
|
4042
4048
|
} : {
|
|
4043
|
-
$eq: [
|
|
4049
|
+
$eq: [
|
|
4050
|
+
{ $toString: "$_id" },
|
|
4051
|
+
{ $convert: { input: '$$convertedId', to: "string", onError: '' } }
|
|
4052
|
+
]
|
|
4044
4053
|
}
|
|
4045
4054
|
]
|
|
4046
4055
|
]
|
|
@@ -4315,6 +4324,7 @@ export const searchData = async ({user, query}) => {
|
|
|
4315
4324
|
} else if (fi.type === 'array') {
|
|
4316
4325
|
// Handle array filtering here
|
|
4317
4326
|
if (data[fi.name]) {
|
|
4327
|
+
console.log('array filtering', data[fi.name]);
|
|
4318
4328
|
pipelines.push({
|
|
4319
4329
|
$match: {
|
|
4320
4330
|
$expr: {
|
|
@@ -4350,6 +4360,7 @@ export const searchData = async ({user, query}) => {
|
|
|
4350
4360
|
|
|
4351
4361
|
let pipelines = [];
|
|
4352
4362
|
if( allIds.length ){
|
|
4363
|
+
console.log({allIds});
|
|
4353
4364
|
const id = {$in: ["$_id", allIds.map(m => new ObjectId(m))]};
|
|
4354
4365
|
pipelines.push({
|
|
4355
4366
|
$match: { $expr: id }
|
package/src/modules/mongodb.js
CHANGED
|
@@ -13,15 +13,6 @@ export async function onInit(defaultEngine) {
|
|
|
13
13
|
|
|
14
14
|
const isProduction = process.env.NODE_ENV === 'production'
|
|
15
15
|
|
|
16
|
-
let ca, cert, key;
|
|
17
|
-
try {
|
|
18
|
-
ca = fs.readFileSync('certs/mongodb-cert.crt');
|
|
19
|
-
cert = fs.readFileSync('certs/mongodb.pem');
|
|
20
|
-
key = fs.readFileSync(`certs/mongodb-cert.key`);
|
|
21
|
-
} catch (e) {
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
16
|
modelsCollection = MongoDatabase.collection("models");
|
|
26
17
|
datasCollection = MongoDatabase.collection("datas");
|
|
27
18
|
filesCollection = MongoDatabase.collection("files");
|
package/src/packs.js
CHANGED
|
@@ -390,8 +390,9 @@ export const getAllPacks = async () => {
|
|
|
390
390
|
return {
|
|
391
391
|
name: m.name,
|
|
392
392
|
permissions: {
|
|
393
|
-
"$
|
|
394
|
-
"$in": ["$name", m.perms]
|
|
393
|
+
"$link": {
|
|
394
|
+
"$in": ["$name", m.perms],
|
|
395
|
+
"_model": "permission"
|
|
395
396
|
}
|
|
396
397
|
}
|
|
397
398
|
}
|
|
@@ -407,7 +408,7 @@ export const getAllPacks = async () => {
|
|
|
407
408
|
"code": "fr"
|
|
408
409
|
}],
|
|
409
410
|
"translation": [
|
|
410
|
-
{ "lang": { "$
|
|
411
|
+
{ "lang": { "$link": { "$eq": ["$code", "fr"]}, "_model": "lang"}, "key": "Visitor alerts", "value": "Alertes visiteurs" }
|
|
411
412
|
]
|
|
412
413
|
}
|
|
413
414
|
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Migration: setting content field to 'fr' in 'content' model
|
|
3
|
-
* Created at: 2024-05-21T10:00:00.000Z
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export const up = async (db) => {
|
|
7
|
-
const datasCollection = db.collection("datas");
|
|
8
|
-
const content = await datasCollection.find({ _model:"content"}).toArray();
|
|
9
|
-
await Promise.all(content.map(async (doc) => {
|
|
10
|
-
if (typeof(doc.html)==='string') {
|
|
11
|
-
const c = { "fr" : doc.html };
|
|
12
|
-
await datasCollection.updateOne({_id: doc._id}, {$set: {html: c}});
|
|
13
|
-
}
|
|
14
|
-
}));
|
|
15
|
-
console.log("Migration UP: setting content field to 'fr' in 'content' model");
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export const down = async (db) => {
|
|
19
|
-
console.log("Migration DOWN: noting to do");
|
|
20
|
-
};
|