data-primals-engine 1.6.0 → 1.6.2-rc1
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 +968 -924
- package/client/package-lock.json +524 -5
- package/client/package.json +2 -1
- package/client/src/App.scss +14 -1
- package/client/src/Dashboard.jsx +4 -0
- package/client/src/DataLayout.jsx +85 -20
- package/client/src/DataLayout.scss +32 -6
- package/client/src/DataTable.jsx +19 -12
- package/client/src/ModelCreator.jsx +12 -12
- package/client/src/ModelCreator.scss +1 -1
- package/client/src/ModelImporter.jsx +4 -1
- package/client/src/ViewSwitcher.jsx +1 -1
- package/client/src/WorkflowEditor.jsx +317 -0
- package/client/src/WorkflowEditor.scss +16 -0
- package/package.json +142 -142
- package/src/defaultModels.js +12 -2
- package/src/i18n.js +35 -3
- package/src/index.js +1 -0
- package/src/modules/data/data.operations.js +91 -5
- package/src/modules/user.js +14 -9
- package/src/modules/workflow.js +1 -4
- package/src/packs.js +239 -24
- package/test/data.integration.test.js +75 -0
- package/test/user.test.js +106 -1
package/src/i18n.js
CHANGED
|
@@ -5,6 +5,10 @@ import LanguageDetector from "i18next-browser-languagedetector";
|
|
|
5
5
|
export const translations = {
|
|
6
6
|
fr: {
|
|
7
7
|
translation: {
|
|
8
|
+
|
|
9
|
+
"model_audience": "Audiences",
|
|
10
|
+
"model_deal": "Opportunités",
|
|
11
|
+
|
|
8
12
|
"relationField.select.title": "Sélectionner {{modelName}}",
|
|
9
13
|
"btns.validate": "Valider",
|
|
10
14
|
"btns.create": "Créer",
|
|
@@ -1318,6 +1322,8 @@ export const translations = {
|
|
|
1318
1322
|
},
|
|
1319
1323
|
en: {
|
|
1320
1324
|
translation: {
|
|
1325
|
+
"model_audience": "Audiences",
|
|
1326
|
+
"model_deal": "Opportunities",
|
|
1321
1327
|
"relationField.select.title": "Select {{modelName}}",
|
|
1322
1328
|
"btns.validate": "Validate",
|
|
1323
1329
|
"btns.create": "Create",
|
|
@@ -2627,6 +2633,8 @@ export const translations = {
|
|
|
2627
2633
|
},
|
|
2628
2634
|
es: {
|
|
2629
2635
|
translation: {
|
|
2636
|
+
"model_audience": "Audiencias",
|
|
2637
|
+
"model_deal": "Oportunidades",
|
|
2630
2638
|
"relationField.select.title": "Seleccionar {{modelName}}",
|
|
2631
2639
|
"btns.validate": "Validar",
|
|
2632
2640
|
"btns.create": "Crear",
|
|
@@ -3973,6 +3981,10 @@ export const translations = {
|
|
|
3973
3981
|
},
|
|
3974
3982
|
pt: {
|
|
3975
3983
|
translation: {
|
|
3984
|
+
|
|
3985
|
+
"model_audience": "Públicos",
|
|
3986
|
+
"model_campaign": "Campanhas de marketing",
|
|
3987
|
+
"model_deal": "Oportunidades",
|
|
3976
3988
|
"relationField.select.title": "Selecionar {{modelName}}",
|
|
3977
3989
|
"btns.validate": "Validar",
|
|
3978
3990
|
"btns.create": "Criar",
|
|
@@ -4125,6 +4137,8 @@ export const translations = {
|
|
|
4125
4137
|
},
|
|
4126
4138
|
de: {
|
|
4127
4139
|
translation: {
|
|
4140
|
+
"model_audience": "Zielgruppen",
|
|
4141
|
+
"model_deal": "Möglichkeiten",
|
|
4128
4142
|
"relationField.select.title": "{{modelName}} auswählen",
|
|
4129
4143
|
"btns.validate": "Validieren",
|
|
4130
4144
|
"btns.create": "Erstellen",
|
|
@@ -5424,7 +5438,6 @@ export const translations = {
|
|
|
5424
5438
|
"field_endpoint_isPublic_hint": "Se selezionato, questo endpoint sarà accessibile senza autenticazione. Lo script verrà eseguito con i permessi del proprietario dell'endpoint.",
|
|
5425
5439
|
"field_endpoint_code": "Codice da eseguire",
|
|
5426
5440
|
"field_endpoint_code_hint": "Lo script da eseguire. Deve restituire un valore o un oggetto che sarà la risposta JSON.",
|
|
5427
|
-
"model_env": "Variabili d'Ambiente (env)",
|
|
5428
5441
|
"datatable.advancedFilter.title": "Filtro avanzato (MongoDB)",
|
|
5429
5442
|
"datatable.advancedFilter.desc": "Crea i tuoi filtri avanzati da zero, utilizzando gli operatori di aggregazione di MongoDB disponibili qui.",
|
|
5430
5443
|
"editData": "Modifica in {{0}}",
|
|
@@ -5696,6 +5709,10 @@ export const translations = {
|
|
|
5696
5709
|
"model_channel": "Canali di notifica",
|
|
5697
5710
|
"model_message": "Messaggi",
|
|
5698
5711
|
"model_kpi": "Indicatori KPI",
|
|
5712
|
+
"model_env": "Variabili d'Ambiente (env)",
|
|
5713
|
+
"model_audience": "Pubblici",
|
|
5714
|
+
"model_deal": "Opportunità",
|
|
5715
|
+
|
|
5699
5716
|
"field_permission_name": "Nome",
|
|
5700
5717
|
"field_permission_description": "Descrizione",
|
|
5701
5718
|
|
|
@@ -6628,6 +6645,9 @@ export const translations = {
|
|
|
6628
6645
|
},
|
|
6629
6646
|
cs: {
|
|
6630
6647
|
translation: {
|
|
6648
|
+
"model_audience": "Publika",
|
|
6649
|
+
"model_campaign": "Marketingové kampaně",
|
|
6650
|
+
"model_deal": "Příležitosti",
|
|
6631
6651
|
"relationField.select.title": "Vybrat {{modelName}}",
|
|
6632
6652
|
"btns.validate": "Ověřit",
|
|
6633
6653
|
"btns.create": "Vytvořit",
|
|
@@ -7553,6 +7573,8 @@ export const translations = {
|
|
|
7553
7573
|
},
|
|
7554
7574
|
ru: {
|
|
7555
7575
|
translation: {
|
|
7576
|
+
"model_audience": "Аудитории",
|
|
7577
|
+
"model_deal": "Возможности",
|
|
7556
7578
|
"relationField.select.title": "Выбрать {{modelName}}",
|
|
7557
7579
|
"btns.validate": "Проверить",
|
|
7558
7580
|
"btns.create": "Создать",
|
|
@@ -8832,6 +8854,8 @@ export const translations = {
|
|
|
8832
8854
|
},
|
|
8833
8855
|
ar: {
|
|
8834
8856
|
translation: {
|
|
8857
|
+
"model_audience": "الجماهير",
|
|
8858
|
+
"model_deal": "الفرص",
|
|
8835
8859
|
"relationField.select.title": "اختر {{modelName}}",
|
|
8836
8860
|
"btns.validate": "تحقق",
|
|
8837
8861
|
"btns.create": "إنشاء",
|
|
@@ -8901,7 +8925,7 @@ export const translations = {
|
|
|
8901
8925
|
"field_endpoint_code": "الكود المنفذ",
|
|
8902
8926
|
"field_endpoint_code_hint": "السكريبت الذي سيتم تنفيذه. يجب أن يعيد قيمة أو كائنًا سيكون استجابة JSON.",
|
|
8903
8927
|
"model_env": "متغيرات البيئة (env)",
|
|
8904
|
-
|
|
8928
|
+
|
|
8905
8929
|
"datatable.advancedFilter.title": "مرشح متقدم (MongoDB)",
|
|
8906
8930
|
"datatable.advancedFilter.desc": "أنشئ مرشحاتك المتقدمة من الصفر، باستخدام عوامل تجميع MongoDB المتوفرة هنا",
|
|
8907
8931
|
"editData": "تعديل في {{0}}",
|
|
@@ -10156,6 +10180,8 @@ export const translations = {
|
|
|
10156
10180
|
},
|
|
10157
10181
|
sv: {
|
|
10158
10182
|
translation: {
|
|
10183
|
+
"model_audience": "Målgrupper",
|
|
10184
|
+
"model_deal": "Möjligheter",
|
|
10159
10185
|
"relationField.select.title": "Välj {{modelName}}",
|
|
10160
10186
|
"btns.validate": "Validera",
|
|
10161
10187
|
"btns.create": "Skapa",
|
|
@@ -11316,7 +11342,7 @@ export const translations = {
|
|
|
11316
11342
|
"no": "Nej",
|
|
11317
11343
|
"true": "Sant",
|
|
11318
11344
|
"false": "Falskt",
|
|
11319
|
-
|
|
11345
|
+
|
|
11320
11346
|
|
|
11321
11347
|
"assistant.type": "Skriv ditt meddelande...",
|
|
11322
11348
|
"assistant.welcome": "Hej! Hur kan jag hjälpa dig med din data?",
|
|
@@ -11383,6 +11409,10 @@ export const translations = {
|
|
|
11383
11409
|
},
|
|
11384
11410
|
el: {
|
|
11385
11411
|
translation: {
|
|
11412
|
+
"model_audience": "Κοινά",
|
|
11413
|
+
"model_deal": "Ευκαιρίες",
|
|
11414
|
+
"model_endpoint": "Τελικά σημεία",
|
|
11415
|
+
"model_env": "Μεταβλητές Περιβάλλοντος",
|
|
11386
11416
|
"relationField.select.title": "Επιλογή {{modelName}}",
|
|
11387
11417
|
"btns.validate": "Επικύρωση",
|
|
11388
11418
|
"btns.create": "Δημιουργία",
|
|
@@ -12586,6 +12616,8 @@ export const translations = {
|
|
|
12586
12616
|
},
|
|
12587
12617
|
fa: {
|
|
12588
12618
|
translation: {
|
|
12619
|
+
"model_audience": "مخاطبان",
|
|
12620
|
+
"model_deal": "فرصتها",
|
|
12589
12621
|
"relationField.select.title": "{{modelName}} را انتخاب کنید",
|
|
12590
12622
|
"btns.validate": "اعتبارسنجی",
|
|
12591
12623
|
"btns.create": "ایجاد",
|
package/src/index.js
CHANGED
|
@@ -7,6 +7,7 @@ export { Config } from './config.js';
|
|
|
7
7
|
export { event_on, event_trigger, event_off } from './core.js';
|
|
8
8
|
export { Event } from './events.js';
|
|
9
9
|
|
|
10
|
+
export { hasPermission, middlewareAuthenticator, getEnv, getSmtpConfig } from "./modules/user.js"
|
|
10
11
|
export { sendSseToUser } from './modules/data/data.routes.js';
|
|
11
12
|
|
|
12
13
|
export { UserProvider } from './providers.js';
|
|
@@ -459,6 +459,12 @@ export const dataTypes = {
|
|
|
459
459
|
}
|
|
460
460
|
};
|
|
461
461
|
|
|
462
|
+
const userStorageCache = new NodeCache({
|
|
463
|
+
stdTTL: 600, // 10 minutes
|
|
464
|
+
checkperiod: 120,
|
|
465
|
+
useClones: false
|
|
466
|
+
});
|
|
467
|
+
|
|
462
468
|
|
|
463
469
|
|
|
464
470
|
let engine, logger;
|
|
@@ -466,6 +472,29 @@ export function onInit(defaultEngine) {
|
|
|
466
472
|
engine = defaultEngine;
|
|
467
473
|
logger = engine.getComponent(Logger);
|
|
468
474
|
|
|
475
|
+
// Précalculer périodiquement l'utilisation du stockage pour chaque utilisateur
|
|
476
|
+
const updateUserStorageUsage = async () => {
|
|
477
|
+
logger.debug('[Storage] Starting periodic user storage calculation...');
|
|
478
|
+
try {
|
|
479
|
+
const usersCollection = getCollection('users');
|
|
480
|
+
const users = await usersCollection.find({}, { projection: { username: 1 } }).toArray();
|
|
481
|
+
|
|
482
|
+
for (const user of users) {
|
|
483
|
+
try {
|
|
484
|
+
const usage = await calculateTotalUserStorageUsage(user);
|
|
485
|
+
userStorageCache.set(user.username, usage);
|
|
486
|
+
} catch (userError) {
|
|
487
|
+
logger.error(`[Storage] Failed to calculate storage for user ${user.username}:`, userError);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
logger.debug(`[Storage] User storage calculation finished for ${users.length} users.`);
|
|
491
|
+
} catch (e) {
|
|
492
|
+
logger.error('[Storage] Error during periodic user storage calculation:', e);
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
setInterval(updateUserStorageUsage, 300000); // Toutes les 5 minutes
|
|
497
|
+
updateUserStorageUsage(); // Lancer une première fois au démarrage
|
|
469
498
|
// Nettoyer périodiquement les relations obsolètes
|
|
470
499
|
setInterval(() => {
|
|
471
500
|
// Nettoyer les relations pour les clés qui n'existent plus
|
|
@@ -990,8 +1019,11 @@ async function checkLimits(datas, model, collection, me) {
|
|
|
990
1019
|
const userStorageLimit = await engine.userProvider.getUserStorageLimit(me);
|
|
991
1020
|
|
|
992
1021
|
// Vérification des limites utilisateur
|
|
993
|
-
|
|
994
|
-
if (currentStorageUsage
|
|
1022
|
+
let currentStorageUsage = userStorageCache.get(me.username);
|
|
1023
|
+
if (currentStorageUsage === undefined) {
|
|
1024
|
+
currentStorageUsage = await calculateTotalUserStorageUsage(me); // Fallback si le cache est vide
|
|
1025
|
+
}
|
|
1026
|
+
if ((currentStorageUsage || 0) + incomingDataSize > userStorageLimit) {
|
|
995
1027
|
throw new Error(i18n.t("api.data.storageLimitExceeded", {
|
|
996
1028
|
limit: Math.round(userStorageLimit / megabytes)
|
|
997
1029
|
}));
|
|
@@ -1074,7 +1106,7 @@ const internalEditOrPatchData = async (modelName, filter, data, files, user, isP
|
|
|
1074
1106
|
const updatePushData = {};
|
|
1075
1107
|
|
|
1076
1108
|
for (const key in data) {
|
|
1077
|
-
if (key.startsWith('_')) continue;
|
|
1109
|
+
if (key.startsWith('_') && !['_env', '_pack'].includes(key)) continue;
|
|
1078
1110
|
|
|
1079
1111
|
if (isPlainObject(data[key]) && data[key].$push !== undefined) {
|
|
1080
1112
|
// C'est une opération $push
|
|
@@ -1251,6 +1283,43 @@ const internalEditOrPatchData = async (modelName, filter, data, files, user, isP
|
|
|
1251
1283
|
}
|
|
1252
1284
|
}
|
|
1253
1285
|
|
|
1286
|
+
// 8. Vérification des limites de stockage avant la mise à jour
|
|
1287
|
+
const originalDocsSize = calculateDataSize(existingDocs);
|
|
1288
|
+
const finalStateForSize = existingDocs.map(doc => {
|
|
1289
|
+
const updatedDoc = { ...doc, ...updateSetData };
|
|
1290
|
+
for (const fieldName in updatePushData) {
|
|
1291
|
+
let itemsToAdd;
|
|
1292
|
+
const pushValue = updatePushData[fieldName];
|
|
1293
|
+
if (isPlainObject(pushValue) && pushValue.$each) {
|
|
1294
|
+
itemsToAdd = pushValue.$each;
|
|
1295
|
+
} else if (Array.isArray(pushValue)) {
|
|
1296
|
+
itemsToAdd = pushValue;
|
|
1297
|
+
} else {
|
|
1298
|
+
itemsToAdd = [pushValue];
|
|
1299
|
+
}
|
|
1300
|
+
if (!Array.isArray(updatedDoc[fieldName])) {
|
|
1301
|
+
updatedDoc[fieldName] = [];
|
|
1302
|
+
}
|
|
1303
|
+
updatedDoc[fieldName].push(...itemsToAdd);
|
|
1304
|
+
}
|
|
1305
|
+
return updatedDoc;
|
|
1306
|
+
});
|
|
1307
|
+
|
|
1308
|
+
const finalDocsSize = calculateDataSize(finalStateForSize);
|
|
1309
|
+
const sizeDelta = finalDocsSize - originalDocsSize;
|
|
1310
|
+
|
|
1311
|
+
if (sizeDelta > 0) {
|
|
1312
|
+
const userStorageLimit = await engine.userProvider.getUserStorageLimit(user);
|
|
1313
|
+
const currentStorageUsage = await calculateTotalUserStorageUsage(user);
|
|
1314
|
+
if (currentStorageUsage + sizeDelta > userStorageLimit) {
|
|
1315
|
+
throw new Error(i18n.t("api.data.storageLimitExceeded", { limit: Math.round(userStorageLimit / megabytes) }));
|
|
1316
|
+
}
|
|
1317
|
+
const serverCapacity = await checkServerCapacity(sizeDelta);
|
|
1318
|
+
if (!serverCapacity.isSufficient) {
|
|
1319
|
+
throw new Error(i18n.t("api.data.serverStorageFull"));
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1254
1323
|
// 8. Calcul du nouveau hash en simulant l'état final
|
|
1255
1324
|
const finalStateForHash = { ...existingDocs[0], ...updateSetData };
|
|
1256
1325
|
for (const fieldName in updatePushData) {
|
|
@@ -1694,7 +1763,8 @@ const generateCacheKey = (query, user) => {
|
|
|
1694
1763
|
filter: query.filter,
|
|
1695
1764
|
depth: query.depth,
|
|
1696
1765
|
autoExpand: query.autoExpand,
|
|
1697
|
-
pack: query.pack
|
|
1766
|
+
pack: query.pack,
|
|
1767
|
+
env: query.env
|
|
1698
1768
|
},
|
|
1699
1769
|
user: user.username
|
|
1700
1770
|
};
|
|
@@ -1710,7 +1780,7 @@ const searchCache = new NodeCache({
|
|
|
1710
1780
|
});
|
|
1711
1781
|
|
|
1712
1782
|
export const searchData = async (query, user) => {
|
|
1713
|
-
const {page, limit, sort, model, pipelinesPosition, pipelines: customPipelines = [], ids, timeout, pack} = query;
|
|
1783
|
+
const {page, limit, sort, model, pipelinesPosition, pipelines: customPipelines = [], ids, timeout, pack, env} = query;
|
|
1714
1784
|
|
|
1715
1785
|
if (user && user.username !== 'demo' && isLocalUser(user) && (
|
|
1716
1786
|
!await hasPermission(["API_ADMIN", "API_SEARCH_DATA", "API_SEARCH_DATA_" + model], user) ||
|
|
@@ -2162,8 +2232,18 @@ export const searchData = async (query, user) => {
|
|
|
2162
2232
|
}
|
|
2163
2233
|
}
|
|
2164
2234
|
|
|
2235
|
+
let envMatchStage;
|
|
2236
|
+
if (env === 'production') {
|
|
2237
|
+
envMatchStage = {$match: {$or: [{'_env': 'production'}, {'_env': {$exists: false}}]}};
|
|
2238
|
+
} else if (env) {
|
|
2239
|
+
envMatchStage = {$match: {'_env': env}};
|
|
2240
|
+
} else {
|
|
2241
|
+
envMatchStage = {$match: {'_env': {$exists: false}}};
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2165
2244
|
return pipelines.concat(
|
|
2166
2245
|
[
|
|
2246
|
+
envMatchStage,
|
|
2167
2247
|
{$match: {'_pack': pack ? pack : {$exists: false}}},
|
|
2168
2248
|
{$match: {$expr: dataNoRelation}}
|
|
2169
2249
|
],
|
|
@@ -3128,6 +3208,7 @@ export async function installPack(packIdentifier, user = null, lang = 'en', opti
|
|
|
3128
3208
|
delete docForInsert._temp_pack_id;
|
|
3129
3209
|
|
|
3130
3210
|
if (user) docForInsert._user = username;
|
|
3211
|
+
|
|
3131
3212
|
docForInsert._model = modelName;
|
|
3132
3213
|
docForInsert._hash = getFieldValueHash(modelDefForHash, docForInsert);
|
|
3133
3214
|
|
|
@@ -3136,6 +3217,8 @@ export async function installPack(packIdentifier, user = null, lang = 'en', opti
|
|
|
3136
3217
|
_hash: docForInsert._hash,
|
|
3137
3218
|
_model: modelName
|
|
3138
3219
|
};
|
|
3220
|
+
|
|
3221
|
+
if (docForInsert._env) existingQuery._env = docForInsert._env;
|
|
3139
3222
|
if (user) existingQuery._user = username;
|
|
3140
3223
|
|
|
3141
3224
|
const existingDoc = await collection.findOne(existingQuery, {projection: {_id: 1}});
|
|
@@ -3159,6 +3242,9 @@ export async function installPack(packIdentifier, user = null, lang = 'en', opti
|
|
|
3159
3242
|
const result = await collection.insertMany(finalDocsToInsert, {ordered: false});
|
|
3160
3243
|
summary.datas.inserted += result.insertedCount;
|
|
3161
3244
|
|
|
3245
|
+
// Invalider le cache pour le modèle qui vient d'être modifié
|
|
3246
|
+
invalidateModelCache(modelName);
|
|
3247
|
+
|
|
3162
3248
|
docsToInsert.forEach((doc, index) => {
|
|
3163
3249
|
if (result.insertedIds[index]) {
|
|
3164
3250
|
tempIdToNewIdMap[doc._temp_pack_id_for_mapping] = result.insertedIds[index];
|
package/src/modules/user.js
CHANGED
|
@@ -108,7 +108,7 @@ export async function onInit(defaultEngine) {
|
|
|
108
108
|
* @returns {Promise<Set<string>>} Un Set contenant les noms de toutes les permissions actives.
|
|
109
109
|
* @private
|
|
110
110
|
*/
|
|
111
|
-
export async function getUserActivePermissions(user) {
|
|
111
|
+
export async function getUserActivePermissions(user, env = null) {
|
|
112
112
|
const datasCollection = await getCollectionForUser(user);
|
|
113
113
|
const now = new Date();
|
|
114
114
|
const activePermissions = new Set();
|
|
@@ -139,12 +139,17 @@ export async function getUserActivePermissions(user) {
|
|
|
139
139
|
// --- ÉTAPE 2: Appliquer les exceptions de permission ---
|
|
140
140
|
const exceptions = await datasCollection.aggregate([
|
|
141
141
|
{
|
|
142
|
-
$match: {
|
|
142
|
+
$match: { // Filtre de base pour les exceptions de l'utilisateur
|
|
143
143
|
_model: "userPermission",
|
|
144
|
-
user: user._id,
|
|
145
|
-
$
|
|
146
|
-
{
|
|
147
|
-
|
|
144
|
+
user: user._id,
|
|
145
|
+
$and: [ // Filtre sur l'environnement et l'expiration
|
|
146
|
+
{
|
|
147
|
+
$or: [ // La permission est soit globale, soit spécifique à l'environnement demandé
|
|
148
|
+
{ env: { $exists: false } },
|
|
149
|
+
{ env: env }
|
|
150
|
+
]
|
|
151
|
+
},
|
|
152
|
+
{ $or: [{ expiresAt: { $exists: false } }, { expiresAt: { $gt: now } }] }
|
|
148
153
|
]
|
|
149
154
|
}
|
|
150
155
|
},
|
|
@@ -193,7 +198,7 @@ export async function getUserActivePermissions(user) {
|
|
|
193
198
|
* @param {object} user - L'objet utilisateur authentifié.
|
|
194
199
|
* @returns {Promise<boolean>} - True si l'utilisateur a la permission, sinon false.
|
|
195
200
|
*/
|
|
196
|
-
export async function hasPermission(permissionNames, user) {
|
|
201
|
+
export async function hasPermission(permissionNames, user, env = null) {
|
|
197
202
|
// Garde la compatibilité pour les utilisateurs non-locaux (ex: système)
|
|
198
203
|
if (!isLocalUser(user)) {
|
|
199
204
|
const userRoles = new Set(user.roles || []);
|
|
@@ -209,10 +214,10 @@ export async function hasPermission(permissionNames, user) {
|
|
|
209
214
|
}
|
|
210
215
|
|
|
211
216
|
// 1. Obtenir l'ensemble final et à jour des permissions de l'utilisateur
|
|
212
|
-
const activePermissions = await getUserActivePermissions(user);
|
|
217
|
+
const activePermissions = await getUserActivePermissions(user, env);
|
|
213
218
|
|
|
214
219
|
// 2. Vérifier si au moins une des permissions requises est dans l'ensemble des permissions actives
|
|
215
|
-
return requiredPermissions.some(pName => activePermissions.has(pName));
|
|
220
|
+
return requiredPermissions.some(pName => activePermissions.has(pName)) || false;
|
|
216
221
|
|
|
217
222
|
} catch (e) {
|
|
218
223
|
logger.error("Erreur lors de la vérification des permissions :", e);
|
package/src/modules/workflow.js
CHANGED
|
@@ -1380,10 +1380,7 @@ export async function triggerWorkflows(triggerData, user, eventType) {
|
|
|
1380
1380
|
}
|
|
1381
1381
|
}
|
|
1382
1382
|
|
|
1383
|
-
return
|
|
1384
|
-
await trigger(triggerData, user, eventType);
|
|
1385
|
-
resolve();
|
|
1386
|
-
}, 0));
|
|
1383
|
+
return trigger(triggerData, user, eventType);
|
|
1387
1384
|
}
|
|
1388
1385
|
/**
|
|
1389
1386
|
* Processes a workflowRun instance step-by-step.
|