data-primals-engine 1.0.11 → 1.1.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 (166) hide show
  1. package/client/README.md +8 -0
  2. package/client/index.html +31 -0
  3. package/client/index.js +25 -0
  4. package/client/package-lock.json +11664 -0
  5. package/client/package.json +70 -0
  6. package/client/public/demo/26899917-d1ba-4df4-bb33-48d09a8778da.jpg +0 -0
  7. package/client/public/demo/4b9894c7-12cd-466d-8fd3-a2757b09ec96.jpg +0 -0
  8. package/client/public/demo/7ed369ac-a1a2-4b45-b0cd-4fd5bcf5a75a.jpg +0 -0
  9. package/client/public/doc/API.postman_collection.json +214 -0
  10. package/client/public/github.svg +1 -0
  11. package/client/public/profilCompany.jpg +0 -0
  12. package/client/public/profilDeveloper.jpg +0 -0
  13. package/client/public/profilEngineer.jpg +0 -0
  14. package/client/public/profilPersonal.jpg +0 -0
  15. package/client/public/profils .psd +0 -0
  16. package/client/public/react.svg +9 -0
  17. package/client/src/APIInfo.jsx +328 -0
  18. package/client/src/AddWidgetTypeModal.jsx +44 -0
  19. package/client/src/AddWidgetTypeModal.scss +87 -0
  20. package/client/src/App.css +42 -0
  21. package/client/src/App.jsx +586 -0
  22. package/client/src/App.scss +1466 -0
  23. package/client/src/AssistantChat.jsx +280 -0
  24. package/client/src/AssistantChat.scss +229 -0
  25. package/client/src/Button.jsx +12 -0
  26. package/client/src/Button.scss +197 -0
  27. package/client/src/CalculationBuilder.jsx +338 -0
  28. package/client/src/CalculationStepRow.jsx +199 -0
  29. package/client/src/CalendarConfigModal.jsx +49 -0
  30. package/client/src/ChartConfigModal.jsx +402 -0
  31. package/client/src/ConditionBuilder.jsx +622 -0
  32. package/client/src/ConditionBuilder.scss +672 -0
  33. package/client/src/ConditionBuilder2.jsx +757 -0
  34. package/client/src/ContentView.jsx +274 -0
  35. package/client/src/CronBuilder.jsx +126 -0
  36. package/client/src/CronBuilder.scss +128 -0
  37. package/client/src/CronPartBuilder.jsx +150 -0
  38. package/client/src/Dashboard.jsx +210 -0
  39. package/client/src/Dashboard.scss +445 -0
  40. package/client/src/DashboardChart.jsx +362 -0
  41. package/client/src/DashboardFlexViewItem.jsx +129 -0
  42. package/client/src/DashboardView.jsx +554 -0
  43. package/client/src/DataChart.jsx +349 -0
  44. package/client/src/DataEditor.jsx +556 -0
  45. package/client/src/DataLayout.jsx +709 -0
  46. package/client/src/DataTable.jsx +996 -0
  47. package/client/src/DataTable.scss +73 -0
  48. package/client/src/Dialog.jsx +90 -0
  49. package/client/src/Dialog.scss +117 -0
  50. package/client/src/DisplayFlexNodeRenderer.jsx +115 -0
  51. package/client/src/DocumentationPageLayout.jsx +93 -0
  52. package/client/src/DocumentationPageLayout.scss +262 -0
  53. package/client/src/Draggable.jsx +222 -0
  54. package/client/src/Draggable.scss +49 -0
  55. package/client/src/ExportDialog.jsx +172 -0
  56. package/client/src/Field.jsx +1558 -0
  57. package/client/src/FlexBuilder.jsx +269 -0
  58. package/client/src/FlexBuilder.scss +226 -0
  59. package/client/src/FlexBuilderControls.jsx +170 -0
  60. package/client/src/FlexBuilderModal.jsx +75 -0
  61. package/client/src/FlexBuilderModal.scss +109 -0
  62. package/client/src/FlexBuilderPreview.jsx +48 -0
  63. package/client/src/FlexBuilderUtils.js +84 -0
  64. package/client/src/FlexDataRenderer.jsx +170 -0
  65. package/client/src/FlexDataRenderer.scss +80 -0
  66. package/client/src/FlexNode.jsx +290 -0
  67. package/client/src/FlexTreeUtils.js +129 -0
  68. package/client/src/HiddenableCell.jsx +46 -0
  69. package/client/src/HiddenableCell.scss +36 -0
  70. package/client/src/KPIDialog.jsx +39 -0
  71. package/client/src/KPIWidget.jsx +139 -0
  72. package/client/src/KanbanCard.jsx +92 -0
  73. package/client/src/KanbanColumn.jsx +29 -0
  74. package/client/src/KanbanConfigModal.jsx +115 -0
  75. package/client/src/KanbanView.jsx +113 -0
  76. package/client/src/KanbanView.scss +107 -0
  77. package/client/src/MessageRotator.jsx +77 -0
  78. package/client/src/MessageRotator.scss +14 -0
  79. package/client/src/ModelCreator.jsx +640 -0
  80. package/client/src/ModelCreator.scss +154 -0
  81. package/client/src/ModelCreatorField.jsx +810 -0
  82. package/client/src/ModelImporter.jsx +120 -0
  83. package/client/src/ModelList.jsx +205 -0
  84. package/client/src/Notification.jsx +137 -0
  85. package/client/src/Notification.scss +126 -0
  86. package/client/src/NotificationProvider.jsx +73 -0
  87. package/client/src/PackGallery.jsx +211 -0
  88. package/client/src/PackGallery.scss +156 -0
  89. package/client/src/Pagination.jsx +141 -0
  90. package/client/src/RTE.jsx +415 -0
  91. package/client/src/RTETrans.jsx +103 -0
  92. package/client/src/RelationField.jsx +247 -0
  93. package/client/src/RelationValue.jsx +230 -0
  94. package/client/src/RestoreConfirmationModal.jsx +183 -0
  95. package/client/src/RestoreDialog.jsx +134 -0
  96. package/client/src/RestoreDialog.scss +67 -0
  97. package/client/src/RichTextEditorModal.jsx +34 -0
  98. package/client/src/RichTextEditorModal.scss +54 -0
  99. package/client/src/TourSpotlight.jsx +234 -0
  100. package/client/src/TourSpotlight.scss +100 -0
  101. package/client/src/TutorialsMenu.jsx +83 -0
  102. package/client/src/TutorialsMenu.scss +217 -0
  103. package/client/src/TutorialsRewardModal.jsx +65 -0
  104. package/client/src/Utils.jsx +78 -0
  105. package/client/src/ViewSwitcher.jsx +54 -0
  106. package/client/src/ViewSwitcher.scss +74 -0
  107. package/client/src/Webpage.jsx +69 -0
  108. package/client/src/_variables.scss +21 -0
  109. package/client/src/assets/react.svg +1 -0
  110. package/client/src/constants.js +68 -0
  111. package/client/src/contexts/AuthContext.jsx +28 -0
  112. package/client/src/contexts/ModelContext.jsx +328 -0
  113. package/client/src/contexts/UIContext.jsx +52 -0
  114. package/client/src/core/data.js +35 -0
  115. package/client/src/entry-client.jsx +15 -0
  116. package/client/src/entry-server.jsx +13 -0
  117. package/client/src/filter.js +222 -0
  118. package/client/src/hooks/data.js +101 -0
  119. package/client/src/hooks/useDebounce.js +20 -0
  120. package/client/src/hooks/useDragAndDrop.js +59 -0
  121. package/client/src/hooks/useLocalStorage.js +100 -0
  122. package/client/src/hooks/useMount.js +42 -0
  123. package/client/src/hooks/useTutorials.jsx +227 -0
  124. package/client/src/hooks/useWindowSize.js +24 -0
  125. package/client/src/i18n.js +47 -0
  126. package/client/src/index.css +90 -0
  127. package/client/src/index.js +10 -0
  128. package/client/src/main.jsx +11 -0
  129. package/client/src/translations.js +15940 -0
  130. package/client/src/tutorials.js +112 -0
  131. package/client/vite.config.js +30 -0
  132. package/eslint.config.js +50 -0
  133. package/package.json +48 -27
  134. package/server.js +9 -0
  135. package/src/constants.js +12 -23
  136. package/src/core.js +9 -8
  137. package/src/data.js +81 -80
  138. package/src/defaultModels.js +114 -115
  139. package/src/email.js +8 -9
  140. package/src/engine.js +5 -5
  141. package/src/events.js +1 -2
  142. package/src/i18n.js +212 -211
  143. package/src/middlewares/middleware-mongodb.js +94 -93
  144. package/src/migrate.js +24 -24
  145. package/src/modules/assistant.js +55 -55
  146. package/src/modules/bucket.js +10 -9
  147. package/src/modules/data.js +387 -357
  148. package/src/modules/file.js +3 -6
  149. package/src/modules/mongodb.js +6 -7
  150. package/src/modules/user.js +24 -47
  151. package/src/modules/workflow.js +68 -67
  152. package/src/openai.jobs.js +2 -2
  153. package/src/packs.js +30 -31
  154. package/src/providers.js +101 -3
  155. package/src/setenv.js +1 -1
  156. package/src/user.js +0 -17
  157. package/src/workers/crypto-worker.js +21 -20
  158. package/src/workers/import-export-worker.js +18 -18
  159. package/test/data.backup.integration.test.js +7 -12
  160. package/test/data.integration.test.js +27 -25
  161. package/test/globalSetup.js +2 -2
  162. package/test/import_export.integration.test.js +6 -6
  163. package/test/model.integration.test.js +21 -22
  164. package/test/workflow.integration.test.js +2 -5
  165. package/test/workflow.robustness.test.js +6 -13
  166. package/vitest.config.js +2 -2
