data-primals-engine 1.2.3 → 1.2.5

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 (58) hide show
  1. package/CONTRIBUTING.md +91 -0
  2. package/README.md +50 -22
  3. package/client/src/App.jsx +0 -5
  4. package/client/src/App.scss +6 -0
  5. package/client/src/ConditionBuilder.scss +34 -1
  6. package/client/src/ConditionBuilder2.jsx +179 -53
  7. package/client/src/ContentView.jsx +0 -3
  8. package/client/src/CronBuilder.jsx +0 -1
  9. package/client/src/CronPartBuilder.jsx +0 -2
  10. package/client/src/DashboardView.jsx +0 -5
  11. package/client/src/DataEditor.jsx +8 -10
  12. package/client/src/DataImporter.jsx +469 -0
  13. package/client/src/DataLayout.jsx +0 -1
  14. package/client/src/DataTable.jsx +2 -368
  15. package/client/src/DataTable.scss +18 -1
  16. package/client/src/Field.jsx +85 -48
  17. package/client/src/FlexBuilder.jsx +1 -1
  18. package/client/src/ModelCreator.jsx +29 -25
  19. package/client/src/ModelCreator.scss +13 -0
  20. package/client/src/ModelCreatorField.jsx +1 -5
  21. package/client/src/RTE.jsx +1 -6
  22. package/client/src/RTETrans.jsx +0 -2
  23. package/client/src/RelationField.jsx +1 -1
  24. package/client/src/RelationValue.jsx +1 -2
  25. package/client/src/TourSpotlight.jsx +0 -2
  26. package/client/src/filter.js +87 -0
  27. package/client/src/hooks/data.js +1 -3
  28. package/client/src/hooks/useTutorials.jsx +0 -1
  29. package/client/src/translations.js +60 -26
  30. package/package.json +4 -3
  31. package/server.js +2 -2
  32. package/src/data.js +8 -0
  33. package/src/email.js +2 -2
  34. package/src/engine.js +59 -20
  35. package/src/index.js +1 -1
  36. package/src/middlewares/middleware-mongodb.js +0 -1
  37. package/src/modules/assistant.js +1 -3
  38. package/src/modules/bucket.js +3 -4
  39. package/src/modules/data/data.core.js +17 -0
  40. package/src/modules/{data.js → data/data.js} +4595 -5991
  41. package/src/modules/data/data.routes.js +1637 -0
  42. package/src/modules/data/index.js +1 -0
  43. package/src/modules/file.js +1 -1
  44. package/src/modules/mongodb.js +0 -1
  45. package/src/modules/user.js +1 -1
  46. package/src/modules/workflow.js +38 -38
  47. package/src/packs.js +4 -1
  48. package/test/data.backup.integration.test.js +4 -5
  49. package/test/data.integration.test.js +2 -6
  50. package/test/events.test.js +1 -1
  51. package/test/file.test.js +1 -4
  52. package/test/import_export.integration.test.js +22 -15
  53. package/test/model.integration.test.js +8 -10
  54. package/test/user.test.js +2 -2
  55. package/test/vm.test.js +1 -1
  56. package/test/workflow.integration.test.js +17 -14
  57. package/test/workflow.robustness.test.js +15 -10
  58. package/src/modules/test +0 -147
package/server.js CHANGED
@@ -8,7 +8,7 @@ import {Config, Engine, BenchmarkTool, GameObject, Logger} from "./src/index.js"
8
8
  import sirv from "sirv";
9
9
  import express from "express";
10
10
 
11
- Config.Set("modules", ["mongodb", "data", "file", "bucket", "workflow","user", "assistant", "swagger"])
11
+ Config.Set("modules", ["data", "mongodb", "file", "bucket", "workflow","user", "assistant", "swagger"])
12
12
  Config.Set("middlewares", []);
13
13
 
14
14
  const bench = GameObject.Create("Benchmark");
