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/packs.js
CHANGED
|
@@ -135,21 +135,20 @@ export const getAllPacks = async () => {
|
|
|
135
135
|
}
|
|
136
136
|
],
|
|
137
137
|
"dashboard": [{
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
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
|
}
|
package/src/providers.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {compare} from "bcrypt";
|
|
2
|
+
import {maxTotalPrivateFilesSize} from "./constants.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* @class UserProvider
|
|
@@ -6,11 +7,15 @@ import {compare} from "bcrypt";
|
|
|
6
7
|
* C'est une classe de base abstraite, elle n'est pas destinée à être utilisée directement.
|
|
7
8
|
*/
|
|
8
9
|
export class UserProvider {
|
|
10
|
+
|
|
11
|
+
plans = {};
|
|
12
|
+
|
|
9
13
|
constructor(engine) {
|
|
10
14
|
if (this.constructor === UserProvider) {
|
|
11
15
|
throw new Error("Abstract classes can't be instantiated.");
|
|
12
16
|
}
|
|
13
17
|
this.engine = engine;
|
|
18
|
+
this.plans = this.getUserPlans();
|
|
14
19
|
}
|
|
15
20
|
|
|
16
21
|
/**
|
|
@@ -50,6 +55,45 @@ export class UserProvider {
|
|
|
50
55
|
throw new Error("Method 'initiateUser()' must be implemented.");
|
|
51
56
|
}
|
|
52
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Récupère la limite de stockage d'un utilisateur.
|
|
60
|
+
* @param user
|
|
61
|
+
* @returns {Promise<number>}
|
|
62
|
+
*/
|
|
63
|
+
async getUserStorageLimit(user) {
|
|
64
|
+
return maxTotalPrivateFilesSize;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Récupère la fréquence de sauvegarde d'un utilisateur.
|
|
69
|
+
* @param user
|
|
70
|
+
* @returns {Promise<void>}
|
|
71
|
+
*/
|
|
72
|
+
async getBackupFrequency(user){
|
|
73
|
+
return 'daily';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Récupère les middlewares d'un utilisateur.
|
|
78
|
+
* @returns {Promise<*[]>}
|
|
79
|
+
*/
|
|
80
|
+
async getMiddlewares(){
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getUserPlans(){
|
|
85
|
+
return {
|
|
86
|
+
free: {
|
|
87
|
+
requestLimitPerHour: 10000,
|
|
88
|
+
features: ['indexes'] // Feature list : indexes is the only option for now. Use a dedicated collection for this user
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async hasFeature(user, feature) {
|
|
94
|
+
return this.plans[user.userPlan]?.features.some(f => f === feature);
|
|
95
|
+
}
|
|
96
|
+
|
|
53
97
|
// Ajoutez ici d'autres méthodes nécessaires : findUserById, createUser, etc.
|
|
54
98
|
}
|
|
55
99
|
|
|
@@ -58,19 +102,73 @@ export class DefaultUserProvider extends UserProvider {
|
|
|
58
102
|
|
|
59
103
|
users= [{ username: "demo", password: "demo" }];
|
|
60
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Trouve un utilisateur. Gère spécifiquement les utilisateurs de démo.
|
|
107
|
+
*/
|
|
61
108
|
async findUserByUsername(username) {
|
|
109
|
+
// Si le nom d'utilisateur commence par "demo", on le considère comme un utilisateur de démo valide et volatile.
|
|
110
|
+
if (typeof username === 'string' && username.startsWith('demo')) {
|
|
111
|
+
// On retourne un objet utilisateur de démo avec la structure attendue.
|
|
112
|
+
return { username: username, password: "demo", userPlan: 'free' }; // Ajout de userPlan pour la cohérence
|
|
113
|
+
}
|
|
114
|
+
// Logique pour les vrais utilisateurs (si vous en ajoutez plus tard)
|
|
62
115
|
return this.users.find(user => user.username === username);
|
|
63
116
|
}
|
|
64
117
|
|
|
118
|
+
async getUserStorageLimit(user) {
|
|
119
|
+
return maxTotalPrivateFilesSize;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async getBackupFrequency(user){
|
|
123
|
+
return 'daily';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async getMiddlewares(){
|
|
127
|
+
return []
|
|
128
|
+
}
|
|
129
|
+
|
|
65
130
|
async validatePassword(user, password) {
|
|
66
|
-
|
|
131
|
+
// Pour un utilisateur de démo, le mot de passe est toujours valide.
|
|
132
|
+
if (user.username.startsWith('demo')) {
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
// Logique pour les vrais utilisateurs
|
|
136
|
+
return true; // ou votre logique de comparaison bcrypt
|
|
67
137
|
}
|
|
68
138
|
|
|
69
139
|
async updateUser(user, data) {
|
|
70
140
|
this.users = this.users.map(user => user.username === user.username ? {...user, ...data} : user);
|
|
71
141
|
}
|
|
72
|
-
|
|
142
|
+
/**
|
|
143
|
+
* Initialise l'utilisateur sur la requête. C'est ici que la magie opère.
|
|
144
|
+
*/
|
|
73
145
|
async initiateUser(req) {
|
|
74
|
-
|
|
146
|
+
// Priorité 1: Un utilisateur est déjà authentifié via une session (pour les vrais comptes).
|
|
147
|
+
if (req.session?.user?.username) {
|
|
148
|
+
const user = await this.findUserByUsername(req.session.user.username);
|
|
149
|
+
if (user) {
|
|
150
|
+
req.me = user;
|
|
151
|
+
return; // L'utilisateur est trouvé, on s'arrête là.
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Priorité 2: PasF de session, mais on vérifie la présence d'un cookie "username" pour la démo.
|
|
156
|
+
const demoUsername = req.cookies?.username;
|
|
157
|
+
if (demoUsername && typeof demoUsername === 'string' && demoUsername.startsWith('demo')) {
|
|
158
|
+
// On a trouvé un cookie de démo. On crée l'objet utilisateur correspondant.
|
|
159
|
+
const demoUser = await this.findUserByUsername(demoUsername);
|
|
160
|
+
if (demoUser) {
|
|
161
|
+
req.me = demoUser;
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
getUserPlans(){
|
|
168
|
+
return {
|
|
169
|
+
free: {
|
|
170
|
+
features: ['indexes']
|
|
171
|
+
}
|
|
172
|
+
}
|
|
75
173
|
}
|
|
76
174
|
}
|
package/src/setenv.js
CHANGED
package/src/user.js
CHANGED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
maxTotalPrivateFilesSizeFree,
|
|
3
|
-
maxTotalPrivateFilesSizePremium,
|
|
4
|
-
maxTotalPrivateFilesSizeStandard
|
|
5
|
-
} from "./constants.js";
|
|
6
|
-
|
|
7
|
-
export const getUserStorageLimit = (user) => {
|
|
8
|
-
switch (user.userPlan) {
|
|
9
|
-
case 'premium':
|
|
10
|
-
return maxTotalPrivateFilesSizePremium;
|
|
11
|
-
case 'standard':
|
|
12
|
-
return maxTotalPrivateFilesSizeStandard;
|
|
13
|
-
case 'free':
|
|
14
|
-
default:
|
|
15
|
-
return maxTotalPrivateFilesSizeFree;
|
|
16
|
-
}
|
|
17
|
-
};
|
|
@@ -46,29 +46,30 @@ parentPort.on('message', async ({ action, payload }) => {
|
|
|
46
46
|
try {
|
|
47
47
|
let result;
|
|
48
48
|
switch (action) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
case 'encrypt':
|
|
50
|
+
console.log(`[Worker] Received action: ${action} for file: ${payload.filePath}`);
|
|
51
|
+
await encryptFile(payload.filePath, payload.password);
|
|
52
|
+
parentPort.postMessage({ success: true }); // Pas de données à retourner
|
|
53
|
+
break;
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
case 'decrypt':
|
|
56
|
+
console.log(`[Worker] Received action: ${action} for file: ${payload.filePath}`);
|
|
57
|
+
await decryptFile(payload.filePath, payload.password);
|
|
58
|
+
parentPort.postMessage({ success: true }); // Pas de données à retourner
|
|
59
|
+
break;
|
|
60
|
+
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
case 'hash':
|
|
63
|
+
console.log(`[Worker] Received action: hash`);
|
|
64
|
+
if (!payload.data) throw new Error('Data to hash is missing in payload.');
|
|
65
|
+
// Le worker gère à la fois la génération du "salt" et le hachage
|
|
66
|
+
const salt = await bcrypt.genSalt(10);
|
|
67
|
+
result = await bcrypt.hash(payload.data, salt);
|
|
68
|
+
parentPort.postMessage({ success: true, data: result }); // On retourne le hash
|
|
69
|
+
break;
|
|
69
70
|
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
default:
|
|
72
|
+
throw new Error(`Unknown crypto action: ${action}`);
|
|
72
73
|
}
|
|
73
74
|
} catch (error) {
|
|
74
75
|
// Envoie l'erreur au thread principal
|
|
@@ -5,27 +5,27 @@ parentPort.on('message', ({ action, payload }) => {
|
|
|
5
5
|
try {
|
|
6
6
|
let result;
|
|
7
7
|
switch (action) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
case 'parse-json':
|
|
9
|
+
// Tâche: Parser une chaîne/buffer JSON en objet JS
|
|
10
|
+
result = JSON.parse(payload.fileContent.toString('utf-8'));
|
|
11
|
+
break;
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
case 'parse-csv':
|
|
14
|
+
// Tâche: Parser une chaîne/buffer CSV en tableau d'objets JS
|
|
15
|
+
result = parse(payload.fileContent, {
|
|
16
|
+
columns: payload.options.columns,
|
|
17
|
+
skip_empty_lines: true,
|
|
18
|
+
trim: true
|
|
19
|
+
});
|
|
20
|
+
break;
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
case 'stringify-json':
|
|
23
|
+
// Tâche: Sérialiser un objet/tableau JS en chaîne JSON
|
|
24
|
+
result = JSON.stringify(payload.data);
|
|
25
|
+
break;
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
default:
|
|
28
|
+
throw new Error(`Unknown action: ${action}`);
|
|
29
29
|
}
|
|
30
30
|
// Renvoyer le résultat au thread principal
|
|
31
31
|
parentPort.postMessage({ success: true, data: result });
|
|
@@ -6,25 +6,21 @@ import { Config } from '../src/config.js';
|
|
|
6
6
|
import { ObjectId } from 'mongodb';
|
|
7
7
|
import {expect, describe, it, beforeAll, afterAll, beforeEach} from 'vitest';
|
|
8
8
|
import { vi } from 'vitest'
|
|
9
|
-
import { Buffer } from 'node:buffer'; // Explicitly import Buffer
|
|
10
|
-
import crypto from 'node:crypto'; //Explicitly import crypto
|
|
11
9
|
|
|
12
10
|
import {
|
|
13
11
|
createModel,
|
|
14
|
-
|
|
12
|
+
insertData
|
|
15
13
|
} from 'data-primals-engine/modules/data';
|
|
16
14
|
|
|
17
15
|
import {
|
|
18
16
|
modelsCollection as getAppModelsCollection,
|
|
19
|
-
getCollectionForUser as getAppUserCollection
|
|
17
|
+
getCollectionForUser as getAppUserCollection
|
|
20
18
|
} from 'data-primals-engine/modules/mongodb';
|
|
21
|
-
import { Engine } from "data-primals-engine/engine";
|
|
22
19
|
import process from "node:process";
|
|
23
20
|
|
|
24
|
-
import { dumpUserData, loadFromDump
|
|
21
|
+
import { dumpUserData, loadFromDump } from 'data-primals-engine/modules/data';
|
|
25
22
|
import fs from "node:fs";
|
|
26
|
-
import {
|
|
27
|
-
import {getUniquePort, initEngine, stopEngine} from "../src/setenv.js";
|
|
23
|
+
import {initEngine} from "../src/setenv.js";
|
|
28
24
|
|
|
29
25
|
vi.mock('data-primals-engine/engine', async(importOriginal) => {
|
|
30
26
|
const mod = await importOriginal() // type is inferred
|
|
@@ -37,7 +33,6 @@ vi.mock('data-primals-engine/engine', async(importOriginal) => {
|
|
|
37
33
|
const mockUser = {
|
|
38
34
|
username: 'testuserBackup',
|
|
39
35
|
_user: 'testuserBackup',
|
|
40
|
-
userPlan: 'premium',
|
|
41
36
|
email: 'testBackup@example.com',
|
|
42
37
|
configS3: {
|
|
43
38
|
bucketName: null
|
|
@@ -50,9 +45,9 @@ const testModelDefinition = {
|
|
|
50
45
|
description: 'Model for testing backup/restore',
|
|
51
46
|
fields: [
|
|
52
47
|
{ name: 'testField', type: 'string', required: true },
|
|
53
|
-
{ name: 'optionalField', type: 'number' }
|
|
48
|
+
{ name: 'optionalField', type: 'number' }
|
|
54
49
|
],
|
|
55
|
-
maxRequestData: 10
|
|
50
|
+
maxRequestData: 10
|
|
56
51
|
};
|
|
57
52
|
|
|
58
53
|
let testModelsColInstance;
|
|
@@ -101,7 +96,7 @@ beforeAll(async () =>{
|
|
|
101
96
|
})
|
|
102
97
|
beforeEach(async () => {
|
|
103
98
|
testModelsColInstance = getAppModelsCollection;
|
|
104
|
-
testDatasColInstance = getAppUserCollection(mockUser);
|
|
99
|
+
testDatasColInstance = await getAppUserCollection(mockUser);
|
|
105
100
|
});
|
|
106
101
|
|
|
107
102
|
describe('Data Backup and Restore Integration', () => {
|
|
@@ -14,8 +14,7 @@ import {
|
|
|
14
14
|
import {
|
|
15
15
|
modelsCollection as getAppModelsCollection,
|
|
16
16
|
getCollection,
|
|
17
|
-
getCollectionForUser as getAppUserCollection
|
|
18
|
-
datasCollection // Accès direct pour vérifications
|
|
17
|
+
getCollectionForUser as getAppUserCollection
|
|
19
18
|
} from 'data-primals-engine/modules/mongodb';
|
|
20
19
|
import { Engine } from "data-primals-engine/engine";
|
|
21
20
|
import process from "node:process";
|
|
@@ -41,6 +40,9 @@ async function setupTestContext() {
|
|
|
41
40
|
email: generateUniqueName('test') + '@example.com'
|
|
42
41
|
};
|
|
43
42
|
|
|
43
|
+
// Initialize collection instances after the engine is ready
|
|
44
|
+
testModelsColInstance = getAppModelsCollection;
|
|
45
|
+
testDatasColInstance = await getAppUserCollection(currentTestUser);
|
|
44
46
|
|
|
45
47
|
const relatedModelDefinition = {
|
|
46
48
|
name: currentRelatedModelName,
|
|
@@ -50,7 +52,7 @@ async function setupTestContext() {
|
|
|
50
52
|
{name: 'relatedName', type: 'string', required: true, unique: true},
|
|
51
53
|
{name: 'relatedValue', type: 'number'}
|
|
52
54
|
],
|
|
53
|
-
maxRequestData: 10
|
|
55
|
+
maxRequestData: 10
|
|
54
56
|
};
|
|
55
57
|
|
|
56
58
|
const comprehensiveTestModelDefinition = {
|
|
@@ -102,23 +104,25 @@ async function setupTestContext() {
|
|
|
102
104
|
{name: 'objectField', type: 'object'},
|
|
103
105
|
// Model & ModelField (validation of string format, not existence)
|
|
104
106
|
{name: 'modelNameField', type: 'model'},
|
|
105
|
-
{name: 'modelFieldNameField', type: 'modelField'}
|
|
107
|
+
{name: 'modelFieldNameField', type: 'modelField'} // Note: modelField type expects an object {model: 'modelName', field: 'fieldName'}
|
|
106
108
|
],
|
|
107
|
-
maxRequestData: 50
|
|
109
|
+
maxRequestData: 50
|
|
108
110
|
};
|
|
109
111
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
if( await testModelsColInstance.find({ $or: [{name: comprehensiveTestModelDefinition.name}, {name: relatedModelDefinition.name}]})) {
|
|
113
|
+
// Insérer les modèles en base
|
|
114
|
+
await testModelsColInstance.insertMany([
|
|
115
|
+
comprehensiveTestModelDefinition,
|
|
116
|
+
relatedModelDefinition
|
|
117
|
+
]);
|
|
118
|
+
}
|
|
115
119
|
await testDatasColInstance.deleteMany({ _user: currentTestUser.username });
|
|
116
120
|
|
|
117
121
|
// Retourner toutes les variables nécessaires pour un test
|
|
118
122
|
return {
|
|
119
123
|
currentTestUser,
|
|
120
124
|
comprehensiveTestModelDefinition,
|
|
121
|
-
relatedModelDefinition
|
|
125
|
+
relatedModelDefinition
|
|
122
126
|
};
|
|
123
127
|
}
|
|
124
128
|
|
|
@@ -128,9 +132,6 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
|
|
|
128
132
|
Config.Set("modules", ["mongodb", "data", "file", "bucket", "workflow","user", "assistant"]);
|
|
129
133
|
await initEngine();
|
|
130
134
|
|
|
131
|
-
// Initialize collection instances after the engine is ready
|
|
132
|
-
testModelsColInstance = getAppModelsCollection;
|
|
133
|
-
testDatasColInstance = datasCollection;
|
|
134
135
|
})
|
|
135
136
|
|
|
136
137
|
describe('insertData avec comprehensiveTestModel', () => {
|
|
@@ -186,13 +187,14 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
|
|
|
186
187
|
codeJsField: 'console.log("hello");',
|
|
187
188
|
objectField: { a: 1, b: "test" },
|
|
188
189
|
modelNameField: 'someModelName',
|
|
189
|
-
modelFieldNameField: { model: 'someModelName', field: 'someFieldName' }
|
|
190
|
+
modelFieldNameField: { model: 'someModelName', field: 'someFieldName' }
|
|
190
191
|
};
|
|
191
192
|
|
|
192
193
|
const result = await insertData(comprehensiveTestModelDefinition.name, validData, {}, currentTestUser, false);
|
|
193
194
|
expect(result.success, `L'insertion principale a échoué: ${result.error}`).toBe(true);
|
|
194
195
|
expect(result.insertedIds).toHaveLength(1);
|
|
195
196
|
|
|
197
|
+
console.log(result);
|
|
196
198
|
const insertedDoc = await testDatasColInstance.findOne({ _id: new ObjectId(result.insertedIds[0]) });
|
|
197
199
|
expect(insertedDoc).not.toBeNull();
|
|
198
200
|
expect(insertedDoc.stringRequired).toBe('Valid String');
|
|
@@ -365,7 +367,7 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
|
|
|
365
367
|
codeJsField: 'console.log("find");',
|
|
366
368
|
objectField: { find: "test" },
|
|
367
369
|
modelNameField: 'findModel',
|
|
368
|
-
modelFieldNameField: { model: 'findModel', field: 'findField' }
|
|
370
|
+
modelFieldNameField: { model: 'findModel', field: 'findField' }
|
|
369
371
|
};
|
|
370
372
|
|
|
371
373
|
// 3. Insérer le document principal
|
|
@@ -411,7 +413,7 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
|
|
|
411
413
|
stringUnique: 'UniqueForEdit',
|
|
412
414
|
enumField: 'alpha',
|
|
413
415
|
passwordField: 'initialPass',
|
|
414
|
-
relationSingle: relatedDocId
|
|
416
|
+
relationSingle: relatedDocId
|
|
415
417
|
};
|
|
416
418
|
const insertResult = await insertData(comprehensiveTestModelDefinition.name, initialData, {}, currentTestUser, false);
|
|
417
419
|
docToEditId = insertResult.insertedIds[0];
|
|
@@ -493,17 +495,17 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
|
|
|
493
495
|
await insertData(simpleModel.name, { name: 'Autre Item', value: 403 }, {}, currentTestUser, false);
|
|
494
496
|
|
|
495
497
|
const filter = { $regexMatch: {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
498
|
+
input: '$name',
|
|
499
|
+
regex: "^Item Filtrable"
|
|
500
|
+
} };
|
|
499
501
|
const deleteResult = await deleteData(simpleModel.name, [], filter, currentTestUser, false);
|
|
500
502
|
|
|
501
503
|
expect(deleteResult.success).toBe(true);
|
|
502
504
|
expect(deleteResult.deletedCount).toBe(2);
|
|
503
505
|
|
|
504
|
-
const remainingCount = await testDatasColInstance.countDocuments({ _model: simpleModel.name, name: { $regex: "^Item Filtrable" } });
|
|
506
|
+
const remainingCount = await testDatasColInstance.countDocuments({ _user: currentTestUser.username, _model: simpleModel.name, name: { $regex: "^Item Filtrable" } });
|
|
505
507
|
expect(remainingCount).toBe(0);
|
|
506
|
-
const totalRemaining = await testDatasColInstance.countDocuments({ _model: simpleModel.name });
|
|
508
|
+
const totalRemaining = await testDatasColInstance.countDocuments({ _user: currentTestUser.username, _model: simpleModel.name });
|
|
507
509
|
expect(totalRemaining).toBe(1);
|
|
508
510
|
});
|
|
509
511
|
});
|
|
@@ -531,7 +533,7 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
|
|
|
531
533
|
passwordField: 'searchPass1',
|
|
532
534
|
relationSingle: relatedDocId_A1,
|
|
533
535
|
relationMultiple: [relatedDocId_A1, relatedDocId_A2],
|
|
534
|
-
number: 10
|
|
536
|
+
number: 10
|
|
535
537
|
};
|
|
536
538
|
const insertResult1 = await insertData(comprehensiveTestModelDefinition.name, data1, {}, currentTestUser, false);
|
|
537
539
|
docId1 = insertResult1.insertedIds[0].toString();
|
|
@@ -543,7 +545,7 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
|
|
|
543
545
|
passwordField: 'searchPass2',
|
|
544
546
|
relationSingle: relatedDocId_B1,
|
|
545
547
|
relationMultiple: [relatedDocId_B1],
|
|
546
|
-
number: 20
|
|
548
|
+
number: 20
|
|
547
549
|
};
|
|
548
550
|
const insertResult2 = await insertData(comprehensiveTestModelDefinition.name, data2, {}, currentTestUser, false);
|
|
549
551
|
docId2 = insertResult2.insertedIds[0].toString();
|
|
@@ -564,7 +566,7 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
|
|
|
564
566
|
},
|
|
565
567
|
autoExpand: true,
|
|
566
568
|
depth: 8
|
|
567
|
-
}
|
|
569
|
+
}
|
|
568
570
|
};
|
|
569
571
|
const { data, count } = await searchData(searchParams);
|
|
570
572
|
expect(count).toBe(1);
|