data-primals-engine 1.2.2 → 1.2.4

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 (54) hide show
  1. package/CONTRIBUTING.md +91 -0
  2. package/README.md +33 -17
  3. package/client/src/App.jsx +0 -5
  4. package/client/src/ConditionBuilder.scss +34 -1
  5. package/client/src/ConditionBuilder2.jsx +179 -53
  6. package/client/src/ContentView.jsx +0 -3
  7. package/client/src/CronBuilder.jsx +0 -1
  8. package/client/src/CronPartBuilder.jsx +0 -2
  9. package/client/src/DashboardView.jsx +0 -5
  10. package/client/src/DataEditor.jsx +8 -211
  11. package/client/src/DataLayout.jsx +0 -1
  12. package/client/src/DataTable.jsx +1 -3
  13. package/client/src/Field.jsx +0 -5
  14. package/client/src/FlexBuilder.jsx +1 -1
  15. package/client/src/ModelCreatorField.jsx +1 -5
  16. package/client/src/RTE.jsx +1 -6
  17. package/client/src/RTETrans.jsx +0 -2
  18. package/client/src/RelationField.jsx +1 -1
  19. package/client/src/RelationValue.jsx +1 -2
  20. package/client/src/TourSpotlight.jsx +0 -2
  21. package/client/src/constants.js +1 -1
  22. package/client/src/filter.js +87 -0
  23. package/client/src/hooks/data.js +1 -3
  24. package/client/src/hooks/useTutorials.jsx +0 -1
  25. package/package.json +3 -3
  26. package/server.js +2 -2
  27. package/src/constants.js +2 -2
  28. package/src/defaultModels.js +1 -14
  29. package/src/email.js +9 -7
  30. package/src/engine.js +60 -20
  31. package/src/events.js +1 -1
  32. package/src/filter.js +221 -0
  33. package/src/index.js +1 -1
  34. package/src/middlewares/middleware-mongodb.js +0 -1
  35. package/src/modules/assistant.js +1 -3
  36. package/src/modules/bucket.js +3 -4
  37. package/src/modules/{data.js → data/data.js} +42 -59
  38. package/src/modules/data/index.js +1 -0
  39. package/src/modules/file.js +1 -1
  40. package/src/modules/mongodb.js +0 -1
  41. package/src/modules/user.js +1 -1
  42. package/src/modules/workflow.js +299 -133
  43. package/src/packs.js +249 -8
  44. package/test/data.backup.integration.test.js +7 -5
  45. package/test/data.integration.test.js +8 -6
  46. package/test/events.test.js +1 -1
  47. package/test/file.test.js +11 -17
  48. package/test/import_export.integration.test.js +38 -27
  49. package/test/model.integration.test.js +20 -21
  50. package/test/user.test.js +32 -25
  51. package/test/vm.test.js +51 -0
  52. package/test/workflow.integration.test.js +22 -14
  53. package/test/workflow.robustness.test.js +19 -9
  54. package/src/modules/test +0 -147
@@ -1,4 +1,4 @@
1
- import {Logger} from "../gameObject.js";
1
+ import {Logger} from "../../gameObject.js";
2
2
  import {BSON, ObjectId} from "mongodb";
3
3
  import * as util from 'node:util';
4
4
  import {promisify} from 'node:util';
@@ -9,9 +9,9 @@ import * as tar from "tar";
9
9
  import process from "node:process";
10
10
  import {randomColor} from "randomcolor";
11
11
  import cronstrue from 'cronstrue/i18n.js';
12
- import {setTimeoutMiddleware} from '../middlewares/timeout.js';
12
+ import {setTimeoutMiddleware} from '../../middlewares/timeout.js';
13
13
  import {mkdir} from 'node:fs/promises';
14
- import {anonymizeText, getDefaultForType, getFieldValueHash, getUserId, isDemoUser, isLocalUser} from "../data.js";
14
+ import {anonymizeText, getDefaultForType, getFieldValueHash, getUserId, isDemoUser, isLocalUser} from "../../data.js";
15
15
  import {
16
16
  allowedFields,
17
17
  dbName,
@@ -36,7 +36,7 @@ import {
36
36
  optionsSanitizer,
37
37
  searchRequestTimeout,
38
38
  storageSafetyMargin
39
- } from "../constants.js";
39
+ } from "../../constants.js";
40
40
  import {
41
41
  getCollection,
42
42
  getCollectionForUser,
@@ -44,43 +44,43 @@ import {
44
44
  isObjectId,
45
45
  modelsCollection,
46
46
  packsCollection
47
- } from "./mongodb.js";
48
- import {dbUrl, MongoClient, MongoDatabase} from "../engine.js";
47
+ } from "../mongodb.js";
48
+ import {dbUrl, MongoClient, MongoDatabase} from "../../engine.js";
49
49
  import path from "node:path";