@@ -3,7 +3,6 @@
3
3
  import {maxPrivateFileSize, megabytes} from "../constants.js";
4
4
  import {isLocalUser} from "../data.js";
5
5
  import i18n from "data-primals-engine/i18n";
6
- import {getUserStorageLimit} from "../user.js";
7
6
  import {getCollection} from "./mongodb.js";
8
7
  import {getFileExtension, isGUID, uuidv4} from "../core.js";
9
8
  import path from "node:path";
@@ -14,9 +13,6 @@ import crypto from "node:crypto";
14
13
  import * as tar from "tar";
15
14
  import {promisify} from "node:util";
16
15
  import {calculateTotalUserStorageUsage, hasPermission} from "./user.js";
17
- import {Logger} from "../gameObject.js";
18
- import YAML from "yaml";
19
- import swaggerUi from "swagger-ui-express";
20
16
 
21
17
  const pbkdf2Async = promisify(crypto.pbkdf2);
22
18
 
@@ -56,7 +52,7 @@ export const addFile = async (file, user) => {
56
52
 
57
53
  const incomingDataSize = file.size;
58
54
 
59
- const userStorageLimit = getUserStorageLimit(user);
55
+ const userStorageLimit = await engine.userProvider.getUserStorageLimit(user);
60
56
  const currentStorageUsage = await calculateTotalUserStorageUsage(user);
61
57
 
62
58
  if (currentStorageUsage + incomingDataSize > userStorageLimit) {
@@ -194,6 +190,7 @@ export async function decryptFile(filePath, password) {
194
190
  }
195
191
  }
196
192
 
193
+ let engine;
197
194
  export async function onInit(defaultEngine) {
198
-
195
+ engine = defaultEngine;
199
196
  }
@@ -1,9 +1,7 @@
1
1
 
2
2
  import process from "process";
3
- import {MongoClient as InternalMongoClient} from "mongodb";
4
3
  import {Logger} from "../gameObject.js";
5
- import {MongoClient, MongoDatabase} from "../engine.js";
6
- import * as tls from "node:tls";
4
+ import {MongoDatabase} from "../engine.js";
7
5
  import fs from "node:fs";
8
6
 
9
7
  export let modelsCollection, datasCollection, filesCollection, packsCollection;
@@ -46,14 +44,15 @@ export const getCollection = (str) => {
46
44
 
47
45
 
48
46
  // New function to determine the collection name for a user
49
- export const getUserCollectionName = (user) => {
50
- return user?.userPlan === 'premium' ? `datas_${user.username}` : 'datas';
47
+ export const getUserCollectionName = async (user) => {
48
+ const feat = await engine.userProvider.hasFeature(user, 'indexes');
49
+ return feat ? `datas_${user.username}` : 'datas';
51
50
  };
52
51
 
53
52
 
54
53
  // Modify existing functions to use the correct collection
55
- export const getCollectionForUser = (user) => {
56
- const collectionName = getUserCollectionName(user);
54
+ export const getCollectionForUser = async (user) => {
55
+ const collectionName = await getUserCollectionName(user);
57
56
  return getCollection(collectionName);
58
57
  };
59
58
 
@@ -1,7 +1,6 @@
1
1
  import i18n from "data-primals-engine/i18n";
2
- import {MongoClient, MongoDatabase} from "../engine.js";
2
+ import {MongoDatabase} from "../engine.js";
3
3
  import {getCollection, getCollectionForUser, getUserCollectionName} from "./mongodb.js";
4
- import {dbName, plans} from "../constants.js";
5
4
  import {isLocalUser} from "../data.js";
6
5
  import {ObjectId} from "mongodb";
7
6
  import {getAPILang} from "./data.js";
@@ -20,10 +19,10 @@ export const userInitiator = async (req, res, next) => {
20
19
  // set current lang for user
21
20
  i18n.changeLanguage(lang);
22
21
 
23
- if (req.me.userPlan === 'premium') {
22
+ if (await engine.userProvider.hasFeature(req.me, 'indexes')) {
24
23
  const collections = await MongoDatabase.listCollections().toArray();
25
24
  const collectionNames = collections.map(c => c.name);
26
- const coll = getUserCollectionName(req.me);
25
+ const coll = await getUserCollectionName(req.me);
27
26
  if (collectionNames.includes(coll)) {
28
27
  const collection = await MongoDatabase.createCollection(coll);
29
28
  const indexes = await collection.indexes();
@@ -83,33 +82,11 @@ export const generateLimiter = rateLimit({
83
82
  }
84
83
  });
85
84
 
86
- const freeSearchLimiter = rateLimit({
87
- windowMs: 1000 * 3600,
88
- limit: plans.free.requestLimitPerHour,
89
- standardHeaders: true,
90
- legacyHeaders: false,
91
- });
92
- const premiumSearchLimiter = rateLimit({
93
- windowMs: 1000 * 3600,
94
- limit: plans.premium.requestLimitPerHour,
95
- standardHeaders: true,
96
- legacyHeaders: false,
97
- });
98
-
99
- export const myFreePremiumAnonymousLimiter = async function (req, res, next) {
100
- const user = req.me;
101
- if (user?.userPlan === "premium") {
102
- return premiumSearchLimiter(req, res, next);
103
- }
104
- return freeSearchLimiter(req, res, next);
105
- };
106
85
 
107
86
  let logger,engine;
108
87
  export async function onInit(defaultEngine) {
109
88
  engine = defaultEngine;
110
89
  logger = engine.getComponent(Logger);
111
-
112
-
113
90
  }
114
91
 
115
92
  export async function hasPermission(permissionNames, user) {
@@ -119,7 +96,7 @@ export async function hasPermission(permissionNames, user) {
119
96
  try {
120
97
  // Si on a une string on le transforme en tableau.
121
98
  const permissionNamesArray = Array.isArray(permissionNames) ? permissionNames : [permissionNames];
122
- const collection = getCollectionForUser(user);
99
+ const collection = await getCollectionForUser(user);
123
100
 
124
101
  const job = [
125
102
  {
@@ -131,39 +108,39 @@ export async function hasPermission(permissionNames, user) {
131
108
  $match: {
132
109
  $expr: {
133
110
  $and: [
134
- { $in: ['$_id', '$$rolesIds'] },
135
- ],
136
- },
137
- },
111
+ { $in: ['$_id', '$$rolesIds'] }
112
+ ]
113
+ }
114
+ }
138
115
  },
139
116
  {
140
117
  $lookup: {
141
118
  from: 'datas',
142
119
  let: { rolePermissions: {
143
- "$map": {
144
- "input": "$permissions",
145
- "in": { "$toObjectId": "$$this" }
146
- }
147
- } },
120
+ "$map": {
121
+ "input": "$permissions",
122
+ "in": { "$toObjectId": "$$this" }
123
+ }
124
+ } },
148
125
  pipeline: [
149
126
  {
150
127
  $match: {
151
128
  $expr: {
152
129
  $and: [
153
- { $in: ['$_id', '$$rolePermissions'] },
154
- ],
155
- },
156
- },
130
+ { $in: ['$_id', '$$rolePermissions'] }
131
+ ]
132
+ }
133
+ }
157
134
  },
158
- { $limit: 1 },
135
+ { $limit: 1 }
159
136
  ],
160
- as: 'permissions',
161
- },
137
+ as: 'permissions'
138
+ }
162
139
  },
163
- { $unwind: { path: '$permissions', preserveNullAndEmptyArrays: true } },
140
+ { $unwind: { path: '$permissions', preserveNullAndEmptyArrays: true } }
164
141
  ],
165
- as: 'roles',
166
- },
142
+ as: 'roles'
143
+ }
167
144
  },