@@ -30,7 +30,7 @@ if (process.argv.length === 3) {
30
30
  const port = process.env.PORT || 7633;
31
31
  engine.start(port, async (r) => {
32
32
  const logger = engine.getComponent(Logger);
33
- console.log("Server started on port" + port);
33
+ console.log("Server started on port " + port);
34
34
  timer.stop();
35
35
 
36
36
  });
package/src/data.js CHANGED
@@ -405,6 +405,14 @@ export function conditionToApiSearchFilter(condition) {
405
405
 
406
406
 
407
407
  export const getFieldValueHash = (model, data) => {
408
+ // Prioritize composite unique constraints if they exist
409
+ const uniqueConstraints = model.constraints?.filter(c => c.type === 'unique') || [];
410
+
411
+ if (uniqueConstraints.length > 0) {
412
+ // Combine keys from all unique constraints to create a truly unique hash.
413
+ const constraintKeys = [...new Set(uniqueConstraints.flatMap(c => c.keys))];
414
+ return getObjectHash(data, constraintKeys, 'constraint');
415
+ }
408
416
  const uniqueFields = model.fields.filter(f => f.unique).map(m => m.name);
409
417
  const fs = model.fields.map(m => m.name);
410
418
  const fields = ['_model', '_user', ...(uniqueFields.length ? uniqueFields : fs)];
package/src/email.js CHANGED
@@ -75,9 +75,9 @@ export const sendEmail = async (email = "", data, smtpConfig = null, lang, tpl =
75
75
 
76
76
  try {
77
77
  await Promise.all(sendPromises);
78
- console.log(`Email(s) envoyé(s) avec succès à: ${emails.join(', ')}`);
78
+ console.log(`Email(s) sent successfully to : ${emails.join(', ')}`);
79
79
  } catch (error) {
80
- console.error("Erreur lors de l'envoi d'un ou plusieurs emails :", error);
80
+ console.error("Error when sending to one ore more emails :", error);
81
81
  // Vous pouvez relancer l'erreur si vous voulez que l'appelant la gère
82
82
  throw error;
83
83
  }
package/src/engine.js CHANGED
@@ -13,13 +13,15 @@ import {
13
13
  import http from "http";
14
14
  import cookieParser from "cookie-parser";
15
15
  import requestIp from 'request-ip';
16
- import {createModel, deleteModels, getModels, installAllPacks, validateModelStructure} from "./modules/data.js";
16
+ import {createModel, deleteModels, getModels, installAllPacks, validateModelStructure} from "./modules/data/data.js";
17
17
  import {defaultModels} from "./defaultModels.js";
18
18
  import {DefaultUserProvider} from "./providers.js";
19
19
  import formidableMiddleware from 'express-formidable';
20
20
  import sirv from "sirv";
21
21
  import * as tls from "node:tls";
22
22
  import {Event} from "./events.js";
23
+ import path from "node:path";
24
+ import {isPathRelativeTo, isValidPath} from "./core.js";
23
25
 
24
26
  // Constants
25
27
  const isProduction = process.env.NODE_ENV === 'production'
@@ -49,23 +51,35 @@ const isTlsActive = !(!process.env.TLS || ["0", "false"].includes(process.env.TL
49
51
  const clientOptions = {
50
52
  maxPoolSize: databasePoolSize
51
53
  };
52
- // On ajoute les options TLS si elles sont activées
54
+
55
+ // We add TLS options if enabled
53
56
  if (isTlsActive) {
54
57
  clientOptions.tls = true;
55
- // Chemin vers le certificat de l'autorité de certification (pour faire confiance au serveur)
56
- if (process.env.CA_CERT) {
57
- clientOptions.tlsCAFile = process.env.CA_CERT;
58
- }
59
- // Chemin vers le certificat et la clé du CLIENT (pour que le serveur vous fasse confiance)
60
- if (process.env.CERT_KEY) {
61
- clientOptions.tlsCertificateKeyFile = process.env.CERT_KEY;
58
+
59
+ // is mTLS ? (client certificate required instead of password)
60
+ if (process.env.CERT) {
61
+ clientOptions.secureContext = tls.createSecureContext({
62
+ ca: fs.readFileSync(process.env.CA_CERT),
63
+ cert: fs.readFileSync(process.env.CERT),
64
+ key: fs.readFileSync(process.env.CERT_KEY)
65
+ });
66
+ }else {
67
+ // Path to the authority certificate
68
+ if (process.env.CA_CERT) {
69
+ clientOptions.tlsCAFile = process.env.CA_CERT;
70
+ }
71
+ // Path to the certificate key
72
+ if (process.env.CERT_KEY) {
73
+ clientOptions.tlsCertificateKeyFile = process.env.CERT_KEY;
74
+ }
62
75
  }
63
- // Options pour le développement (à utiliser avec prudence)
64
76
  if (tlsAllowInvalidCertificates) {
65
77
  clientOptions.tlsAllowInvalidCertificates = true;
78
+ console.warn("🚨 [SECURITY WARNING] tlsAllowInvalidCertificates is ON. Server certificate will not be validated.");
66
79
  }
67
80
  if (tlsAllowInvalidHostnames) {
68
81
  clientOptions.tlsAllowInvalidHostnames = true;
82
+ console.warn("🚨 [SECURITY WARNING] tlsAllowInvalidHostnames is ON. Server hostname will not be validated.");
69
83
  }
70
84
  }
71
85
 
@@ -143,18 +157,44 @@ export const Engine = {
143
157
  }
144
158
  };
145
159
 
146
- await Promise.all(Config.Get('modules', []).map(async module => {
160
+ await Promise.all(Config.Get('modules', []).map(async moduleIdentifier => {
147
161
  try {
148
- if( fs.existsSync(module) ){
149
- return await importModule(module);
150
- }else {
151
- return await importModule('./modules/' + module + ".js");
162
+ let moduleDir;
163
+ const moduleName = path.basename(moduleIdentifier);
164
+
165
+ const directPath = path.resolve(moduleIdentifier);
166
+ let isDir = fs.existsSync(directPath) && fs.statSync(directPath).isDirectory();
167
+ if (isDir) {
168
+ moduleDir = directPath;
169
+ if (!fs.existsSync(moduleDir) || !fs.statSync(moduleDir).isDirectory()) {
170
+ logger.warn(`Le dossier du module est introuvable pour l'identifiant : '${moduleIdentifier}'. Chemin cherché : '${moduleDir}'.`);
171
+ return null;
172
+ }
173
+ } else {
174
+ moduleDir = path.resolve('./src/modules', moduleIdentifier);
175
+ }
176
+
177
+ let moduleEntryPoint;
178
+ const jsPath = moduleDir+'.js';
179
+ const indexJsPath = path.join(moduleDir, 'index.js');
180
+ const moduleJsPath = path.join(moduleDir, `${moduleName}.js`);
181
+
182
+ if (fs.existsSync(jsPath)) {
183
+ moduleEntryPoint = 'file://'+jsPath;
184
+ } else if (fs.existsSync(indexJsPath)) {
185
+ moduleEntryPoint = 'file://'+indexJsPath;
186
+ } else if (fs.existsSync(moduleJsPath)) {
187
+ moduleEntryPoint = 'file://'+moduleJsPath;
152
188
  }
153
- } catch (e){
154
- logger.info('ERROR at loading module '+ module, e.stack);
189
+
190
+ return await importModule(moduleEntryPoint);
191
+ } catch (e) {
192
+ logger.error(`Échec du chargement du module '${moduleIdentifier}':`, e.stack);
193
+ return null;
155
194
  }
156
- })).then(async e => {
157
- engine._modules = e;
195
+ })).then(async results => {
196
+ // On filtre les modules qui n'ont pas pu être chargés
197
+ engine._modules = results.filter(Boolean);
158
198
  return Promise.resolve();
159
199
  });
160
200
 
@@ -239,4 +279,3 @@ export const Engine = {
239
279
  return engine;
240
280
  }
241
281
  }
242
-
package/src/index.js CHANGED
@@ -10,6 +10,6 @@ export { UserProvider } from './providers.js';
10
10
 
11
11
  // --- Database & Data Modules ---
12
12
  export { datasCollection, filesCollection, modelsCollection, packsCollection } from './modules/mongodb.js';
13
- export { searchData, insertData, editData, exportData, importData, scheduleAlerts, cancelAlerts, validateModelStructure, installPack, jobDumpUserData, loadFromDump, dumpUserData, validateRestoreRequest, patchData, deleteData, createModel, editModel, deleteModels, getModel, getModels } from './modules/data.js';
13
+ export { searchData, insertData, editData, exportData, importData, scheduleAlerts, cancelAlerts, validateModelStructure, installPack, jobDumpUserData, loadFromDump, dumpUserData, validateRestoreRequest, patchData, deleteData, createModel, editModel, deleteModels, getModel, getModels } from './modules/data/index.js';
14
14
 
15
15
 
@@ -60,7 +60,6 @@ function _sanitize(target, options) {
60
60
 
61
61
  if( (key === 'regex' && obj.input) || key === '$regex' ){
62
62
  obj[key] = new RegExp(val, "ui");
63
- console.log('regex san', obj[key]);
64
63
  }
65
64
  else if (!wl.includes(key) && regex.test(key)) {
66
65
  isSanitized = true;
@@ -1,11 +1,9 @@
1
- // server/src/modules/assistant.js
2
-
3
1
  import { getCollectionForUser, modelsCollection } from "./mongodb.js";
4
2
  import { Logger } from "../gameObject.js";
5
3
  import { ChatOpenAI } from "@langchain/openai";
6
4
  import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
7
5
  import { HumanMessage, SystemMessage } from "@langchain/core/messages";
8
- import {searchData, patchData, deleteData, insertData} from "./data.js";
6
+ import {searchData, patchData, deleteData, insertData} from "./data/index.js";
9
7
  import { getDataAsString } from "../data.js";
10
8
  import i18n from "../../src/i18n.js";
11
9
  import {generateLimiter} from "./user.js";
@@ -3,7 +3,7 @@ import AWS from "aws-sdk";
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import {decryptValue, encryptValue} from "../data.js";
6
- import {loadFromDump, validateRestoreRequest} from "./data.js";
6
+ import {loadFromDump, validateRestoreRequest} from "./data/index.js";
7
7
  import {Logger} from "../gameObject.js";
8
8
  import {middlewareAuthenticator, userInitiator} from "./user.js";
9
9
  import {awsDefaultConfig, maxBytesPerSecondThrottleData} from "../constants.js";
@@ -117,7 +117,6 @@ export async function getUserS3Config(user) {
117
117
  bucketName: userConfig.bucketName || defaultConfig.bucketName
118
118
  };
119
119
  }
120
- console.log({defaultConfig})
121
120
  return defaultConfig;
122
121
  }
123
122
  const getS3Client = (s3Config) => {
@@ -212,9 +211,9 @@ export const deleteFromS3 = async (s3Config, remoteFilename) => {
212
211
 
213
212
  try {
214
213
  await s3.deleteObject(params).promise();
215
- console.log(`Fichier supprimé avec succès de S3 : ${bucketPath}`);
214
+ console.log(`File successfully removed from S3 : ${bucketPath}`);
216
215
  } catch (err) {
217
- console.error("Erreur lors de la suppression sur S3 :", err);
216
+ console.error("Error when deleting S3 file :", err);
218
217
  throw err;
219
218
  }
220
219
  };
@@ -0,0 +1,17 @@
1
+ import NodeCache from "node-cache";
2
+
3
+ export const modelsCache = new NodeCache( { stdTTL: 100, checkperiod: 120 } );
4
+
5
+ export const mongoDBWhitelist = [
6
+ "$$NOW", "$in", "$eq", "$gt", "$gte", "$in", "$lt", "$lte", "$ne", "$nin", "$type", "$size",
7
+ "$and", "$not", "$nor", "$or", "$regexMatch", "$find", "$elemMatch", "$filter", "$toString", "$toObjectId",
8
+ "$concat",
9
+ '$add', '$subtract', '$multiply', '$divide', '$mod', '$pow', "$sqrt",
10
+ "$rand",
11
+ "$abs", '$sin', '$cos', '$tan', '$asin', '$acos', '$atan',
12
+ "$toDate", "$toBool", "$toString", "$toInt", "$toDouble",
13
+ "$dateDiff", "$dateSubtract", "$dateAdd", "$dateToString",
14
+ '$year', '$month', '$week', '$dayOfMonth', '$dayOfWeek', '$dayOfYear', '$hour', '$minute', '$second', '$millisecond'
15
+ ];
16
+ export let importJobs = {};
17
+