data-primals-engine 1.0.11 → 1.1.1
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/README.md +36 -0
- package/client/README.md +116 -0
- package/client/index.html +31 -0
- package/client/index.js +25 -0
- package/client/package-lock.json +11664 -0
- package/client/package.json +70 -0
- package/client/public/demo/26899917-d1ba-4df4-bb33-48d09a8778da.jpg +0 -0
- package/client/public/demo/4b9894c7-12cd-466d-8fd3-a2757b09ec96.jpg +0 -0
- package/client/public/demo/7ed369ac-a1a2-4b45-b0cd-4fd5bcf5a75a.jpg +0 -0
- package/client/public/doc/API.postman_collection.json +214 -0
- package/client/public/github.svg +1 -0
- package/client/public/profilCompany.jpg +0 -0
- package/client/public/profilDeveloper.jpg +0 -0
- package/client/public/profilEngineer.jpg +0 -0
- package/client/public/profilPersonal.jpg +0 -0
- package/client/public/profils .psd +0 -0
- package/client/public/react.svg +9 -0
- package/client/src/APIInfo.jsx +328 -0
- package/client/src/AddWidgetTypeModal.jsx +44 -0
- package/client/src/AddWidgetTypeModal.scss +87 -0
- package/client/src/App.css +42 -0
- package/client/src/App.jsx +588 -0
- package/client/src/App.scss +1466 -0
- package/client/src/AssistantChat.jsx +280 -0
- package/client/src/AssistantChat.scss +229 -0
- package/client/src/Button.jsx +12 -0
- package/client/src/Button.scss +197 -0
- package/client/src/CalculationBuilder.jsx +338 -0
- package/client/src/CalculationStepRow.jsx +199 -0
- package/client/src/CalendarConfigModal.jsx +49 -0
- package/client/src/ChartConfigModal.jsx +402 -0
- package/client/src/ConditionBuilder.jsx +622 -0
- package/client/src/ConditionBuilder.scss +672 -0
- package/client/src/ConditionBuilder2.jsx +757 -0
- package/client/src/ContentView.jsx +274 -0
- package/client/src/CronBuilder.jsx +126 -0
- package/client/src/CronBuilder.scss +128 -0
- package/client/src/CronPartBuilder.jsx +150 -0
- package/client/src/Dashboard.jsx +210 -0
- package/client/src/Dashboard.scss +445 -0
- package/client/src/DashboardChart.jsx +362 -0
- package/client/src/DashboardFlexViewItem.jsx +129 -0
- package/client/src/DashboardView.jsx +554 -0
- package/client/src/DataChart.jsx +349 -0
- package/client/src/DataEditor.jsx +556 -0
- package/client/src/DataLayout.jsx +709 -0
- package/client/src/DataTable.jsx +996 -0
- package/client/src/DataTable.scss +73 -0
- package/client/src/Dialog.jsx +90 -0
- package/client/src/Dialog.scss +117 -0
- package/client/src/DisplayFlexNodeRenderer.jsx +115 -0
- package/client/src/DocumentationPageLayout.jsx +93 -0
- package/client/src/DocumentationPageLayout.scss +262 -0
- package/client/src/Draggable.jsx +222 -0
- package/client/src/Draggable.scss +49 -0
- package/client/src/ExportDialog.jsx +172 -0
- package/client/src/Field.jsx +1558 -0
- package/client/src/FlexBuilder.jsx +269 -0
- package/client/src/FlexBuilder.scss +226 -0
- package/client/src/FlexBuilderControls.jsx +170 -0
- package/client/src/FlexBuilderModal.jsx +75 -0
- package/client/src/FlexBuilderModal.scss +109 -0
- package/client/src/FlexBuilderPreview.jsx +48 -0
- package/client/src/FlexBuilderUtils.js +84 -0
- package/client/src/FlexDataRenderer.jsx +170 -0
- package/client/src/FlexDataRenderer.scss +80 -0
- package/client/src/FlexNode.jsx +290 -0
- package/client/src/FlexTreeUtils.js +129 -0
- package/client/src/HiddenableCell.jsx +46 -0
- package/client/src/HiddenableCell.scss +36 -0
- package/client/src/KPIDialog.jsx +39 -0
- package/client/src/KPIWidget.jsx +139 -0
- package/client/src/KanbanCard.jsx +92 -0
- package/client/src/KanbanColumn.jsx +29 -0
- package/client/src/KanbanConfigModal.jsx +115 -0
- package/client/src/KanbanView.jsx +113 -0
- package/client/src/KanbanView.scss +107 -0
- package/client/src/MessageRotator.jsx +77 -0
- package/client/src/MessageRotator.scss +14 -0
- package/client/src/ModelCreator.jsx +640 -0
- package/client/src/ModelCreator.scss +154 -0
- package/client/src/ModelCreatorField.jsx +810 -0
- package/client/src/ModelImporter.jsx +120 -0
- package/client/src/ModelList.jsx +205 -0
- package/client/src/Notification.jsx +137 -0
- package/client/src/Notification.scss +126 -0
- package/client/src/NotificationProvider.jsx +73 -0
- package/client/src/PackGallery.jsx +211 -0
- package/client/src/PackGallery.scss +156 -0
- package/client/src/Pagination.jsx +141 -0
- package/client/src/RTE.jsx +415 -0
- package/client/src/RTETrans.jsx +103 -0
- package/client/src/RelationField.jsx +247 -0
- package/client/src/RelationValue.jsx +230 -0
- package/client/src/RestoreConfirmationModal.jsx +183 -0
- package/client/src/RestoreDialog.jsx +134 -0
- package/client/src/RestoreDialog.scss +67 -0
- package/client/src/RichTextEditorModal.jsx +34 -0
- package/client/src/RichTextEditorModal.scss +54 -0
- package/client/src/TourSpotlight.jsx +234 -0
- package/client/src/TourSpotlight.scss +100 -0
- package/client/src/TutorialsMenu.jsx +83 -0
- package/client/src/TutorialsMenu.scss +217 -0
- package/client/src/TutorialsRewardModal.jsx +65 -0
- package/client/src/Utils.jsx +78 -0
- package/client/src/ViewSwitcher.jsx +54 -0
- package/client/src/ViewSwitcher.scss +74 -0
- package/client/src/Webpage.jsx +69 -0
- package/client/src/_variables.scss +21 -0
- package/client/src/assets/react.svg +1 -0
- package/client/src/constants.js +68 -0
- package/client/src/contexts/AuthContext.jsx +28 -0
- package/client/src/contexts/ModelContext.jsx +328 -0
- package/client/src/contexts/UIContext.jsx +52 -0
- package/client/src/core/data.js +35 -0
- package/client/src/entry-client.jsx +15 -0
- package/client/src/entry-server.jsx +13 -0
- package/client/src/filter.js +222 -0
- package/client/src/hooks/data.js +101 -0
- package/client/src/hooks/useDebounce.js +20 -0
- package/client/src/hooks/useDragAndDrop.js +59 -0
- package/client/src/hooks/useLocalStorage.js +100 -0
- package/client/src/hooks/useMount.js +42 -0
- package/client/src/hooks/useTutorials.jsx +227 -0
- package/client/src/hooks/useWindowSize.js +24 -0
- package/client/src/i18n.js +47 -0
- package/client/src/index.css +90 -0
- package/client/src/index.js +10 -0
- package/client/src/main.jsx +11 -0
- package/client/src/translations.js +15940 -0
- package/client/src/tutorials.js +112 -0
- package/client/vite.config.js +30 -0
- package/eslint.config.js +50 -0
- package/package.json +51 -27
- package/server.js +9 -0
- package/src/constants.js +12 -23
- package/src/core.js +9 -8
- package/src/data.js +81 -80
- package/src/defaultModels.js +114 -115
- package/src/email.js +8 -9
- package/src/engine.js +5 -5
- package/src/events.js +1 -2
- package/src/i18n.js +212 -211
- package/src/middlewares/middleware-mongodb.js +94 -93
- package/src/migrate.js +24 -24
- package/src/modules/assistant.js +55 -55
- package/src/modules/bucket.js +10 -9
- package/src/modules/data.js +387 -357
- package/src/modules/file.js +3 -6
- package/src/modules/mongodb.js +6 -7
- package/src/modules/user.js +24 -47
- package/src/modules/workflow.js +139 -73
- package/src/openai.jobs.js +2 -2
- package/src/packs.js +30 -31
- package/src/providers.js +101 -3
- package/src/setenv.js +1 -1
- package/src/user.js +0 -17
- package/src/workers/crypto-worker.js +21 -20
- package/src/workers/import-export-worker.js +18 -18
- package/test/data.backup.integration.test.js +7 -12
- package/test/data.integration.test.js +27 -25
- package/test/globalSetup.js +2 -2
- package/test/import_export.integration.test.js +6 -6
- package/test/model.integration.test.js +21 -22
- package/test/workflow.integration.test.js +2 -5
- package/test/workflow.robustness.test.js +6 -13
- package/vitest.config.js +2 -2
package/src/modules/file.js
CHANGED
|
@@ -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
|
}
|
package/src/modules/mongodb.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
|
package/src/modules/user.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import i18n from "data-primals-engine/i18n";
|
|
2
|
-
import {
|
|
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
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
package/src/modules/workflow.js
CHANGED
|
@@ -4,17 +4,16 @@ import schedule from "node-schedule";
|
|
|
4
4
|
import {ObjectId} from "mongodb";
|
|
5
5
|
import crypto from "node:crypto";
|
|
6
6
|
|
|
7
|
+
import {VM, VMScript} from 'vm2';
|
|
7
8
|
import {Logger} from "../gameObject.js";
|
|
8
|
-
import {deleteData,
|
|
9
|
+
import {deleteData, insertData, patchData, searchData} from "./data.js";
|
|
9
10
|
import {maxExecutionsByStep, maxWorkflowSteps} from "../constants.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
11
|
+
import {ChatOpenAI} from "@langchain/openai";
|
|
12
|
+
import {ChatGoogleGenerativeAI} from "@langchain/google-genai";
|
|
13
|
+
import {ChatPromptTemplate} from "@langchain/core/prompts";
|
|
13
14
|
import i18n from "data-primals-engine/i18n";
|
|
14
15
|
import {sendEmail} from "../email.js";
|
|
15
16
|
|
|
16
|
-
// 1. ADD THIS IMPORT AT THE TOP OF THE FILE
|
|
17
|
-
// This allows the module to call its own exported functions.
|
|
18
17
|
import * as workflowModule from './workflow.js';
|
|
19
18
|
|
|
20
19
|
let logger = null;
|
|
@@ -131,7 +130,7 @@ export async function scheduleWorkflowTriggers() {
|
|
|
131
130
|
// Trouver tous les workflows actifs avec une cronExpression définie
|
|
132
131
|
const workflowsToSchedule = await datasCollection.find({
|
|
133
132
|
_model: 'workflowTrigger',
|
|
134
|
-
cronExpression: { $exists: true, $ne: "" }
|
|
133
|
+
cronExpression: { $exists: true, $ne: "" }
|
|
135
134
|
// Ajoutez d'autres conditions si nécessaire (ex: active: true)
|
|
136
135
|
}).toArray();
|
|
137
136
|
|
|
@@ -181,6 +180,69 @@ export async function scheduleWorkflowTriggers() {
|
|
|
181
180
|
}
|
|
182
181
|
|
|
183
182
|
|
|
183
|
+
/* Remplacement de la fonction executeSafeJavascript
|
|
184
|
+
async function executeWithIsolatedVm(actionDef, context) {
|
|
185
|
+
const code = actionDef.script;
|
|
186
|
+
const isolate = new ivm.Isolate({ memoryLimit: 128 }); // Limite de 128MB de mémoire
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
const vmContext = await isolate.createContext();
|
|
190
|
+
const jail = vmContext.global;
|
|
191
|
+
|
|
192
|
+
// On expose une fonction 'log' sécurisée à l'intérieur de la VM.
|
|
193
|
+
// C'est une référence, pas la fonction elle-même.
|
|
194
|
+
await jail.set('log', new ivm.Reference(function(...args) {
|
|
195
|
+
// On peut préfixer les logs pour savoir qu'ils viennent de la VM
|
|
196
|
+
logger.info('[VM Script Log]', ...args);
|
|
197
|
+
}));
|
|
198
|
+
|
|
199
|
+
// On injecte les données du contexte. Elles sont COPIÉES, pas référencées.
|
|
200
|
+
// C'est une caractéristique de sécurité clé.
|
|
201
|
+
await jail.set('contextData', new ivm.ExternalCopy(context).copyInto());
|
|
202
|
+
// On peut aussi exposer des fonctions spécifiques de votre application
|
|
203
|
+
// await jail.set('myApiFunction', new ivm.Reference(async (params) => { ... }));
|
|
204
|
+
|
|
205
|
+
// On compile le script
|
|
206
|
+
const script = await isolate.compileScript(code);
|
|
207
|
+
|
|
208
|
+
// On exécute le script avec un timeout
|
|
209
|
+
const result = await script.run(vmContext, { timeout: 1000 });
|
|
210
|
+
|
|
211
|
+
// Le résultat est une référence, on le copie pour l'utiliser dans notre contexte principal.
|
|
212
|
+
return result;
|
|
213
|
+
|
|
214
|
+
} catch (error) {
|
|
215
|
+
logger.error("Error executing script with isolated-vm:", error.stack);
|
|
216
|
+
// On propage une erreur propre
|
|
217
|
+
throw new Error(`Script execution failed: ${error.message}`);
|
|
218
|
+
} finally {
|
|
219
|
+
// TRÈS IMPORTANT : Toujours libérer l'isolate pour éviter les fuites de mémoire.
|
|
220
|
+
if (isolate && !isolate.isDisposed) {
|
|
221
|
+
isolate.dispose();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
REQUIRES NODE >=20
|
|
226
|
+
*/
|
|
227
|
+
async function executeSafeJavascript(actionDef, context) {
|
|
228
|
+
const code = actionDef.script;
|
|
229
|
+
const vm = new VM({
|
|
230
|
+
timeout: 1000, // Time out after 1 second
|
|
231
|
+
sandbox: context, // Pass the context object
|
|
232
|
+
console: 'redirect', // Redirect console output
|
|
233
|
+
require: false, // Disable require
|
|
234
|
+
wasm: false // disable WebAssembly
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
const script = new VMScript(code);
|
|
239
|
+
return vm.run(script);
|
|
240
|
+
} catch (error) {
|
|
241
|
+
console.error("Error executing script:", error);
|
|
242
|
+
throw error; // or return an error object
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
184
246
|
/**
|
|
185
247
|
* Handles the 'Webhook' workflow action.
|
|
186
248
|
* Sends an HTTP request to a specified URL with substituted data using native fetch.
|
|
@@ -270,7 +332,7 @@ async function handleWebhookAction(actionDef, contextData, user, dbCollection) {
|
|
|
270
332
|
// 4. Prepare Fetch Options
|
|
271
333
|
const fetchOptions = {
|
|
272
334
|
method: method.toUpperCase(),
|
|
273
|
-
headers: headersObject
|
|
335
|
+
headers: headersObject // Native fetch accepts an object directly
|
|
274
336
|
};
|
|
275
337
|
|
|
276
338
|
if (bodyObject !== null && ['POST', 'PUT', 'PATCH'].includes(fetchOptions.method)) {
|
|
@@ -649,32 +711,36 @@ export async function executeStepAction(actionDef, contextData, user, dbCollecti
|
|
|
649
711
|
try {
|
|
650
712
|
let result;
|
|
651
713
|
switch (actionDef.type) {
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
714
|
+
case 'Log':
|
|
715
|
+
logger.info(`[Workflow Log Action] Action: ${actionDef.name}. Contexte:`, contextData);
|
|
716
|
+
result = { success: true, message: 'Log action executed successfully.' }; // <--- CORRECTION
|
|
717
|
+
break;
|
|
718
|
+
case 'Webhook':
|
|
719
|
+
result = await handleWebhookAction(actionDef, contextData, user, dbCollection);
|
|
720
|
+
break;
|
|
721
|
+
case 'CreateData':
|
|
722
|
+
result = await handleCreateDataAction(actionDef, contextData, user, dbCollection);
|
|
723
|
+
break;
|
|
724
|
+
case 'UpdateData':
|
|
725
|
+
result = await handleUpdateDataAction(actionDef, contextData, user);
|
|
726
|
+
break;
|
|
727
|
+
case 'DeleteData':
|
|
728
|
+
result = await handleDeleteDataAction(actionDef, contextData, user, dbCollection);
|
|
729
|
+
break;
|
|
730
|
+
case 'GenerateAIContent':
|
|
731
|
+
result = await executeGenerateAIContentAction(actionDef, contextData, user);
|
|
732
|
+
break;
|
|
733
|
+
case 'SendEmail':
|
|
734
|
+
result = await handleSendEmailAction(actionDef, contextData, user);
|
|
735
|
+
break;
|
|
736
|
+
case 'ExecuteScript':
|
|
737
|
+
result = await executeSafeJavascript(actionDef, contextData);
|
|
738
|
+
break;
|
|
739
|
+
|
|
674
740
|
// ... autres cases à venir ...
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
741
|
+
default:
|
|
742
|
+
logger.error(`[executeStepAction] Unknown action type: ${actionDef.type}`);
|
|
743
|
+
return { success: false, message: `Unknown action type: ${actionDef.type}` };
|
|
678
744
|
}
|
|
679
745
|
return result;
|
|
680
746
|
} catch (error) {
|
|
@@ -749,7 +815,7 @@ async function resolvePathValue(pathString, initialContext, user) {
|
|
|
749
815
|
|
|
750
816
|
let currentModelName = initialContext._model;
|
|
751
817
|
let currentDocId = new ObjectId(initialContext._id);
|
|
752
|
-
const collection = getCollectionForUser(user);
|
|
818
|
+
const collection = await getCollectionForUser(user);
|
|
753
819
|
|
|
754
820
|
// Construire le pipeline d'agrégation
|
|
755
821
|
const pipeline = [
|
|
@@ -862,7 +928,7 @@ export async function substituteVariables(template, contextData, user) {
|
|
|
862
928
|
// --- À partir d'ici, nous savons que `template` est une chaîne de caractères ---
|
|
863
929
|
|
|
864
930
|
// 4. Construire le contexte complet pour la substitution
|
|
865
|
-
const dbCollection = getCollectionForUser(user);
|
|
931
|
+
const dbCollection = await getCollectionForUser(user);
|
|
866
932
|
const userEnvVars = await dbCollection.find({ _model: 'env', _user: user.username }).toArray();
|
|
867
933
|
const userEnv = userEnvVars.reduce((acc, v) => ({ ...acc, [v.name]: v.value }), {});
|
|
868
934
|
|
|
@@ -972,7 +1038,7 @@ export async function triggerWorkflows(triggerData, user, eventType) {
|
|
|
972
1038
|
console.log(`[Workflow Trigger] Event: ${eventType}, Model: ${targetModelName}${dataId ? `, Data ID: ${dataId}` : ''}, User: ${user.username}`);
|
|
973
1039
|
|
|
974
1040
|
try {
|
|
975
|
-
const dbCollection = getCollectionForUser(user); // Collection des données utilisateur
|
|
1041
|
+
const dbCollection = await getCollectionForUser(user); // Collection des données utilisateur
|
|
976
1042
|
|
|
977
1043
|
// 1. Trouver les WorkflowTriggers pertinents
|
|
978
1044
|
const workflowTriggers = await dbCollection.find({
|
|
@@ -1008,29 +1074,29 @@ export async function triggerWorkflows(triggerData, user, eventType) {
|
|
|
1008
1074
|
continue; // Passer au trigger suivant si le filtre est invalide
|
|
1009
1075
|
}
|
|
1010
1076
|
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
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
|
|
1077
|
+
try {
|
|
1078
|
+
const finalFilter = {
|
|
1079
|
+
'$and': [
|
|
1080
|
+
dataFilterCondition // Applique la condition du trigger
|
|
1081
|
+
]
|
|
1082
|
+
};
|
|
1083
|
+
|
|
1084
|
+
console.debug(`[Workflow Trigger] Vérification dataFilter pour trigger ${trigger._id} avec filtre combiné:`, JSON.stringify(finalFilter));
|
|
1085
|
+
|
|
1086
|
+
// Exécuter la vérification dans la base de données
|
|
1087
|
+
// Utilisation de countDocuments pour une vérification rapide
|
|
1088
|
+
const matchCount = await searchData({ user, query: { model: targetModelName, filter: finalFilter, limit: 1 } });
|
|
1089
|
+
|
|
1090
|
+
if (!matchCount.count) {
|
|
1091
|
+
console.debug(`[Workflow Trigger] Trigger ${trigger._id}: dataFilter non satisfait par la donnée ${dataId}. WorkflowRun non créé.`);
|
|
1092
|
+
continue; // Passer au trigger suivant
|
|
1093
|
+
} else {
|
|
1094
|
+
console.debug(`[Workflow Trigger] Trigger ${trigger._id}: dataFilter satisfait par la donnée ${dataId}.`);
|
|
1033
1095
|
}
|
|
1096
|
+
} catch (filterError) {
|
|
1097
|
+
console.error(`[Workflow Trigger] Erreur lors de la conversion ou de l'exécution du dataFilter pour le trigger ${trigger._id}:`, filterError);
|
|
1098
|
+
continue; // Ne pas créer en cas d'erreur de filtre
|
|
1099
|
+
}
|
|
1034
1100
|
} // Fin de la vérification dataFilter
|
|
1035
1101
|
|
|
1036
1102
|
// 4. Si les filtres (eventType, dataFilter) sont passés, créer l'instance workflowRun
|
|
@@ -1087,9 +1153,9 @@ export async function triggerWorkflows(triggerData, user, eventType) {
|
|
|
1087
1153
|
}
|
|
1088
1154
|
|
|
1089
1155
|
return new Promise((resolve) => setTimeout(async () => {
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1156
|
+
await trigger(triggerData, user, eventType);
|
|
1157
|
+
resolve();
|
|
1158
|
+
}, 0)
|
|
1093
1159
|
);
|
|
1094
1160
|
}
|
|
1095
1161
|
|
|
@@ -1105,7 +1171,7 @@ export async function triggerWorkflows(triggerData, user, eventType) {
|
|
|
1105
1171
|
// C:/Dev/hackersonline-engine/server/src/modules/workflow.js
|
|
1106
1172
|
|
|
1107
1173
|
export async function processWorkflowRun(workflowRunId, user) {
|
|
1108
|
-
const dbCollection = getCollectionForUser(user);
|
|
1174
|
+
const dbCollection = await getCollectionForUser(user);
|
|
1109
1175
|
const runId = typeof workflowRunId === 'string' ? new ObjectId(workflowRunId) : workflowRunId;
|
|
1110
1176
|
|
|
1111
1177
|
logger.info(`[processWorkflowRun] Starting processing for workflowRun ID: ${runId}`);
|
|
@@ -1290,7 +1356,7 @@ async function executeGenerateAIContentAction(action, context, user) {
|
|
|
1290
1356
|
|
|
1291
1357
|
const providers = {
|
|
1292
1358
|
"OpenAI" : "OPENAI_API_KEY",
|
|
1293
|
-
"Google": "GOOGLE_API_KEY"
|
|
1359
|
+
"Google": "GOOGLE_API_KEY"
|
|
1294
1360
|
}
|
|
1295
1361
|
const envKeyName = providers[aiProvider];
|
|
1296
1362
|
if( !envKeyName ) {
|
|
@@ -1298,7 +1364,7 @@ async function executeGenerateAIContentAction(action, context, user) {
|
|
|
1298
1364
|
}
|
|
1299
1365
|
|
|
1300
1366
|
// Cherche d'abord dans les variables d'environnement de l'utilisateur
|
|
1301
|
-
const envCollection = getCollectionForUser(user);
|
|
1367
|
+
const envCollection = await getCollectionForUser(user);
|
|
1302
1368
|
const userEnvVar = await envCollection.findOne({ _model: 'env', name: envKeyName, _user: user.username });
|
|
1303
1369
|
|
|
1304
1370
|
if (userEnvVar && userEnvVar.value) {
|
|
@@ -1319,14 +1385,14 @@ async function executeGenerateAIContentAction(action, context, user) {
|
|
|
1319
1385
|
let llm;
|
|
1320
1386
|
try {
|
|
1321
1387
|
switch (aiProvider) {
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1388
|
+
case 'OpenAI':
|
|
1389
|
+
llm = new ChatOpenAI({ apiKey, modelName: aiModel, temperature: 0.7 });
|
|
1390
|
+
break;
|
|
1391
|
+
case 'GoogleGemini':
|
|
1392
|
+
llm = new ChatGoogleGenerativeAI({ apiKey, modelName: aiModel, temperature: 0.7 });
|
|
1393
|
+
break;
|
|
1394
|
+
default:
|
|
1395
|
+
throw new Error(`Fournisseur IA non supporté : ${aiProvider}`);
|
|
1330
1396
|
}
|
|
1331
1397
|
} catch (initError) {
|
|
1332
1398
|
const message = `Échec de l'initialisation du client IA pour ${aiProvider}: ${initError.message}`;
|
|
@@ -1413,7 +1479,7 @@ async function handleSendEmailAction(action, triggerData, user) {
|
|
|
1413
1479
|
// 3. Préparer les données pour sendEmail
|
|
1414
1480
|
const emailData = {
|
|
1415
1481
|
title: rsubject,
|
|
1416
|
-
content: rbody
|
|
1482
|
+
content: rbody
|
|
1417
1483
|
};
|
|
1418
1484
|
|
|
1419
1485
|
await sendEmail(rto, emailData, smtpConfig, user.lang);
|
package/src/openai.jobs.js
CHANGED
|
@@ -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;
|