50
- import {getFileExtension, getObjectHash, getRandom, isGUID, isPlainObject, randomDate, uuidv4} from "../core.js";
51
- import {Event} from "../events.js";
50
+ import {getFileExtension, getObjectHash, getRandom, isGUID, isPlainObject, randomDate, sleep, uuidv4} from "../../core.js";
51
+ import {Event} from "../../events.js";
52
52
  import fs from "node:fs";
53
53
  import schedule from "node-schedule";
54
- import {middleware} from "../middlewares/middleware-mongodb.js";
55
- import i18n from "../i18n.js";
54
+ import {middleware} from "../../middlewares/middleware-mongodb.js";
55
+ import i18n from "../../i18n.js";
56
56
  import {
57
- executeSafeJavascript,
57
+ executeSafeJavascript, processWorkflowRun,
58
58
  runScheduledJobWithDbLock,
59
59
  scheduleWorkflowTriggers,
60
60
  triggerWorkflows
61
- } from "./workflow.js";
61
+ } from "../workflow.js";
62
62
  import NodeCache from "node-cache";
63
63
  import AWS from 'aws-sdk';
64
- import {openaiJobModel} from "../openai.jobs.js";
64
+ import {openaiJobModel} from "../../openai.jobs.js";
65
65
  import checkDiskSpace from "check-disk-space";
66
66
  import {fileURLToPath} from 'url';
67
67
  import {Worker} from 'worker_threads';
68
- import {addFile, encryptFile, removeFile} from "./file.js";
69
- import {downloadFromS3, getS3Stream, getUserS3Config, listS3Backups, uploadToS3} from "./bucket.js";
68
+ import {addFile, encryptFile, removeFile} from "../file.js";
69
+ import {downloadFromS3, getS3Stream, getUserS3Config, listS3Backups, uploadToS3} from "../bucket.js";
70
70
  import {
71
71
  calculateTotalUserStorageUsage,
72
72
  generateLimiter,
73
73
  hasPermission,
74
74
  middlewareAuthenticator,
75
75
  userInitiator
76
- } from "./user.js";
77
- import {assistantGlobalLimiter} from "./assistant.js";
78
- import {getAllPacks} from "../packs.js";
79
- import {throttleMiddleware} from "../middlewares/throttle.js";
80
- import {Config} from "../config.js";
81
- import {profiles} from "../../client/src/constants.js";
82
- import {processFilterPlaceholders} from "../../client/src/filter.js";
83
- import {tutorialsConfig} from "../../client/src/tutorials.js";
76
+ } from "../user.js";
77
+ import {assistantGlobalLimiter} from "../assistant.js";
78
+ import {getAllPacks} from "../../packs.js";
79
+ import {throttleMiddleware} from "../../middlewares/throttle.js";
80
+ import {Config} from "../../config.js";
81
+ import {profiles} from "../../../client/src/constants.js";
82
+ import {processFilterPlaceholders} from "../../../client/src/filter.js";
83
+ import {tutorialsConfig} from "../../../client/src/tutorials.js";
84
84
 
85
85
  // Obtenir le chemin du répertoire courant de manière fiable avec ES Modules
86
86
  const __filename = fileURLToPath(import.meta.url);
@@ -94,7 +94,6 @@ const sseConnections = new Map();
94
94
  const delay = ms => new Promise(res => setTimeout(res, ms));
95
95
 
96
96
  const getBackupDir = () => process.env.BACKUP_DIR || './backups'; // Répertoire de stockage des sauvegardes
97
- const execAsync = promisify(exec);
98
97
  const execFileAsync = promisify(execFile);
99
98
 
100
99
  let importJobs = {};
@@ -203,7 +202,6 @@ export const dataTypes = {
203
202
  },
