data-primals-engine 1.2.6 → 1.3.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.
- package/README.md +38 -2
- package/client/src/App.jsx +13 -20
- package/client/src/AssistantChat.jsx +5 -4
- package/client/src/DataEditor.jsx +1 -1
- package/client/src/DataTable.jsx +47 -3
- package/client/src/ExportDialog.jsx +2 -2
- package/client/src/Field.jsx +2 -2
- package/client/src/KanbanCard.jsx +4 -2
- package/client/src/KanbanConfigModal.jsx +5 -7
- package/client/src/ModelCreatorField.jsx +9 -9
- package/client/src/PackGallery.jsx +89 -9
- package/client/src/PackGallery.scss +58 -4
- package/client/src/RTETrans.jsx +11 -0
- package/client/src/RelationValue.jsx +3 -4
- package/client/src/constants.js +1 -1
- package/client/src/core/data.js +2 -1
- package/client/src/translations.js +63 -0
- package/package.json +8 -1
- package/server.js +4 -4
- package/src/constants.js +6 -0
- package/src/defaultModels.js +23 -10
- package/src/i18n.js +1 -1
- package/src/modules/{assistant.js → assistant/assistant.js} +42 -27
- package/src/modules/assistant/constants.js +16 -0
- package/src/modules/data/data.js +4601 -4552
- package/src/modules/data/data.routes.js +29 -3
- package/src/modules/user.js +12 -1
- package/src/modules/workflow.js +133 -48
- package/src/packs.js +1015 -9
- package/src/services/index.js +11 -0
- package/src/services/stripe.js +141 -0
- package/test/data.integration.test.js +65 -2
- package/test/workflow.actions.integration.test.js +2 -2
- package/test/workflow.integration.test.js +1 -1
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
middlewareAuthenticator,
|
|
34
34
|
userInitiator
|
|
35
35
|
} from "../user.js";
|
|
36
|
-
import {assistantGlobalLimiter} from "../assistant.js";
|
|
36
|
+
import {assistantGlobalLimiter} from "../assistant/assistant.js";
|
|
37
37
|
import {Config} from "../../config.js";
|
|
38
38
|
import {processFilterPlaceholders} from "../../../client/src/filter.js";
|
|
39
39
|
import {tutorialsConfig} from "../../../client/src/tutorials.js";
|
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
editData, editModel, exportData,
|
|
44
44
|
getModel, getResource,
|
|
45
45
|
handleCustomEndpointRequest,
|
|
46
|
-
handleDemoInitialization, importData, insertData, installPack, loadFromDump,
|
|
46
|
+
handleDemoInitialization, importData, insertData, installPack, loadFromDump, middlewareEndpointAuthenticator,
|
|
47
47
|
patchData, searchData, validateModelStructure
|
|
48
48
|
} from "./data.js";
|
|
49
49
|
import process from "node:process";
|
|
@@ -161,7 +161,7 @@ export async function registerRoutes(engine){
|
|
|
161
161
|
|
|
162
162
|
logger = engine.getComponent(Logger);
|
|
163
163
|
|
|
164
|
-
engine.all('/api/actions/:path', [
|
|
164
|
+
engine.all('/api/actions/:path', [middlewareEndpointAuthenticator, userInitiator], handleCustomEndpointRequest);
|
|
165
165
|
engine.post('/api/demo/initialize', [middlewareAuthenticator, userInitiator], handleDemoInitialization);
|
|
166
166
|
|
|
167
167
|
engine.post('/api/magnets', [middlewareAuthenticator, userInitiator], async (req, res) => {
|
|
@@ -1556,6 +1556,32 @@ export async function registerRoutes(engine){
|
|
|
1556
1556
|
res.status(500).json({ success: false, error: error.message || 'An internal server error occurred.' });
|
|
1557
1557
|
}
|
|
1558
1558
|
});
|
|
1559
|
+
engine.post('/api/packs/install', [throttle, middlewareAuthenticator, userInitiator, setTimeoutMiddleware(60000)], async (req, res) => {
|
|
1560
|
+
const { id } = req.params;
|
|
1561
|
+
const user = req.me;
|
|
1562
|
+
const lang = req.query.lang || req.fields.lang;
|
|
1563
|
+
|
|
1564
|
+
try {
|
|
1565
|
+
// Vérification des permissions
|
|
1566
|
+
if (user.username !== 'demo' && isLocalUser(user) && !await hasPermission(["API_ADMIN", "API_INSTALL_PACK"], user)) {
|
|
1567
|
+
return res.status(403).json({ success: false, error: i18n.t('api.permission.installPack') });
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
const result = await installPack(req.fields.packData, user, lang);
|
|
1571
|
+
|
|
1572
|
+
if (result.success) {
|
|
1573
|
+
res.status(200).json({ success: true, message: `Pack installed successfully.`, summary: result.summary });
|
|
1574
|
+
} else if (!result.success && !result.modifiedCount) {
|
|
1575
|
+
res.status(200).json({ success: true, message: `No data to insert.`, summary: result.summary });
|
|
1576
|
+
} else {
|
|
1577
|
+
res.status(400).json({ success: false, error: 'Pack installation had errors.', errors: result.errors, summary: result.summary });
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
} catch (error) {
|
|
1581
|
+
logger.error(`[POST /api/packs/${id}/install] Critical error:`, error);
|
|
1582
|
+
res.status(500).json({ success: false, error: error.message || 'An internal server error occurred.' });
|
|
1583
|
+
}
|
|
1584
|
+
});
|
|
1559
1585
|
/*
|
|
1560
1586
|
engine.post('/api/packs/install', [throttle, middlewareAuthenticator, userInitiator, ...userMiddlewares], async (req, res) => {
|
|
1561
1587
|
|
package/src/modules/user.js
CHANGED
|
@@ -3,9 +3,10 @@ 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/index.js";
|
|
6
|
+
import {getAPILang, searchData} from "./data/index.js";
|
|
7
7
|
import {Logger} from "../gameObject.js";
|
|
8
8
|
import rateLimit from "express-rate-limit";
|
|
9
|
+
import ivm from "isolated-vm";
|
|
9
10
|
|
|
10
11
|
export const userInitiator = async (req, res, next) => {
|
|
11
12
|
|
|
@@ -256,3 +257,13 @@ export async function calculateTotalUserStorageUsage(user) {
|
|
|
256
257
|
logger.debug(`[Storage] User ${userId}: Data size = ${dataSize} bytes, Files size = ${filesSize} bytes. Total = ${dataSize + filesSize} bytes.`);
|
|
257
258
|
return dataSize + filesSize;
|
|
258
259
|
}
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
export async function getEnv(user){
|
|
263
|
+
const result = await searchData({ model: 'env' }, user);
|
|
264
|
+
const envObject = result.data.reduce((acc, v) => {
|
|
265
|
+
acc[v.name] = v.value;
|
|
266
|
+
return acc;
|
|
267
|
+
}, {});
|
|
268
|
+
return envObject;
|
|
269
|
+
}
|
package/src/modules/workflow.js
CHANGED
|
@@ -7,7 +7,7 @@ import ivm from 'isolated-vm';
|
|
|
7
7
|
|
|
8
8
|
import {Logger} from "../gameObject.js";
|
|
9
9
|
import {deleteData, getModel, insertData, patchData, scheduleAlerts, searchData} from "./data/index.js";
|
|
10
|
-
import {emailDefaultConfig, maxExecutionsByStep, maxWorkflowSteps} from "../constants.js";
|
|
10
|
+
import {emailDefaultConfig, maxExecutionsByStep, maxWorkflowSteps, port} from "../constants.js";
|
|
11
11
|
import {ChatOpenAI} from "@langchain/openai";
|
|
12
12
|
import {ChatGoogleGenerativeAI} from "@langchain/google-genai";
|
|
13
13
|
import {ChatPromptTemplate} from "@langchain/core/prompts";
|
|
@@ -16,9 +16,13 @@ import i18n from "../../src/i18n.js";
|
|
|
16
16
|
import {sendEmail} from "../email.js";
|
|
17
17
|
|
|
18
18
|
import * as workflowModule from './workflow.js';
|
|
19
|
-
import util from "node:util";
|
|
20
|
-
import {object_equals} from "../core.js";
|
|
21
19
|
import {isConditionMet} from "../filter.js";
|
|
20
|
+
import { services } from '../services/index.js';
|
|
21
|
+
import {getEnv} from "./user.js";
|
|
22
|
+
import {getHost} from "../constants.js";
|
|
23
|
+
import {providers} from "./assistant/constants.js";
|
|
24
|
+
import {ChatAnthropic} from "@langchain/anthropic";
|
|
25
|
+
import {getAIProvider} from "./assistant/assistant.js";
|
|
22
26
|
|
|
23
27
|
let logger = null;
|
|
24
28
|
export async function onInit(defaultEngine) {
|
|
@@ -27,6 +31,51 @@ export async function onInit(defaultEngine) {
|
|
|
27
31
|
await scheduleWorkflowTriggers();
|
|
28
32
|
}
|
|
29
33
|
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Déclenche un workflow par son nom et lui passe des données de contexte.
|
|
38
|
+
* C'est la fonction clé à exposer aux endpoints pour lancer des processus métier.
|
|
39
|
+
*
|
|
40
|
+
* @param {string} name - Le nom du workflow à exécuter.
|
|
41
|
+
* @param {object} data - Les données à injecter dans context.triggerData.
|
|
42
|
+
* @param {object} user - L'objet utilisateur qui initie l'action.
|
|
43
|
+
* @returns {Promise<{success: boolean, message?: string, runId?: ObjectId}>}
|
|
44
|
+
*/
|
|
45
|
+
export async function runWorkflowByName(name, data, user) {
|
|
46
|
+
if (!name) {
|
|
47
|
+
return { success: false, message: "Workflow name is required." };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const dbCollection = await getCollectionForUser(user);
|
|
51
|
+
|
|
52
|
+
// 1. Trouver la définition du workflow par son nom
|
|
53
|
+
const workflowDefinition = await dbCollection.findOne({ _model: 'workflow', name });
|
|
54
|
+
|
|
55
|
+
if (!workflowDefinition) {
|
|
56
|
+
const msg = `Workflow with name "${name}" not found.`;
|
|
57
|
+
logger.error(`[runWorkflowByName] ${msg}`);
|
|
58
|
+
return { success: false, message: msg };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 2. Créer le document workflowRun
|
|
62
|
+
const workflowRunData = {
|
|
63
|
+
_model: 'workflowRun',
|
|
64
|
+
_user: user._user || user.username,
|
|
65
|
+
workflow: workflowDefinition._id,
|
|
66
|
+
contextData: { triggerData: data }, // Les données passées deviennent le triggerData
|
|
67
|
+
status: 'pending',
|
|
68
|
+
startedAt: new Date()
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const insertResult = await dbCollection.insertOne(workflowRunData);
|
|
72
|
+
logger.info(`[runWorkflowByName] Created workflowRun ${insertResult.insertedId} for workflow "${name}".`);
|
|
73
|
+
|
|
74
|
+
// 3. Lancer le traitement de manière asynchrone
|
|
75
|
+
await processWorkflowRun(insertResult.insertedId, user);
|
|
76
|
+
|
|
77
|
+
return { success: true, runId: insertResult.insertedId };
|
|
78
|
+
}
|
|
30
79
|
/**
|
|
31
80
|
* Exécute une fonction de manière sécurisée en s'assurant qu'une seule instance
|
|
32
81
|
* s'exécute à la fois, grâce à un système de verrouillage distribué basé sur la base de données.
|
|
@@ -220,7 +269,11 @@ export async function executeSafeJavascript(actionDef, context, user) {
|
|
|
220
269
|
return new ivm.ExternalCopy(result.data?.[0] || null).copyInto();
|
|
221
270
|
};
|
|
222
271
|
|
|
223
|
-
// 1. Build the sandboxed API
|
|
272
|
+
// 1. Build the sandboxed API methods
|
|
273
|
+
await jail.set('_workflow_run', new ivm.Reference(async (name, contextData) => {
|
|
274
|
+
const result = await runWorkflowByName(name, JSON.parse(contextData), user);
|
|
275
|
+
return new ivm.ExternalCopy(result).copyInto();
|
|
276
|
+
}));
|
|
224
277
|
await jail.set('_db_create', new ivm.Reference(async (modelName, dataObject) => {
|
|
225
278
|
const result = await insertData(modelName, JSON.parse(dataObject), {}, user, false);
|
|
226
279
|
if (result.success && result.insertedIds) {
|
|
@@ -255,12 +308,8 @@ export async function executeSafeJavascript(actionDef, context, user) {
|
|
|
255
308
|
return new ivm.ExternalCopy(result.data?.[0]?.value || null).copyInto();
|
|
256
309
|
}));
|
|
257
310
|
await jail.set('_env_get_all', new ivm.Reference(async () => {
|
|
258
|
-
const result = await
|
|
259
|
-
|
|
260
|
-
acc[v.name] = v.value;
|
|
261
|
-
return acc;
|
|
262
|
-
}, {});
|
|
263
|
-
return new ivm.ExternalCopy(envObject).copyInto();
|
|
311
|
+
const result = await getEnv(user);
|
|
312
|
+
return new ivm.ExternalCopy(result).copyInto();
|
|
264
313
|
}));
|
|
265
314
|
|
|
266
315
|
// Contexte sécurisé
|
|
@@ -282,6 +331,10 @@ export async function executeSafeJavascript(actionDef, context, user) {
|
|
|
282
331
|
update: (...args) => _db_update.applySyncPromise(null, normalizeArgs(args)),
|
|
283
332
|
delete: (...args) => _db_delete.applySyncPromise(null, normalizeArgs(args))
|
|
284
333
|
};
|
|
334
|
+
|
|
335
|
+
const workflow = {
|
|
336
|
+
run: (...args) => _workflow_run.applySyncPromise(null, normalizeArgs(args))
|
|
337
|
+
};
|
|
285
338
|
|
|
286
339
|
const logger = {
|
|
287
340
|
info: _log_info,
|
|
@@ -332,7 +385,7 @@ export async function executeSafeJavascript(actionDef, context, user) {
|
|
|
332
385
|
}
|
|
333
386
|
|
|
334
387
|
/**
|
|
335
|
-
* Handles the '
|
|
388
|
+
* Handles the 'HttpRequest' workflow action.
|
|
336
389
|
* Sends an HTTP request to a specified URL with substituted data using native fetch.
|
|
337
390
|
*
|
|
338
391
|
* @param {object} actionDef - The definition of the 'Webhook' action.
|
|
@@ -341,17 +394,17 @@ export async function executeSafeJavascript(actionDef, context, user) {
|
|
|
341
394
|
* @param {object} dbCollection - The MongoDB collection (moins pertinent ici, mais gardé pour la cohérence).
|
|
342
395
|
* @returns {Promise<{success: boolean, message?: string, responseStatus?: number, responseBody?: any}>} - Result of the action.
|
|
343
396
|
*/
|
|
344
|
-
async function
|
|
397
|
+
async function handleHttpRequestAction(actionDef, contextData, user, dbCollection) {
|
|
345
398
|
const { name: actionName, _id: actionId, url, method = 'POST', headers: headersTemplate, body: bodyTemplate } = actionDef;
|
|
346
399
|
|
|
347
400
|
// 1. Basic Validation
|
|
348
401
|
if (!url) {
|
|
349
|
-
const msg = `[
|
|
402
|
+
const msg = `[handleHttpRequestAction] Action ${actionName} (${actionId}): Missing 'url'.`;
|
|
350
403
|
logger.error(msg);
|
|
351
404
|
return { success: false, message: msg };
|
|
352
405
|
}
|
|
353
406
|
|
|
354
|
-
logger.info(`[
|
|
407
|
+
logger.info(`[handleHttpRequestAction] Action ${actionName} (${actionId}): Executing webhook. Method: ${method}`);
|
|
355
408
|
|
|
356
409
|
try {
|
|
357
410
|
// 2. Substitute Variables
|
|
@@ -368,7 +421,7 @@ async function handleWebhookAction(actionDef, contextData, user, dbCollection) {
|
|
|
368
421
|
} else if (typeof headersTemplate === 'object') {
|
|
369
422
|
headersObject = await substituteVariables(headersTemplate, contextData, user);
|
|
370
423
|
} else {
|
|
371
|
-
logger.warn(`[
|
|
424
|
+
logger.warn(`[handleHttpRequestAction] Action ${actionName} (${actionId}): 'headers' has an invalid type (${typeof headersTemplate}). Ignoring.`);
|
|
372
425
|
}
|
|
373
426
|
}
|
|
374
427
|
|
|
@@ -379,7 +432,7 @@ async function handleWebhookAction(actionDef, contextData, user, dbCollection) {
|
|
|
379
432
|
} else if (typeof bodyTemplate === 'object') {
|
|
380
433
|
bodyObject = await substituteVariables(bodyTemplate, contextData, user);
|
|
381
434
|
} else {
|
|
382
|
-
logger.warn(`[
|
|
435
|
+
logger.warn(`[handleHttpRequestAction] Action ${actionName} (${actionId}): 'body' has an invalid type (${typeof bodyTemplate}). Ignoring.`);
|
|
383
436
|
}
|
|
384
437
|
}
|
|
385
438
|
|
|
@@ -391,7 +444,7 @@ async function handleWebhookAction(actionDef, contextData, user, dbCollection) {
|
|
|
391
444
|
throw new Error("Parsed headers is not a valid object.");
|
|
392
445
|
}
|
|
393
446
|
} catch (parseError) {
|
|
394
|
-
logger.error(`[
|
|
447
|
+
logger.error(`[handleHttpRequestAction] Action ${actionName} (${actionId}): Failed to parse substituted 'headers' JSON. Error: ${parseError.message}. Using default headers. Substituted string: ${substitutedHeadersString}`);
|
|
395
448
|
headersObject = { 'Content-Type': 'application/json' }; // Fallback
|
|
396
449
|
}
|
|
397
450
|
}
|
|
@@ -433,7 +486,7 @@ async function handleWebhookAction(actionDef, contextData, user, dbCollection) {
|
|
|
433
486
|
}
|
|
434
487
|
|
|
435
488
|
// 5. Execute Fetch Request using native fetch
|
|
436
|
-
logger.info(`[
|
|
489
|
+
logger.info(`[handleHttpRequestAction] Action ${actionName} (${actionId}): Calling URL: ${substitutedUrl}`);
|
|
437
490
|
const response = await fetch(substitutedUrl, fetchOptions); // Utilisation de fetch natif
|
|
438
491
|
|
|
439
492
|
// 6. Process Response
|
|
@@ -446,7 +499,7 @@ async function handleWebhookAction(actionDef, contextData, user, dbCollection) {
|
|
|
446
499
|
responseBody = await response.text();
|
|
447
500
|
}
|
|
448
501
|
} catch (responseParseError) {
|
|
449
|
-
logger.error(`[
|
|
502
|
+
logger.error(`[handleHttpRequestAction] Action ${actionName} (${actionId}): Failed to parse response body. Error: ${responseParseError.message}`);
|
|
450
503
|
// Try reading as text again in case of error during json parsing
|
|
451
504
|
try {
|
|
452
505
|
responseBody = await response.text();
|
|
@@ -455,7 +508,7 @@ async function handleWebhookAction(actionDef, contextData, user, dbCollection) {
|
|
|
455
508
|
}
|
|
456
509
|
}
|
|
457
510
|
|
|
458
|
-
logger.info(`[
|
|
511
|
+
logger.info(`[handleHttpRequestAction] Action ${actionName} (${actionId}): Received response. Status: ${response.status}`);
|
|
459
512
|
|
|
460
513
|
// 7. Return Result
|
|
461
514
|
if (response.ok) { // Status code 200-299
|
|
@@ -463,13 +516,13 @@ async function handleWebhookAction(actionDef, contextData, user, dbCollection) {
|
|
|
463
516
|
success: true,
|
|
464
517
|
message: `Webhook executed successfully. Status: ${response.status}`,
|
|
465
518
|
responseStatus: response.status,
|
|
466
|
-
responseBody: responseBody
|
|
467
|
-
|
|
519
|
+
responseBody: responseBody,
|
|
520
|
+
updatedContext: { httpResponse: responseBody }
|
|
468
521
|
};
|
|
469
522
|
} else {
|
|
470
523
|
// Handle non-successful responses (4xx, 5xx)
|
|
471
524
|
const errorMsg = `Webhook execution failed. Status: ${response.status}. Response: ${typeof responseBody === 'string' ? responseBody : JSON.stringify(responseBody)}`;
|
|
472
|
-
logger.error(`[
|
|
525
|
+
logger.error(`[handleHttpRequestAction] Action ${actionName} (${actionId}): ${errorMsg}`);
|
|
473
526
|
return {
|
|
474
527
|
success: false,
|
|
475
528
|
message: errorMsg,
|
|
@@ -480,7 +533,7 @@ async function handleWebhookAction(actionDef, contextData, user, dbCollection) {
|
|
|
480
533
|
|
|
481
534
|
} catch (error) {
|
|
482
535
|
// Catch network errors or other unexpected errors during the process
|
|
483
|
-
const msg = `[
|
|
536
|
+
const msg = `[handleHttpRequestAction] Action ${actionName} (${actionId}): Unexpected error during webhook execution. Error: ${error.message}`;
|
|
484
537
|
logger.error(msg, error.stack);
|
|
485
538
|
return { success: false, message: msg };
|
|
486
539
|
}
|
|
@@ -786,6 +839,52 @@ async function handleDeleteDataAction(actionDef, contextData, user, dbCollection
|
|
|
786
839
|
}
|
|
787
840
|
}
|
|
788
841
|
|
|
842
|
+
/**
|
|
843
|
+
* Handles the 'ExecuteServiceFunction' workflow action.
|
|
844
|
+
* Acts as a secure bridge between the workflow engine and native service modules.
|
|
845
|
+
*
|
|
846
|
+
* @param {object} actionDef - The action definition.
|
|
847
|
+
* @param {object} contextData - The current workflow context.
|
|
848
|
+
* @param {object} user - The user object.
|
|
849
|
+
* @returns {Promise<{success: boolean, message?: string, updatedContext?: object}>}
|
|
850
|
+
*/
|
|
851
|
+
async function handleExecuteServiceFunction(actionDef, contextData, user) {
|
|
852
|
+
const { serviceName, functionName, args: argsTemplate } = actionDef;
|
|
853
|
+
|
|
854
|
+
if (!serviceName || !functionName) {
|
|
855
|
+
return { success: false, message: "Action requires 'serviceName' and 'functionName'." };
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
const service = services[serviceName];
|
|
859
|
+
if (!service) {
|
|
860
|
+
return { success: false, message: `Service '${serviceName}' not found in the registry.` };
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
const func = service[functionName];
|
|
864
|
+
if (typeof func !== 'function') {
|
|
865
|
+
return { success: false, message: `Function '${functionName}' not found in service '${serviceName}'.` };
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
try {
|
|
869
|
+
// Substitute variables in the arguments array
|
|
870
|
+
const substitutedArgs = Array.isArray(argsTemplate)
|
|
871
|
+
? await substituteVariables(argsTemplate, contextData, user)
|
|
872
|
+
: [];
|
|
873
|
+
|
|
874
|
+
logger.info(`[Service Call] Calling ${serviceName}.${functionName} with ${substitutedArgs.length} argument(s).`);
|
|
875
|
+
const result = await func(...substitutedArgs, user);
|
|
876
|
+
|
|
877
|
+
return {
|
|
878
|
+
success: true,
|
|
879
|
+
updatedContext: { serviceResult: result } // Store result in context
|
|
880
|
+
};
|
|
881
|
+
} catch (error) {
|
|
882
|
+
const msg = `Error executing ${serviceName}.${functionName}: ${error.message}`;
|
|
883
|
+
logger.error(`[Service Call] ${msg}`, error.stack);
|
|
884
|
+
return { success: false, message: msg };
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
789
888
|
// Dans workflow.js
|
|
790
889
|
export async function executeStepAction(actionDef, contextData, user, dbCollection) {
|
|
791
890
|
logger.info(`[executeStepAction] Executing action type ${actionDef.type} for action ${actionDef._id} (${actionDef.name})`);
|
|
@@ -797,8 +896,8 @@ export async function executeStepAction(actionDef, contextData, user, dbCollecti
|
|
|
797
896
|
logger.info(`[Workflow Log Action] Action: ${actionDef.name}. Contexte:`, contextData);
|
|
798
897
|
result = { success: true, message: 'Log action executed successfully.' }; // <--- CORRECTION
|
|
799
898
|
break;
|
|
800
|
-
case '
|
|
801
|
-
result = await
|
|
899
|
+
case 'HttpRequest':
|
|
900
|
+
result = await handleHttpRequestAction(actionDef, contextData, user, dbCollection);
|
|
802
901
|
break;
|
|
803
902
|
case 'CreateData':
|
|
804
903
|
result = await handleCreateDataAction(actionDef, contextData, user, dbCollection);
|
|
@@ -821,6 +920,9 @@ export async function executeStepAction(actionDef, contextData, user, dbCollecti
|
|
|
821
920
|
case 'ExecuteScript':
|
|
822
921
|
result = await executeSafeJavascript(actionDef, contextData, user);
|
|
823
922
|
break;
|
|
923
|
+
case 'ExecuteServiceFunction':
|
|
924
|
+
result = await handleExecuteServiceFunction(actionDef, contextData, user);
|
|
925
|
+
break;
|
|
824
926
|
default:
|
|
825
927
|
logger.error(`[executeStepAction] Unknown action type: ${actionDef.type}`);
|
|
826
928
|
return { success: false, message: `Unknown action type: ${actionDef.type}` };
|
|
@@ -1035,6 +1137,8 @@ export async function substituteVariables(template, contextData, user) {
|
|
|
1035
1137
|
return new Date().toISOString();
|
|
1036
1138
|
} else if (path === 'randomUUID') {
|
|
1037
1139
|
return crypto.randomUUID();
|
|
1140
|
+
} else if( path === "baseUrl" ){
|
|
1141
|
+
return process.env.NODE_ENV === 'production' ? 'https://'+getHost()+'/' : 'http://localhost:/'+port;
|
|
1038
1142
|
}
|
|
1039
1143
|
|
|
1040
1144
|
// Détecter si le chemin est complexe (contient plus d'un point)
|
|
@@ -1479,12 +1583,7 @@ async function executeGenerateAIContentAction(action, context, user) {
|
|
|
1479
1583
|
// 1. Retrieve the API key (User Environment > Machine Environment)
|
|
1480
1584
|
let apiKey;
|
|
1481
1585
|
|
|
1482
|
-
const
|
|
1483
|
-
"OpenAI" : "OPENAI_API_KEY",
|
|
1484
|
-
"Google": "GOOGLE_API_KEY",
|
|
1485
|
-
"DeepSeek": "DEEPSEEK_API_KEY"
|
|
1486
|
-
}
|
|
1487
|
-
const envKeyName = providers[aiProvider];
|
|
1586
|
+
const envKeyName = providers[aiProvider].key;
|
|
1488
1587
|
if( !envKeyName ) {
|
|
1489
1588
|
return {success: false, message: i18n.t('aiContent.env', `API key for provider ${aiProvider} (${envKeyName}) not found in user environment.`)};
|
|
1490
1589
|
}
|
|
@@ -1508,22 +1607,8 @@ async function executeGenerateAIContentAction(action, context, user) {
|
|
|
1508
1607
|
}
|
|
1509
1608
|
|
|
1510
1609
|
// 2. Initialize the LLM client with LangChain
|
|
1511
|
-
let llm;
|
|
1512
|
-
|
|
1513
|
-
switch (aiProvider) {
|
|
1514
|
-
case 'OpenAI':
|
|
1515
|
-
llm = new ChatOpenAI({ apiKey, model: aiModel, temperature: 0.7 });
|
|
1516
|
-
break;
|
|
1517
|
-
case 'Google':
|
|
1518
|
-
llm = new ChatGoogleGenerativeAI({ apiKey, model: aiModel, temperature: 0.7 });
|
|
1519
|
-
break;
|
|
1520
|
-
case 'DeepSeek':
|
|
1521
|
-
llm = new ChatDeepSeek({ apiKey, model: aiModel, temperature: 0.7 });
|
|
1522
|
-
break;
|
|
1523
|
-
default:
|
|
1524
|
-
throw new Error(`Unsupported AI provider: ${aiProvider}`);
|
|
1525
|
-
}
|
|
1526
|
-
} catch (initError) {
|
|
1610
|
+
let llm = getAIProvider(aiProvider, aiModel, apiKey);
|
|
1611
|
+
if( !llm ) {
|
|
1527
1612
|
const message = `Failed to initialize AI client for ${aiProvider}: ${initError.message}`;
|
|
1528
1613
|
logger.error(`[AI Action] ${message}`);
|
|
1529
1614
|
return { success: false, message };
|