168
145
  { $unwind: { path: '$roles', preserveNullAndEmptyArrays: true } },
169
146
  { $match: { 'roles.permissions.name': { $in: permissionNamesArray } } }, // Match if permissions.name in array
@@ -186,7 +163,7 @@ export async function hasPermission(permissionNames, user) {
186
163
  */
187
164
  export async function calculateTotalUserStorageUsage(user) {
188
165
  const userId = user._user || user.username;
189
- const datasCollection = getCollectionForUser(user);
166
+ const datasCollection = await getCollectionForUser(user);
190
167
  const filesCollection = getCollection("files");
191
168
 
192
169
  // Pipeline pour calculer la taille des documents de données
@@ -131,7 +131,7 @@ export async function scheduleWorkflowTriggers() {
131
131
  // Trouver tous les workflows actifs avec une cronExpression définie
132
132
  const workflowsToSchedule = await datasCollection.find({
133
133
  _model: 'workflowTrigger',
134
- cronExpression: { $exists: true, $ne: "" },
134
+ cronExpression: { $exists: true, $ne: "" }
135
135
  // Ajoutez d'autres conditions si nécessaire (ex: active: true)
136
136
  }).toArray();
137
137
 
@@ -270,7 +270,7 @@ async function handleWebhookAction(actionDef, contextData, user, dbCollection) {
270
270
  // 4. Prepare Fetch Options
271
271
  const fetchOptions = {
272
272
  method: method.toUpperCase(),
273
- headers: headersObject, // Native fetch accepts an object directly
273
+ headers: headersObject // Native fetch accepts an object directly
274
274
  };
275
275
 
276
276
  if (bodyObject !== null && ['POST', 'PUT', 'PATCH'].includes(fetchOptions.method)) {
@@ -649,32 +649,33 @@ export async function executeStepAction(actionDef, contextData, user, dbCollecti
649
649
  try {
650
650
  let result;
651
651
  switch (actionDef.type) {
652
- case 'Log':
653
- logger.info(`[Workflow Log Action] Action: ${actionDef.name}. Contexte:`, contextData);
654
- result = { success: true, message: 'Log action executed successfully.' }; // <--- CORRECTION
655
- break;
656
- case 'Webhook':
657
- result = await handleWebhookAction(actionDef, contextData, user, dbCollection);
658
- break;
659
- case 'CreateData':
660
- result = await handleCreateDataAction(actionDef, contextData, user, dbCollection);
661
- break;
662
- case 'UpdateData':
663
- result = await handleUpdateDataAction(actionDef, contextData, user);
664
- break;
665
- case 'DeleteData':
666
- result = await handleDeleteDataAction(actionDef, contextData, user, dbCollection);
667
- break;
668
- case 'GenerateAIContent':
669
- result = await executeGenerateAIContentAction(actionDef, contextData, user);
670
- break;
671
- case 'SendEmail':
672
- result = await handleSendEmailAction(actionDef, contextData, user);
673
- break;
652
+ case 'Log':
653
+ logger.info(`[Workflow Log Action] Action: ${actionDef.name}. Contexte:`, contextData);
654
+ result = { success: true, message: 'Log action executed successfully.' }; // <--- CORRECTION
655
+ break;
656
+ case 'Webhook':
657
+ result = await handleWebhookAction(actionDef, contextData, user, dbCollection);
658
+ break;
659
+ case 'CreateData':
660
+ result = await handleCreateDataAction(actionDef, contextData, user, dbCollection);
661
+ break;
662
+ case 'UpdateData':
663
+ result = await handleUpdateDataAction(actionDef, contextData, user);
664
+ break;
665
+ case 'DeleteData':
666
+ result = await handleDeleteDataAction(actionDef, contextData, user, dbCollection);
667
+ break;
668
+ case 'GenerateAIContent':
669
+ result = await executeGenerateAIContentAction(actionDef, contextData, user);
670
+ break;
671
+ case 'SendEmail':
672
+ result = await handleSendEmailAction(actionDef, contextData, user);
673
+ break;
674
+
674
675
  // ... autres cases à venir ...
675
- default:
676
- logger.error(`[executeStepAction] Unknown action type: ${actionDef.type}`);
677
- return { success: false, message: `Unknown action type: ${actionDef.type}` };
676
+ default:
677
+ logger.error(`[executeStepAction] Unknown action type: ${actionDef.type}`);
678
+ return { success: false, message: `Unknown action type: ${actionDef.type}` };
678
679
  }
679
680
  return result;
680
681
  } catch (error) {
@@ -749,7 +750,7 @@ async function resolvePathValue(pathString, initialContext, user) {
749
750
 
750
751
  let currentModelName = initialContext._model;
751
752
  let currentDocId = new ObjectId(initialContext._id);
752
- const collection = getCollectionForUser(user);
753
+ const collection = await getCollectionForUser(user);
753
754
 
754
755
  // Construire le pipeline d'agrégation
755
756
  const pipeline = [
@@ -862,7 +863,7 @@ export async function substituteVariables(template, contextData, user) {
862
863
  // --- À partir d'ici, nous savons que `template` est une chaîne de caractères ---
863
864
 
864
865
  // 4. Construire le contexte complet pour la substitution
865
- const dbCollection = getCollectionForUser(user);
866
+ const dbCollection = await getCollectionForUser(user);
866
867
  const userEnvVars = await dbCollection.find({ _model: 'env', _user: user.username }).toArray();
867
868
  const userEnv = userEnvVars.reduce((acc, v) => ({ ...acc, [v.name]: v.value }), {});
868
869
 
@@ -972,7 +973,7 @@ export async function triggerWorkflows(triggerData, user, eventType) {
972
973
  console.log(`[Workflow Trigger] Event: ${eventType}, Model: ${targetModelName}${dataId ? `, Data ID: ${dataId}` : ''}, User: ${user.username}`);
973
974
 
974
975
  try {
975
- const dbCollection = getCollectionForUser(user); // Collection des données utilisateur
976
+ const dbCollection = await getCollectionForUser(user); // Collection des données utilisateur
976
977
 
977
978
  // 1. Trouver les WorkflowTriggers pertinents
978
979
  const workflowTriggers = await dbCollection.find({
@@ -1008,29 +1009,29 @@ export async function triggerWorkflows(triggerData, user, eventType) {
1008
1009
  continue; // Passer au trigger suivant si le filtre est invalide
1009
1010
  }
1010
1011
 
1011
- try {
1012
- const finalFilter = {
1013
- '$and': [
1014
- dataFilterCondition // Applique la condition du trigger
1015
- ]
1016
- };
1017
-
1018
- console.debug(`[Workflow Trigger] Vérification dataFilter pour trigger ${trigger._id} avec filtre combiné:`, JSON.stringify(finalFilter));
1019
-
1020
- // Exécuter la vérification dans la base de données
1021
- // Utilisation de countDocuments pour une vérification rapide
1022
- const matchCount = await searchData({ user, query: { model: targetModelName, filter: finalFilter, limit: 1 } });
1023
-
1024
- if (!matchCount.count) {
1025
- console.debug(`[Workflow Trigger] Trigger ${trigger._id}: dataFilter non satisfait par la donnée ${dataId}. WorkflowRun non créé.`);
1026
- continue; // Passer au trigger suivant
1027
- } else {
1028
- console.debug(`[Workflow Trigger] Trigger ${trigger._id}: dataFilter satisfait par la donnée ${dataId}.`);
1029
- }
1030
- } catch (filterError) {
1031
- console.error(`[Workflow Trigger] Erreur lors de la conversion ou de l'exécution du dataFilter pour le trigger ${trigger._id}:`, filterError);
1032
- continue; // Ne pas créer en cas d'erreur de filtre
1012
+ try {
1013
+ const finalFilter = {
1014
+ '$and': [
1015
+ dataFilterCondition // Applique la condition du trigger
1016
+ ]
1017
+ };
1018
+
1019
+ console.debug(`[Workflow Trigger] Vérification dataFilter pour trigger ${trigger._id} avec filtre combiné:`, JSON.stringify(finalFilter));
1020
+
1021
+ // Exécuter la vérification dans la base de données
1022
+ // Utilisation de countDocuments pour une vérification rapide
1023
+ const matchCount = await searchData({ user, query: { model: targetModelName, filter: finalFilter, limit: 1 } });
1024
+
1025
+ if (!matchCount.count) {
1026
+ console.debug(`[Workflow Trigger] Trigger ${trigger._id}: dataFilter non satisfait par la donnée ${dataId}. WorkflowRun non créé.`);
1027
+ continue; // Passer au trigger suivant
1028
+ } else {
1029
+ console.debug(`[Workflow Trigger] Trigger ${trigger._id}: dataFilter satisfait par la donnée ${dataId}.`);
1033
1030
  }
1031
+ } catch (filterError) {
1032
+ console.error(`[Workflow Trigger] Erreur lors de la conversion ou de l'exécution du dataFilter pour le trigger ${trigger._id}:`, filterError);
1033
+ continue; // Ne pas créer en cas d'erreur de filtre
1034
+ }
1034
1035
  } // Fin de la vérification dataFilter
1035
1036
 
1036
1037
  // 4. Si les filtres (eventType, dataFilter) sont passés, créer l'instance workflowRun
@@ -1087,9 +1088,9 @@ export async function triggerWorkflows(triggerData, user, eventType) {
1087
1088
  }
1088
1089
 
1089
1090
  return new Promise((resolve) => setTimeout(async () => {
1090
- await trigger(triggerData, user, eventType);
1091
- resolve();
1092
- }, 0)
1091
+ await trigger(triggerData, user, eventType);
1092
+ resolve();
1093
+ }, 0)
1093
1094
  );
1094
1095
  }
1095
1096
 
@@ -1105,7 +1106,7 @@ export async function triggerWorkflows(triggerData, user, eventType) {
1105
1106
  // C:/Dev/hackersonline-engine/server/src/modules/workflow.js
1106
1107
 
1107
1108
  export async function processWorkflowRun(workflowRunId, user) {
1108
- const dbCollection = getCollectionForUser(user);
1109
+ const dbCollection = await getCollectionForUser(user);
1109
1110
  const runId = typeof workflowRunId === 'string' ? new ObjectId(workflowRunId) : workflowRunId;
1110
1111
 
1111
1112
  logger.info(`[processWorkflowRun] Starting processing for workflowRun ID: ${runId}`);
@@ -1290,7 +1291,7 @@ async function executeGenerateAIContentAction(action, context, user) {
1290
1291
 
1291
1292
  const providers = {
1292
1293
  "OpenAI" : "OPENAI_API_KEY",
1293
- "Google": "GOOGLE_API_KEY",
1294
+ "Google": "GOOGLE_API_KEY"
1294
1295
  }
1295
1296
  const envKeyName = providers[aiProvider];
1296
1297
  if( !envKeyName ) {
@@ -1298,7 +1299,7 @@ async function executeGenerateAIContentAction(action, context, user) {
1298
1299
  }
1299
1300
 
1300
1301
  // Cherche d'abord dans les variables d'environnement de l'utilisateur
1301
- const envCollection = getCollectionForUser(user);
1302
+ const envCollection = await getCollectionForUser(user);
1302
1303
  const userEnvVar = await envCollection.findOne({ _model: 'env', name: envKeyName, _user: user.username });
1303
1304
 
1304
1305
  if (userEnvVar && userEnvVar.value) {
@@ -1319,14 +1320,14 @@ async function executeGenerateAIContentAction(action, context, user) {
1319
1320
  let llm;
1320
1321
  try {
1321
1322
  switch (aiProvider) {
1322
- case 'OpenAI':
1323
- llm = new ChatOpenAI({ apiKey, modelName: aiModel, temperature: 0.7 });
1324
- break;
1325
- case 'GoogleGemini':
1326
- llm = new ChatGoogleGenerativeAI({ apiKey, modelName: aiModel, temperature: 0.7 });
1327
- break;
1328
- default:
1329
- throw new Error(`Fournisseur IA non supporté : ${aiProvider}`);
1323
+ case 'OpenAI':
1324
+ llm = new ChatOpenAI({ apiKey, modelName: aiModel, temperature: 0.7 });
1325
+ break;
1326
+ case 'GoogleGemini':
1327
+ llm = new ChatGoogleGenerativeAI({ apiKey, modelName: aiModel, temperature: 0.7 });
1328
+ break;
1329
+ default:
1330
+ throw new Error(`Fournisseur IA non supporté : ${aiProvider}`);
1330
1331
  }
1331
1332
  } catch (initError) {
1332
1333
  const message = `Échec de l'initialisation du client IA pour ${aiProvider}: ${initError.message}`;
@@ -1413,7 +1414,7 @@ async function handleSendEmailAction(action, triggerData, user) {
1413
1414
  // 3. Préparer les données pour sendEmail
1414
1415
  const emailData = {
1415
1416
  title: rsubject,
1416
- content: rbody,
1417
+ content: rbody
1417
1418
  };
1418
1419
 
1419
1420
  await sendEmail(rto, emailData, smtpConfig, user.lang);
@@ -121,10 +121,10 @@ export const openaiJobModel = async (lang, txt, history, existingModels = []) =>
121
121
  },
122
122
  {
123
123
  role: "user",
124
- content: typeof(txt) === 'string' ? txt.substring(0, 4096) : '',
124
+ content: typeof(txt) === 'string' ? txt.substring(0, 4096) : ''
125
125
  },
126
126
  ...history
127
- ],
127
+ ]
128
128
  });
129
129
 
130
130
  const aiResponse = completion.choices[0].message.content;
package/src/packs.js CHANGED
@@ -135,21 +135,20 @@ export const getAllPacks = async () => {
135
135
  }
136
136
  ],
137
137
  "dashboard": [{
138
- name: "Business Overview",
139
- description: "Displays the total revenue, total orders, and average order value of the store.",
140
- layout: [{
141
- "name": "Store Overview",
142
- "kpis": ["Total Orders", "Average Order Value", "Total Revenue"],
143
- "chartConfigs": [],
144
- "flexViews": []
145
- }],
146
- settings: {
147
- "defaultTimeRange": "last_7_days",
148
- "refreshInterval": null
149
- }
138
+ name: "Business Overview",
139
+ description: "Displays the total revenue, total orders, and average order value of the store.",
140
+ layout: [{
141
+ "name": "Store Overview",
142
+ "kpis": ["Total Orders", "Average Order Value", "Total Revenue"],
143
+ "chartConfigs": [],
144
+ "flexViews": []
145
+ }],
146
+ settings: {
147
+ "defaultTimeRange": "last_7_days",
148
+ "refreshInterval": null
150
149
  }
151
- ],
152
- "order": [
150
+ }],
151
+ "order": [
153
152
  {
154
153
  "orderId": "ORDER-0001",
155
154
  "orderDate": "2023-10-27T10:00:00Z",
@@ -275,13 +274,13 @@ export const getAllPacks = async () => {
275
274
  "name": "Validate Order",
276
275
  "workflow": { "$link": { "name": "Order Fulfillment", "_model": "workflow" } },
277
276
  "actions": { "$link": { "name": "Update order status to 'processing'", "_model": "workflowAction" } },
278
- "onSuccessStep": { "$link": { "name": "Prepare Shipment", "_model": "workflowStep" } },
277
+ "onSuccessStep": { "$link": { "name": "Prepare Shipment", "_model": "workflowStep" } }
279
278
  },
280
279
  {
281
280
  "name": "Prepare Shipment",
282
281
  "workflow": { "$link": { "name": "Order Fulfillment", "_model": "workflow" }},
283
282
  "actions": { "$link": { "name": "Create Shipment Record", "_model": "workflowAction" } },
284
- "onSuccessStep": { "$link": { "name": "Ship Order", "_model": "workflowStep" } },
283
+ "onSuccessStep": { "$link": { "name": "Ship Order", "_model": "workflowStep" } }
285
284
  },
286
285
  {
287
286
  "name": "Send Shipment Email Step",
@@ -296,7 +295,7 @@ export const getAllPacks = async () => {
296
295
  "$link": {
297
296
  "$or": [
298
297
  { "$eq": ["$name", "Update order status to 'shipped'"]},
299
- { "$eq": ["$name", "Send Shipping Confirmation"]},
298
+ { "$eq": ["$name", "Send Shipping Confirmation"]}
300
299
  ],
301
300
  "_model": "workflowAction"
302
301
  }
@@ -374,17 +373,17 @@ export const getAllPacks = async () => {
374
373
  type: 'category',
375
374
  parent: {
376
375
  "$find": {
377
- "name": "Website",
376
+ "name": "Website"
378
377
  }
379
378
  }
380
379
  })), ...tags.map(t =>({
381
380
  name: t,
382
- type: 'keyword',
381
+ type: 'keyword'
383
382
  }))],
384
383
  "permission": getPerms(['website', 'taxonomy', 'lang', 'user', 'role', 'permission']).map(m => {
385
384
  return {
386
385
  name: m,
387
- description: '',
386
+ description: ''
388
387
  }
389
388
  }),
390
389
  "role": roles.map(m => {
@@ -399,8 +398,8 @@ export const getAllPacks = async () => {
399
398
  }),
400
399
  "channel": [
401
400
  { "name": "Visitor alerts", "type": "web", "description": "Visitor main channel informations" },
402
- { "name": "Newsletter", "type": "email", "description": "Newsletter channel" },
403
- ],
401
+ { "name": "Newsletter", "type": "email", "description": "Newsletter channel" }
402
+ ]
404
403
  },
405
404
  "fr": {
406
405
  "lang": [{
@@ -408,8 +407,8 @@ export const getAllPacks = async () => {
408
407
  "code": "fr"
409
408
  }],
410
409
  "translation": [
411
- { "lang": { "$find": { "$eq": ["$code", "fr"]}}, "key": "Visitor alerts", "value": "Alertes visiteurs" },
412
- ],
410
+ { "lang": { "$find": { "$eq": ["$code", "fr"]}}, "key": "Visitor alerts", "value": "Alertes visiteurs" }
411
+ ]
413
412
  }
414
413
  }
415
414
  },
@@ -548,7 +547,7 @@ export const getAllPacks = async () => {
548
547
  { "lang": { "$link": { "code": "fr", "_model": "lang" } }, "key": "model_description_deal", "value": "Représente une opportunité commerciale avec un contact ou une entreprise." },
549
548
 
550
549
  { "lang": { "$link": { "code": "fr", "_model": "lang" } }, "key": "model_interaction", "value": "Interactions" },
551
- { "lang": { "$link": { "code": "fr", "_model": "lang" } }, "key": "model_description_interaction", "value": "Représente une interaction (appel, email, rdv) avec un contact ou une entreprise." },
550
+ { "lang": { "$link": { "code": "fr", "_model": "lang" } }, "key": "model_description_interaction", "value": "Représente une interaction (appel, email, rdv) avec un contact ou une entreprise." }
552
551
  ]
553
552
  },
554
553
  "en": {
@@ -595,7 +594,7 @@ export const getAllPacks = async () => {
595
594
  { "lang": { "$link": { "code": "en", "_model": "lang" } }, "key": "model_description_deal", "value": "Represents a business opportunity with a contact or company." },
596
595
 
597
596
  { "lang": { "$link": { "code": "en", "_model": "lang" } }, "key": "model_interaction", "value": "Interactions" },
598
- { "lang": { "$link": { "code": "en", "_model": "lang" } }, "key": "model_description_interaction", "value": "Represents an interaction (call, email, meeting) with a contact or company." },
597
+ { "lang": { "$link": { "code": "en", "_model": "lang" } }, "key": "model_description_interaction", "value": "Represents an interaction (call, email, meeting) with a contact or company." }
599
598
  ]
600
599
  },
601
600
  "it": {
@@ -1075,14 +1074,14 @@ export const getAllPacks = async () => {
1075
1074
  "type": "GenerateAIContent",
1076
1075
  "aiProvider": "OpenAI",
1077
1076
  "aiModel": "gpt-4o-mini",
1078
- "prompt": "Write a short, SEO-optimized product description (approximately 30-40 words) for the following product: '{triggerData.name}'. The description should be engaging and highlight the product. Return only the description text, without an introduction or conclusion.",
1077
+ "prompt": "Write a short, SEO-optimized product description (approximately 30-40 words) for the following product: '{triggerData.name}'. The description should be engaging and highlight the product. Return only the description text, without an introduction or conclusion."
1079
1078
  },
1080
1079
  {
1081
1080
  "name": "Generate SEO Description from Product (Google API)",
1082
1081
  "type": "GenerateAIContent",
1083
1082
  "aiProvider": "Google",
1084
1083
  "aiModel": "gemini-2.0-flash",
1085
- "prompt": "Write a short, SEO-optimized product description (approximately 30-40 words) for the following product: '{triggerData.name}'. The description should be engaging and highlight the product. Return only the description text, without an introduction or conclusion.",
1084
+ "prompt": "Write a short, SEO-optimized product description (approximately 30-40 words) for the following product: '{triggerData.name}'. The description should be engaging and highlight the product. Return only the description text, without an introduction or conclusion."
1086
1085
  },
1087
1086
  {
1088
1087
  "name": "Update Product with AI Description",
@@ -1100,7 +1099,7 @@ export const getAllPacks = async () => {
1100
1099
  "$find": {
1101
1100
  $or: [
1102
1101
  {"$eq": ["$name", "Generate SEO Description from Product (OpenAI API)"]},
1103
- {"$eq": ["$name", "Update Product with AI Description"]},
1102
+ {"$eq": ["$name", "Update Product with AI Description"]}
1104
1103
  ]
1105
1104
  }
1106
1105
  },
@@ -2300,7 +2299,7 @@ export const getAllPacks = async () => {
2300
2299
  { name:"Nouveau manat turkmène",code:"TMT", symbol: "m" },
2301
2300
  { name:"Som ouzbek",code:"UZS", symbol: "som" },
2302
2301
  { name:"Dong vietnamien",code:"VND", symbol: "₫" }
2303
- ],
2302
+ ]
2304
2303
  },
2305
2304
  "fr":{
2306
2305
  "lang": [{
@@ -3807,7 +3806,7 @@ export const getAllPacks = async () => {
3807
3806
  { "lang": { "$link": { "code": "sv", "_model": "lang" } }, "key": "Som ouzbek", "value": "Uzbekisk sum" },
3808
3807
  { "lang": { "$link": { "code": "sv", "_model": "lang" } }, "key": "Dong vietnamien", "value": "Vietnamesisk dong" }
3809
3808
  ]
3810
- },
3809
+ }
3811
3810
 
3812
3811
  }
3813
3812
  }