204
203
  code: {
205
204
  validate: (value, field) => {
206
- console.log(field,typeof(value));
207
205
  return value === null || (field.language === 'json' && typeof(value) === 'object') || (typeof value === 'string' && (field.maxlength === undefined || field.maxlength <= 0 || value.length <= field.maxlength));
208
206
  },
209
207
  filter: async (value, field) => {
@@ -537,7 +535,7 @@ export const getAPILang = (langs) => {
537
535
  */
538
536
  function runImportExportWorker(action, payload) {
539
537
  return new Promise((resolve, reject) => {
540
- const workerPath = path.resolve(__dirname, '../workers/import-export-worker.js');
538
+ const workerPath = path.resolve(process.cwd(), './src/workers/import-export-worker.js');
541
539
  const worker = new Worker(workerPath);
542
540
 
543
541
  worker.postMessage({ action, payload });
@@ -572,7 +570,7 @@ function runImportExportWorker(action, payload) {
572
570
  */
573
571
  function runCryptoWorkerTask(action, payload) {
574
572
  return new Promise((resolve, reject) => {
575
- const workerPath = path.resolve(__dirname, '../workers/crypto-worker.js');
573
+ const workerPath = path.resolve(process.cwd(), './src/workers/crypto-worker.js');
576
574
  const worker = new Worker(workerPath);
577
575
 
578
576
  worker.postMessage({ action, payload });
@@ -1297,7 +1295,7 @@ export async function onInit(defaultEngine) {
1297
1295
  "$rand",
1298
1296
  "$abs", '$sin', '$cos', '$tan', '$asin', '$acos', '$atan',
1299
1297
  "$toDate", "$toBool", "$toString", "$toInt", "$toDouble",
1300
- "$dateSubtract", "$dateAdd", "$dateToString",
1298
+ "$dateDiff", "$dateSubtract", "$dateAdd", "$dateToString",
1301
1299
  '$year', '$month', '$week', '$dayOfMonth', '$dayOfWeek', '$dayOfYear', '$hour', '$minute', '$second', '$millisecond'
1302
1300
  ]}));
1303
1301
 
@@ -1386,6 +1384,7 @@ export async function onInit(defaultEngine) {
1386
1384
  schedule.scheduleJob("0 2 * * *", jobDumpUserData);
1387
1385
  //await jobDumpUserData();
1388
1386
 
1387
+
1389
1388
  schedule.scheduleJob("0 0 * * *", async () => {
1390
1389
  const dt = new Date();
1391
1390
  dt.setTime(dt.getTime()-1000*3600*24*14);
@@ -1967,7 +1966,7 @@ export async function onInit(defaultEngine) {
1967
1966
  const filter = req.fields.filter;
1968
1967
  const hash = req.params.id; // Récupérer l'identifiant de la ressource à modifier
1969
1968
  const data = req.fields.data || (req.fields._data && JSON.parse(req.fields._data));
1970
- const r = await editData(req.fields.model, filter || hash, data, req.files, req.me)
1969
+ const r = await editData(req.fields.model, filter || { "$eq": ["$_id", { "$toObjectId": hash}]}, data, req.files, req.me)
1971
1970
  if (r.error)
1972
1971
  res.status(400).json(r);
1973
1972
  else
@@ -2014,7 +2013,6 @@ export async function onInit(defaultEngine) {
2014
2013
  .limit(maxModelsPerUser).toArray());
2015
2014
  res.json(models);
2016
2015
  } catch (error) {
2017
- console.log(error);
2018
2016
  logger.error(error);
2019
2017
  res.status(500).json({ success: false, error: error.message });
2020
2018
  }
@@ -2340,15 +2338,6 @@ export async function onInit(defaultEngine) {
2340
2338
  res.json({ success: true, value: resultValue, totalCount: totalCount });
2341
2339
 
2342
2340
  } catch (error) { // <--- CATCH PRINCIPAL
2343
- // --- Log de l'erreur BRUTE interceptée ---
2344
- console.log('--- CATCH PRINCIPAL - RAW ERROR OBJECT ---');
2345
- console.log('Type:', typeof error);
2346
- console.log('Instance of Error:', error instanceof Error);
2347
- console.log('Error Object:', error); // Affiche la structure brute
2348
- console.log('Error Message:', error?.message);
2349
- console.log('Error Stack:', error?.stack);
2350
- console.log('--- END CATCH PRINCIPAL ---');
2351
- // --- Fin Log ---
2352
2341
 
2353
2342
  // Tentative de log via le logger standard (qui peut encore échouer si l'erreur est vraiment étrange)
2354
2343
  try {
@@ -2889,14 +2878,14 @@ export const deleteModels = async (filter) => {
2889
2878
  }
2890
2879
 
2891
2880
  export const getModel = async (modelName, user) => {
2892
- const modelInCache = modelsCache.get(user.username+"@@"+modelName);
2881
+ const modelInCache = modelsCache.get((user?.username||'')+"@@"+modelName);
2893
2882
  if(modelInCache)
2894
2883
  return modelInCache;
2895
- const model = await getCollection('models').findOne({name: modelName, $and: [{_user: {$exists: true}}, {$or: [{_user: user._user}, {_user: user.username}]}]});
2884
+ const model = await getCollection('models').findOne({name: modelName, $and: user ? [{_user: {$exists: true}}, {$or: [{_user: user._user}, {_user: user.username}]}] : [{_user: { $exists: false}}]});
2896
2885
  if (!model) {
2897
2886
  throw new Error(i18n.t('api.model.notFound', {model: modelName}));
2898
2887
  }
2899
- modelsCache.set(user.username+"@@"+modelName, model);
2888
+ modelsCache.set((user?.username||'')+"@@"+modelName, model);
2900
2889
  return model;
2901
2890
  }
2902
2891
  export const getModels = async () => {
@@ -3244,7 +3233,8 @@ async function processDocuments(datas, model, collection, me) {
3244
3233
  const idMap = new Map();
3245
3234
  const allInsertedIds = [];
3246
3235
 
3247
- for (const doc of datas) {
3236
+ const realData = Event.Trigger("OnDataInsert", "event", "system", datas) || datas;
3237
+ for (const doc of realData) {
3248
3238
  try {
3249
3239
  const newDocId = await insertAndResolveRelations(doc, model, collection, me, idMap);
3250
3240
  if (newDocId) {
@@ -3445,10 +3435,12 @@ async function applyFieldFilters(docToProcess, model) {
3445
3435
  for (const field of model.fields) {
3446
3436
  docToProcess[field.name] = typeof(docToProcess[field.name]) === 'undefined' || docToProcess[field.name] === null ? field.default:docToProcess[field.name];
3447
3437
  if (dataTypes[field.type]?.filter) {
3448
- docToProcess[field.name] = await dataTypes[field.type].filter(
3438
+ const filter = await dataTypes[field.type].filter(
3449
3439
  docToProcess[field.name],
3450
3440
  field
3451
3441
  );
3442
+ const realFilter = Event.Trigger('OnDataFilter', "event", "system",filter, field, docToProcess );
3443
+ docToProcess[field.name] = realFilter || filter;
3452
3444
  }
3453
3445
  }
3454
3446
  }
@@ -3478,7 +3470,9 @@ function validateModelData(doc, model, isPatch = false) {
3478
3470
  if (!fieldDef) continue; // On ignore les champs supplémentaires
3479
3471
 
3480
3472
  const validator = dataTypes[fieldDef.type]?.validate;
3481
- if (validator && !validator(value, fieldDef)) {
3473
+ const valid = validator && validator(value, fieldDef);
3474
+ const realValidation = Event.Trigger('OnDataValidate', "event", "system", value, fieldDef,doc );
3475
+ if (!(valid || realValidation)) {
3482
3476
  throw new Error(i18n.t('api.field.validationFailed', { field: fieldName, value }));
3483
3477
  }
3484
3478
  }
@@ -3694,8 +3688,6 @@ export const editData = async (modelName, filter, data, files, user, triggerWork
3694
3688
  return await internalEditOrPatchData(modelName, filter, data, files, user, false, triggerWorkflow, waitForWorkflow);
3695
3689
  };
3696
3690
 
3697
- // Dans src/modules/data.js
3698
-
3699
3691
  const internalEditOrPatchData = async (modelName, filter, data, files, user, isPatch, triggerWorkflow = true, waitForWorkflow = false) => {
3700
3692
  try {
3701
3693
  // 1. Vérification des permissions
@@ -3949,7 +3941,6 @@ export const deleteData = async (modelName, filter, user ={}, triggerWorkflow, w
3949
3941
 
3950
3942
  }
3951
3943
 
3952
- console.log(util.inspect(findFilter, false, 8, true));
3953
3944
  // 2. Récupérer les documents à supprimer pour vérifier leur type et annuler les schedules
3954
3945
  const documentsToDelete = await collection.aggregate([{ $match: { $expr: { "$and": findFilter } } }]).toArray();
3955
3946
 
@@ -4130,12 +4121,10 @@ export const deleteData = async (modelName, filter, user ={}, triggerWorkflow, w
4130
4121
  }
4131
4122
  }
4132
4123
 
4133
- // ... (le reste du fichier data.js)
4134
-
4135
4124
  export const searchData = async (query, user) => {
4136
4125
  const { page, limit, sort, model, ids, timeout, pack } = query; // Les filtres de la requête (attention aux injections MongoDB !)
4137
4126
 
4138
- if( user.username !== 'demo' && isLocalUser(user) && (
4127
+ if( user && user.username !== 'demo' && isLocalUser(user) && (
4139
4128
  !await hasPermission(["API_ADMIN", "API_SEARCH_DATA", "API_SEARCH_DATA_"+model], user) ||
4140
4129
  await hasPermission(["API_SEARCH_DATA_NOT_"+model], user))){
4141
4130
  throw new Error(i18n.t('api.permission.searchData'));
@@ -4555,7 +4544,6 @@ export const searchData = async (query, user) => {
4555
4544
  } else if (fi.type === 'array') {
4556
4545
  // Handle array filtering here
4557
4546
  if (data[fi.name]) {
4558
- console.log('array filtering', data[fi.name]);
4559
4547
  pipelines.push({
4560
4548
  $match: {
4561
4549
  $expr: {
@@ -4591,7 +4579,6 @@ export const searchData = async (query, user) => {
4591
4579
 
4592
4580
  let pipelines = [];
4593
4581
  if( allIds.length ){
4594
- console.log({allIds});
4595
4582
  const id = {$in: ["$_id", allIds.map(m => new ObjectId(m))]};
4596
4583
  pipelines.push({
4597
4584
  $match: { $expr: id }
@@ -4617,7 +4604,7 @@ export const searchData = async (query, user) => {
4617
4604
  pipelines.push({$project: {_model: 0}});
4618
4605
  }
4619
4606
 
4620
- console.log(util.inspect(pipelines, false, 29, true));
4607
+ //console.log(util.inspect(pipelines, false, 29, true));
4621
4608
 
4622
4609
  // 4. Exécuter la pipeline
4623
4610
  const ts = parseInt(timeout, 10)/2.0 || searchRequestTimeout;
@@ -5787,7 +5774,6 @@ export async function installPack(packId, user, lang) {
5787
5774
  if (documents.length === 0) continue;
5788
5775
 
5789
5776
  const docsToInsert = [];
5790
- console.log(modelName, user);
5791
5777
 
5792
5778
  const modelDefForHash = await getModel(modelName, user);
5793
5779
 
@@ -5921,10 +5907,7 @@ export async function installPack(packId, user, lang) {
5921
5907
 
5922
5908
 
5923
5909
  export const installAllPacks = async () => {
5924
-
5925
5910
  const packs = await getAllPacks();
5926
-
5927
- console.log(util.inspect(packs, false, 20, true));
5928
5911
  await packsCollection.deleteMany({ _user: { $exists : false }});
5929
5912
  await packsCollection.insertMany(packs);
5930
5913
  }
@@ -5988,4 +5971,4 @@ export async function handleDemoInitialization(req, res) {
5988
5971
  logger.error(`[Demo Init] Critical error during initialization for user '${user.username}':`, error);
5989
5972
  res.status(500).json({ success: false, error: 'An internal server error occurred during initialization.' });
5990
5973
  }
5991
- }
5974
+ }
@@ -0,0 +1 @@
1
+ export * from './data.js';
@@ -8,7 +8,7 @@ import {getFileExtension, isGUID, uuidv4} from "../core.js";
8
8
  import path from "node:path";
9
9
  import process from "node:process";
10
10
  import fs from "node:fs";
11
- import { checkServerCapacity} from "./data.js";
11
+ import { checkServerCapacity} from "./data/index.js";
12
12
  import crypto from "node:crypto";
13
13
  import * as tar from "tar";
14
14
  import {promisify} from "node:util";
@@ -2,7 +2,6 @@
2
2
  import process from "process";
3
3
  import {Logger} from "../gameObject.js";
4
4
  import {MongoDatabase} from "../engine.js";
5
- import fs from "node:fs";
6
5
 
7
6
  export let modelsCollection, datasCollection, filesCollection, packsCollection;
8
7
 
@@ -3,7 +3,7 @@ import {MongoDatabase} from "../engine.js";
3
3
  import {getCollection, getCollectionForUser, getUserCollectionName} from "./mongodb.js";
4
4
  import {isLocalUser} from "../data.js";
5
5
  import {ObjectId} from "mongodb";
6
- import {getAPILang} from "./data.js";
6
+ import {getAPILang} from "./data/index.js";
7
7
  import {Logger} from "../gameObject.js";
8
8
  import rateLimit from "express-rate-limit";
9
9