data-primals-engine 1.2.6-rc2 → 1.2.6
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 +1 -1
- package/client/package-lock.json +247 -4354
- package/client/package.json +16 -15
- package/client/src/App.scss +1 -0
- package/client/src/DataEditor.jsx +1 -1
- package/client/src/Field.jsx +4 -16
- package/client/src/translations.js +17 -0
- package/package.json +120 -120
- package/src/engine.js +0 -1
- package/src/filter.js +35 -5
- package/src/modules/data/data.core.js +72 -1
- package/src/modules/data/data.js +59 -101
- package/src/modules/data/data.routes.js +2 -2
- package/src/modules/mongodb.js +3 -1
- package/src/modules/workflow.js +66 -70
- package/test/data.integration.test.js +1 -1
- package/test/workflow.actions.integration.test.js +474 -0
package/client/package.json
CHANGED
|
@@ -13,17 +13,17 @@
|
|
|
13
13
|
},
|
|
14
14
|
"peerDependencies": {
|
|
15
15
|
"@tiptap/react": "^2.11.5",
|
|
16
|
-
"react-router-dom": "^7.1.5",
|
|
17
|
-
"react-query": "^3.39.3",
|
|
18
|
-
"react-icons": "^5.0.1",
|
|
19
|
-
"react-tooltip": "^5.28.1",
|
|
20
|
-
"react-helmet": "^6.1.0",
|
|
21
|
-
"react-cookie": "^8.0.0",
|
|
22
|
-
"react-syntax-highlighter": "^15.6.1",
|
|
23
|
-
"react-international-phone": "^4.5.0",
|
|
24
16
|
"react-chartjs-2": "^5.3.0",
|
|
25
17
|
"react-code-blocks": "^0.1.6",
|
|
26
|
-
"react-
|
|
18
|
+
"react-cookie": "^8.0.0",
|
|
19
|
+
"react-ga": "^3.3.1",
|
|
20
|
+
"react-helmet": "^6.1.0",
|
|
21
|
+
"react-icons": "^5.0.1",
|
|
22
|
+
"react-international-phone": "^4.5.0",
|
|
23
|
+
"react-query": "^3.39.3",
|
|
24
|
+
"react-router-dom": "^7.1.5",
|
|
25
|
+
"react-syntax-highlighter": "^15.6.1",
|
|
26
|
+
"react-tooltip": "^5.28.1"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@babel/runtime": "^7.24.7",
|
|
@@ -43,15 +43,16 @@
|
|
|
43
43
|
"@uiw/react-codemirror": "^4.23.10",
|
|
44
44
|
"chart.js": "^4.4.9",
|
|
45
45
|
"chartjs-adapter-date-fns": "^3.0.0",
|
|
46
|
-
"react-dom": "^18.3.1",
|
|
47
|
-
"react-router": "^7.2.0",
|
|
48
|
-
"yet-another-react-lightbox": "^3.22.0",
|
|
49
|
-
"react": ">=18.0.0",
|
|
50
46
|
"classnames": "^2.5.1",
|
|
51
47
|
"date-fns": "^4.1.0",
|
|
52
48
|
"express-rate-limit": "^7.5.1",
|
|
53
49
|
"lowlight": "^3.3.0",
|
|
54
|
-
"
|
|
50
|
+
"react": ">=18.0.0",
|
|
51
|
+
"react-dom": "^18.3.1",
|
|
52
|
+
"react-router": "^7.2.0",
|
|
53
|
+
"react-switch": "^7.1.0",
|
|
54
|
+
"uniqid": "^5.4.0",
|
|
55
|
+
"yet-another-react-lightbox": "^3.22.0"
|
|
55
56
|
},
|
|
56
57
|
"devDependencies": {
|
|
57
58
|
"@eslint/js": "^9.17.0",
|
|
@@ -67,4 +68,4 @@
|
|
|
67
68
|
"sass-embedded": "^1.83.4",
|
|
68
69
|
"vite": "^6.1.1"
|
|
69
70
|
}
|
|
70
|
-
}
|
|
71
|
+
}
|
package/client/src/App.scss
CHANGED
|
@@ -239,7 +239,7 @@ export const DataEditor = forwardRef(function MyDataEditor({
|
|
|
239
239
|
onChange={(e) => handleChange({name: field.name, value: e})}/>
|
|
240
240
|
case 'boolean':
|
|
241
241
|
return <CheckboxField
|
|
242
|
-
help={
|
|
242
|
+
help={t('field_' + model.name + '_' + field.name + '_hint', field.hint || '')}
|
|
243
243
|
key={field.name} {...inputProps} checked={value}
|
|
244
244
|
onChange={(e) => handleChange({name: field.name, value: e.target.checked})}/>
|
|
245
245
|
case 'string':
|
package/client/src/Field.jsx
CHANGED
|
@@ -34,7 +34,7 @@ import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs';
|
|
|
34
34
|
import { PhoneInput } from 'react-international-phone';
|
|
35
35
|
import 'react-international-phone/style.css';
|
|
36
36
|
import {useAuthContext} from "./contexts/AuthContext.jsx";
|
|
37
|
-
|
|
37
|
+
import Switch from "react-switch";
|
|
38
38
|
export const Form = ({
|
|
39
39
|
name,
|
|
40
40
|
onValidate,
|
|
@@ -492,20 +492,6 @@ const CheckboxField = forwardRef(
|
|
|
492
492
|
return (
|
|
493
493
|
<>
|
|
494
494
|
<div className={cn({field: true, "field-checkbox": true,"field-bg": true})}>
|
|
495
|
-
<div className="inline"><input
|
|
496
|
-
aria-required={required}
|
|
497
|
-
aria-readonly={readOnly}
|
|
498
|
-
readOnly={readOnly}
|
|
499
|
-
type="checkbox"
|
|
500
|
-
checked={value}
|
|
501
|
-
placeholder={placeholder}
|
|
502
|
-
id={id}
|
|
503
|
-
name={name}
|
|
504
|
-
onChange={handleChange}
|
|
505
|
-
minLength={minlength}
|
|
506
|
-
maxLength={maxlength}
|
|
507
|
-
{...rest}
|
|
508
|
-
/>
|
|
509
495
|
{label && (
|
|
510
496
|
<label
|
|
511
497
|
contentEditable={editable}
|
|
@@ -522,7 +508,9 @@ const CheckboxField = forwardRef(
|
|
|
522
508
|
)}
|
|
523
509
|
</label>
|
|
524
510
|
)}
|
|
525
|
-
|
|
511
|
+
<Switch
|
|
512
|
+
onChange={handleChange}
|
|
513
|
+
checked={value} />
|
|
526
514
|
{help && <div className="flex help">{help}</div>}
|
|
527
515
|
</div>
|
|
528
516
|
{errors.length > 0 && (
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
export const websiteTranslations = {
|
|
3
3
|
fr: {
|
|
4
4
|
translation: {
|
|
5
|
+
|
|
6
|
+
"api.data.relationFilterFailed": "La valeur {{value}} pour le champ {{field}} ne respecte pas le filtre de relation défini.",
|
|
7
|
+
|
|
5
8
|
"dataimporter.excelPreview":"Aperçu des données Excel",
|
|
6
9
|
"dataimporter.previewNote": "Note: Ceci est un aperçu des premières lignes. Les cellules vides sont affichées comme \"(vide)\".",
|
|
7
10
|
"dataimporter.nullValue": "(vide)",
|
|
@@ -567,6 +570,8 @@ export const websiteTranslations = {
|
|
|
567
570
|
"suggested_prompt.recipes.title": "Cooking Recipes",
|
|
568
571
|
"suggested_prompt.recipes.prompt": "A collection of cooking recipes, with ingredients, instructions, and preparation time.",
|
|
569
572
|
|
|
573
|
+
"api.data.relationFilterFailed": "The value {{value}} for field {{field}} does not match the defined relationship filter.",
|
|
574
|
+
|
|
570
575
|
"api.data.duplicateValue": "The value '{{value}}' already exists for the unique field '{{field}}'.",
|
|
571
576
|
"api.model.invalidStructure": "The model is invalid. Some fields are incorrect.",
|
|
572
577
|
"api.validate.fieldArray": "Field '{{0}}' must be an array.",
|
|
@@ -1975,6 +1980,7 @@ export const websiteTranslations = {
|
|
|
1975
1980
|
"suggested_prompt.recipes.prompt": "Una colección de recetas de cocina, con ingredientes, instrucciones y tiempo de preparación.",
|
|
1976
1981
|
|
|
1977
1982
|
"api.data.duplicateValue": "El valor '{{value}}' ya existe para el campo único '{{field}}'.",
|
|
1983
|
+
"api.data.relationFilterFailed": "El valor {{value}} del campo {{field}} no coincide con el filtro de relación definido.",
|
|
1978
1984
|
"api.model.invalidStructure": "El modelo no es válido. Algunos campos son incorrectos.",
|
|
1979
1985
|
"api.validate.fieldArray": "El campo '{{0}}' debe ser un array.",
|
|
1980
1986
|
"api.validate.invalidMimeType": "Tipo de archivo '{{type}}' no válido. Los tipos permitidos son: {{authorized}}.",
|
|
@@ -3388,6 +3394,7 @@ export const websiteTranslations = {
|
|
|
3388
3394
|
"suggested_prompt.recipes.prompt": "Uma coleção de receitas culinárias, com ingredientes, instruções e tempo de preparação.",
|
|
3389
3395
|
|
|
3390
3396
|
"api.data.duplicateValue": "O valor '{{value}}' já existe para o campo exclusivo '{{field}}'.",
|
|
3397
|
+
"api.data.relationFilterFailed": "O valor {{value}} para o campo {{field}} não corresponde ao filtro de relacionamento definido.",
|
|
3391
3398
|
"api.model.invalidStructure": "O modelo é inválido. Alguns campos estão incorretos.",
|
|
3392
3399
|
"api.validate.fieldArray": "O campo '{{0}}' deve ser um array.",
|
|
3393
3400
|
"api.validate.invalidMimeType": "Tipo de ficheiro inválido '{{type}}'. Os tipos permitidos são: {{authorized}}.",
|
|
@@ -4782,6 +4789,7 @@ export const websiteTranslations = {
|
|
|
4782
4789
|
"suggested_prompt.recipes.prompt": "Eine Sammlung von Kochrezepten mit Zutaten, Anweisungen und Vorbereitungszeit.",
|
|
4783
4790
|
|
|
4784
4791
|
"api.data.duplicateValue": "Der Wert '{{value}}' ist für das eindeutige Feld '{{field}}' bereits vorhanden.",
|
|
4792
|
+
"api.data.relationFilterFailed": "Der Wert {{value}} für das Feld {{field}} entspricht nicht dem definierten Beziehungsfilter.",
|
|
4785
4793
|
"api.model.invalidStructure": "Das Modell ist ungültig. Einige Felder sind fehlerhaft.",
|
|
4786
4794
|
"api.validate.fieldArray": "Feld '{{0}}' muss ein Array sein.",
|
|
4787
4795
|
"api.validate.invalidMimeType": "Ungültiger Dateityp '{{type}}'. Zulässige Typen sind: {{authorized}}.",
|
|
@@ -6185,6 +6193,7 @@ export const websiteTranslations = {
|
|
|
6185
6193
|
"suggested_prompt.recipes.prompt": "Una raccolta di ricette di cucina, con ingredienti, istruzioni e tempi di preparazione.",
|
|
6186
6194
|
|
|
6187
6195
|
"api.data.duplicateValue": "Il valore '{{value}}' esiste già per il campo univoco '{{field}}'.",
|
|
6196
|
+
"api.data.relationFilterFailed": "Il valore {{value}} per il campo {{field}} non corrisponde al filtro di relazione definito.",
|
|
6188
6197
|
"api.model.invalidStructure": "Il modello non è valido. Alcuni campi sono errati.",
|
|
6189
6198
|
"api.validate.fieldArray": "Il campo '{{0}}' deve essere un array.",
|
|
6190
6199
|
"api.validate.invalidMimeType": "Tipo di file '{{type}}' non valido. I tipi consentiti sono: {{authorized}}.",
|
|
@@ -7589,6 +7598,7 @@ export const websiteTranslations = {
|
|
|
7589
7598
|
"suggested_prompt.recipes.prompt": "Sbírka receptů na vaření s ingrediencemi, návodem a přípravou čas.",
|
|
7590
7599
|
|
|
7591
7600
|
"api.data.duplicateValue": "Hodnota '{{value}}' pro jedinečné pole '{{field}}' již existuje.",
|
|
7601
|
+
"api.data.relationFilterFailed": "Hodnota {{value}} pro pole {{field}} neodpovídá definovanému filtru vztahů.",
|
|
7592
7602
|
"api.model.invalidStructure": "Model je neplatný. Některá pole jsou nesprávná.",
|
|
7593
7603
|
"api.validate.fieldArray": "Pole '{{0}}' musí být pole.",
|
|
7594
7604
|
"api.validate.invalidMimeType": "Neplatný typ souboru '{{type}}'. Povolené typy jsou: {{authorized}}.",
|
|
@@ -8988,6 +8998,7 @@ export const websiteTranslations = {
|
|
|
8988
8998
|
"suggested_prompt.recipes.prompt": "Коллекция кулинарных рецептов с ингредиентами, инструкциями и описанием приготовления. время.",
|
|
8989
8999
|
|
|
8990
9000
|
"api.data.duplicateValue": "Значение '{{value}}' уже существует для уникального поля '{{field}}'.",
|
|
9001
|
+
"api.data.relationFilterFailed": "Значение {{value}} для поля {{field}} не соответствует заданному фильтру связи.",
|
|
8991
9002
|
"api.model.invalidStructure": "Модель недействительна. Некоторые поля неверны.",
|
|
8992
9003
|
"api.validate.fieldArray": "Поле '{{0}}' должно быть массивом.",
|
|
8993
9004
|
"api.validate.invalidMimeType": "Недопустимый тип файла '{{type}}'. Допустимые типы: {{authorized}}.",
|
|
@@ -10396,6 +10407,7 @@ export const websiteTranslations = {
|
|
|
10396
10407
|
"suggested_prompt.recipes.prompt": "مجموعة من وصفات الطبخ، مع المكونات، والتعليمات، ومدة التحضير.",
|
|
10397
10408
|
|
|
10398
10409
|
"api.data.duplicateValue": "القيمة '{{value}}' موجودة بالفعل للحقل الفريد '{{field}}'.",
|
|
10410
|
+
"api.data.relationFilterFailed": "القيمة {{value}} للحقل {{field}} لا تتطابق مع مرشح العلاقة المحدد.",
|
|
10399
10411
|
"api.model.invalidStructure": "النموذج غير صالح. بعض الحقول غير صحيحة.",
|
|
10400
10412
|
"api.validate.fieldArray": "يجب أن يكون الحقل '{{0}}' مصفوفة.",
|
|
10401
10413
|
"api.validate.invalidMimeType": "نوع الملف '{{type}}' غير صالح. الأنواع المسموح بها هي: {{authorized}}.",
|
|
@@ -11823,6 +11835,7 @@ export const websiteTranslations = {
|
|
|
11823
11835
|
"suggested_prompt.recipes.prompt": "En samling matlagningsrecept, med ingredienser, instruktioner, och förberedelsetid.",
|
|
11824
11836
|
|
|
11825
11837
|
"api.data.duplicateValue": "Värdet '{{value}}' finns redan för det unika fältet '{{field}}'.",
|
|
11838
|
+
"api.data.relationFilterFailed": "Värdet {{value}} för fältet {{field}} matchar inte det definierade relationsfiltret.",
|
|
11826
11839
|
"api.model.invalidStructure": "Modellen är ogiltig. Vissa fält är felaktiga.",
|
|
11827
11840
|
"api.validate.fieldArray": "Fältet '{{0}}' måste vara en array.",
|
|
11828
11841
|
"api.validate.invalidMimeType": "Ogiltig filtyp '{{type}}'. Tillåtna typer är: {{authorized}}.",
|
|
@@ -13222,6 +13235,8 @@ export const websiteTranslations = {
|
|
|
13222
13235
|
"suggested_prompt.recipes.prompt": "Μια συλλογή συνταγών μαγειρικής, με υλικά, οδηγίες και χρόνο προετοιμασίας.",
|
|
13223
13236
|
|
|
13224
13237
|
"api.data.duplicateValue": "Η τιμή '{{value}}' υπάρχει ήδη για το μοναδικό πεδίο '{{field}}'.",
|
|
13238
|
+
"api.data.relationFilterFailed": "Η τιμή {{value}} για το πεδίο {{field}} δεν ταιριάζει με το καθορισμένο φίλτρο σχέσης.",
|
|
13239
|
+
|
|
13225
13240
|
"api.model.invalidStructure": "Το μοντέλο δεν είναι έγκυρο. Ορισμένα πεδία είναι λανθασμένα.",
|
|
13226
13241
|
"api.validate.fieldArray": "Το πεδίο '{{0}}' πρέπει να είναι ένας πίνακας.",
|
|
13227
13242
|
"api.validate.invalidMimeType": "Μη έγκυρος τύπος αρχείου '{{type}}'. Οι επιτρεπόμενοι τύποι είναι: {{authorized}}.",
|
|
@@ -14629,6 +14644,8 @@ export const websiteTranslations = {
|
|
|
14629
14644
|
"suggested_prompt.recipes.prompt": "مجموعهای از دستور پخت غذا، با مواد لازم، دستورالعمل و زمان آمادهسازی.",
|
|
14630
14645
|
|
|
14631
14646
|
"api.data.duplicateValue": "مقدار '{{value}}' از قبل برای فیلد منحصر به فرد '{{field}}' وجود دارد.",
|
|
14647
|
+
"api.data.relationFilterFailed": "مقدار {{value}} برای فیلد {{field}} با فیلتر رابطه تعریف شده مطابقت ندارد.",
|
|
14648
|
+
|
|
14632
14649
|
"api.model.invalidStructure": "مدل نامعتبر است. برخی از فیلدها نادرست هستند.",
|
|
14633
14650
|
"api.validate.fieldArray": "فیلد '{{0}}' باید یک آرایه باشد.",
|
|
14634
14651
|
"api.validate.invalidMimeType": "نوع فایل '{{type}}' نامعتبر است. انواع مجاز عبارتند از: {{authorized}}.",
|
package/package.json
CHANGED
|
@@ -1,120 +1,120 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "data-primals-engine",
|
|
3
|
-
"version": "1.2.6
|
|
4
|
-
"description": "data-primals-engine is a package responsible from handling large amount of data using MongoDB in a practical and performant way. It can also get workflow models working (for automation), and fully supports internationalisation. It also has integrated AI assistant.",
|
|
5
|
-
"main": "src/engine.js",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"scripts": {
|
|
8
|
-
"preinstall": "npx force-resolutions",
|
|
9
|
-
"dev": "concurrently \"npm:client\" \"npm:devserver\"",
|
|
10
|
-
"prod": "npm run build-server && npm run server",
|
|
11
|
-
"client": "cd client && npm run dev",
|
|
12
|
-
"build-server": "vite build client --config client/vite.config.js --outDir dist",
|
|
13
|
-
"build-client": "cd client && npm run build",
|
|
14
|
-
"server": "cross-env NODE_ENV=production node server.js",
|
|
15
|
-
"devserver": "npm run build-server && cross-env NODE_ENV=development PORT=7633 node server.js",
|
|
16
|
-
"lint": "eslint .",
|
|
17
|
-
"format": "prettier --write .",
|
|
18
|
-
"clean": "rm -rf node_modules package-lock.json",
|
|
19
|
-
"test": "cross-env MONGO_DB_URL=\"mongodb://localhost:27017\" PORT=7635 vitest --no-file-parallelism",
|
|
20
|
-
"audit": "npm audit --audit-level=high",
|
|
21
|
-
"migrate:create": "node src/migrate.js create",
|
|
22
|
-
"migrate:up": "node src/migrate.js up",
|
|
23
|
-
"migrate:down": "node src/migrate.js down",
|
|
24
|
-
"migrate:revert": "node src/migrate.js revert",
|
|
25
|
-
"migrate:status": "node src/migrate.js status"
|
|
26
|
-
},
|
|
27
|
-
"optionalDependencies": {
|
|
28
|
-
"@rollup/rollup-linux-x64-gnu": "4.6.1"
|
|
29
|
-
},
|
|
30
|
-
"resolutions": {
|
|
31
|
-
"tar-fs": "3.0.9",
|
|
32
|
-
"on-headers": "1.1.0",
|
|
33
|
-
"brace-expansion": "2.0.2",
|
|
34
|
-
"prismjs": "1.30.0"
|
|
35
|
-
},
|
|
36
|
-
"repository": {
|
|
37
|
-
"type": "git",
|
|
38
|
-
"url": "https://github.com/anonympins/data-primals-engine.git"
|
|
39
|
-
},
|
|
40
|
-
"exports": {
|
|
41
|
-
".": "./src/index.js",
|
|
42
|
-
"./modules/*.js": "./src/modules/*.js",
|
|
43
|
-
"./modules/*": "./src/modules/*/index.js",
|
|
44
|
-
"./client": "./client/index.js",
|
|
45
|
-
"./*": "./src/*.js"
|
|
46
|
-
},
|
|
47
|
-
"peerDependencies": {
|
|
48
|
-
"express": "^5.1.0",
|
|
49
|
-
"react": ">=18.0.0",
|
|
50
|
-
"react-query": ">=3.0.0"
|
|
51
|
-
},
|
|
52
|
-
"dependencies": {
|
|
53
|
-
"@langchain/core": "^0.3.66",
|
|
54
|
-
"@langchain/deepseek": "^0.1.0",
|
|
55
|
-
"@langchain/google-genai": "^0.2.16",
|
|
56
|
-
"@langchain/openai": "^0.6.3",
|
|
57
|
-
"archiver": "^7.0.1",
|
|
58
|
-
"aws-sdk": "^2.1692.0",
|
|
59
|
-
"bcrypt": "^6.0.0",
|
|
60
|
-
"body-parser": "^2.2.0",
|
|
61
|
-
"chalk": "^5.4.1",
|
|
62
|
-
"check-disk-space": "^3.4.0",
|
|
63
|
-
"compression": "^1.8.1",
|
|
64
|
-
"cookie-parser": "^1.4.7",
|
|
65
|
-
"cronstrue": "^3.2.0",
|
|
66
|
-
"csv-parse": "^6.1.0",
|
|
67
|
-
"date-fns": "^4.1.0",
|
|
68
|
-
"express-csrf-double-submit-cookie": "^2.0.0",
|
|
69
|
-
"express-formidable": "^1.2.0",
|
|
70
|
-
"express-mongo-sanitize": "^2.2.0",
|
|
71
|
-
"express-rate-limit": "^8.0.1",
|
|
72
|
-
"express-session": "^1.18.2",
|
|
73
|
-
"i18next-browser-languagedetector": "^8.2.0",
|
|
74
|
-
"isolated-vm": "^4.7.2",
|
|
75
|
-
"juice": "^11.0.1",
|
|
76
|
-
"mathjs": "^14.6.0",
|
|
77
|
-
"mongodb": "^6.18.0",
|
|
78
|
-
"node-cache": "^5.1.2",
|
|
79
|
-
"node-schedule": "^2.1.1",
|
|
80
|
-
"nodemailer": "^7.0.5",
|
|
81
|
-
"openai": "^5.10.2",
|
|
82
|
-
"process": "^0.11.10",
|
|
83
|
-
"prop-types": "^15.8.1",
|
|
84
|
-
"randomcolor": "^0.6.2",
|
|
85
|
-
"react-i18next": "^15.6.1",
|
|
86
|
-
"react-markdown": "^10.1.0",
|
|
87
|
-
"read-excel-file": "^5.8.8",
|
|
88
|
-
"request-ip": "^3.3.0",
|
|
89
|
-
"sanitize-html": "^2.17.0",
|
|
90
|
-
"sirv": "^3.0.1",
|
|
91
|
-
"swagger-ui-express": "^5.0.1",
|
|
92
|
-
"tar": "^7.4.3",
|
|
93
|
-
"uniqid": "^5.4.0",
|
|
94
|
-
"vitest": "^3.2.4",
|
|
95
|
-
"yaml": "^2.8.0"
|
|
96
|
-
},
|
|
97
|
-
"devDependencies": {
|
|
98
|
-
"@eslint/js": "^9.32.0",
|
|
99
|
-
"concurrently": "^9.2.0",
|
|
100
|
-
"cross-env": "^10.0.0",
|
|
101
|
-
"eslint": "^9.32.0",
|
|
102
|
-
"globals": "^16.3.0"
|
|
103
|
-
},
|
|
104
|
-
"subPackages": [
|
|
105
|
-
"client"
|
|
106
|
-
],
|
|
107
|
-
"keywords": [
|
|
108
|
-
"data-driven engine",
|
|
109
|
-
"headless CMS",
|
|
110
|
-
"backend",
|
|
111
|
-
"automation",
|
|
112
|
-
"AWS S3",
|
|
113
|
-
"MongoDB"
|
|
114
|
-
],
|
|
115
|
-
"author": "anonympins",
|
|
116
|
-
"license": "MIT",
|
|
117
|
-
"engines": {
|
|
118
|
-
"node": ">=20.0.0"
|
|
119
|
-
}
|
|
120
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "data-primals-engine",
|
|
3
|
+
"version": "1.2.6",
|
|
4
|
+
"description": "data-primals-engine is a package responsible from handling large amount of data using MongoDB in a practical and performant way. It can also get workflow models working (for automation), and fully supports internationalisation. It also has integrated AI assistant.",
|
|
5
|
+
"main": "src/engine.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"preinstall": "npx force-resolutions",
|
|
9
|
+
"dev": "concurrently \"npm:client\" \"npm:devserver\"",
|
|
10
|
+
"prod": "npm run build-server && npm run server",
|
|
11
|
+
"client": "cd client && npm run dev",
|
|
12
|
+
"build-server": "vite build client --config client/vite.config.js --outDir dist",
|
|
13
|
+
"build-client": "cd client && npm run build",
|
|
14
|
+
"server": "cross-env NODE_ENV=production node server.js",
|
|
15
|
+
"devserver": "npm run build-server && cross-env NODE_ENV=development PORT=7633 node server.js",
|
|
16
|
+
"lint": "eslint .",
|
|
17
|
+
"format": "prettier --write .",
|
|
18
|
+
"clean": "rm -rf node_modules package-lock.json",
|
|
19
|
+
"test": "cross-env MONGO_DB_URL=\"mongodb://localhost:27017\" PORT=7635 vitest --no-file-parallelism",
|
|
20
|
+
"audit": "npm audit --audit-level=high",
|
|
21
|
+
"migrate:create": "node src/migrate.js create",
|
|
22
|
+
"migrate:up": "node src/migrate.js up",
|
|
23
|
+
"migrate:down": "node src/migrate.js down",
|
|
24
|
+
"migrate:revert": "node src/migrate.js revert",
|
|
25
|
+
"migrate:status": "node src/migrate.js status"
|
|
26
|
+
},
|
|
27
|
+
"optionalDependencies": {
|
|
28
|
+
"@rollup/rollup-linux-x64-gnu": "4.6.1"
|
|
29
|
+
},
|
|
30
|
+
"resolutions": {
|
|
31
|
+
"tar-fs": "3.0.9",
|
|
32
|
+
"on-headers": "1.1.0",
|
|
33
|
+
"brace-expansion": "2.0.2",
|
|
34
|
+
"prismjs": "1.30.0"
|
|
35
|
+
},
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/anonympins/data-primals-engine.git"
|
|
39
|
+
},
|
|
40
|
+
"exports": {
|
|
41
|
+
".": "./src/index.js",
|
|
42
|
+
"./modules/*.js": "./src/modules/*.js",
|
|
43
|
+
"./modules/*": "./src/modules/*/index.js",
|
|
44
|
+
"./client": "./client/index.js",
|
|
45
|
+
"./*": "./src/*.js"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"express": "^5.1.0",
|
|
49
|
+
"react": ">=18.0.0",
|
|
50
|
+
"react-query": ">=3.0.0"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@langchain/core": "^0.3.66",
|
|
54
|
+
"@langchain/deepseek": "^0.1.0",
|
|
55
|
+
"@langchain/google-genai": "^0.2.16",
|
|
56
|
+
"@langchain/openai": "^0.6.3",
|
|
57
|
+
"archiver": "^7.0.1",
|
|
58
|
+
"aws-sdk": "^2.1692.0",
|
|
59
|
+
"bcrypt": "^6.0.0",
|
|
60
|
+
"body-parser": "^2.2.0",
|
|
61
|
+
"chalk": "^5.4.1",
|
|
62
|
+
"check-disk-space": "^3.4.0",
|
|
63
|
+
"compression": "^1.8.1",
|
|
64
|
+
"cookie-parser": "^1.4.7",
|
|
65
|
+
"cronstrue": "^3.2.0",
|
|
66
|
+
"csv-parse": "^6.1.0",
|
|
67
|
+
"date-fns": "^4.1.0",
|
|
68
|
+
"express-csrf-double-submit-cookie": "^2.0.0",
|
|
69
|
+
"express-formidable": "^1.2.0",
|
|
70
|
+
"express-mongo-sanitize": "^2.2.0",
|
|
71
|
+
"express-rate-limit": "^8.0.1",
|
|
72
|
+
"express-session": "^1.18.2",
|
|
73
|
+
"i18next-browser-languagedetector": "^8.2.0",
|
|
74
|
+
"isolated-vm": "^4.7.2",
|
|
75
|
+
"juice": "^11.0.1",
|
|
76
|
+
"mathjs": "^14.6.0",
|
|
77
|
+
"mongodb": "^6.18.0",
|
|
78
|
+
"node-cache": "^5.1.2",
|
|
79
|
+
"node-schedule": "^2.1.1",
|
|
80
|
+
"nodemailer": "^7.0.5",
|
|
81
|
+
"openai": "^5.10.2",
|
|
82
|
+
"process": "^0.11.10",
|
|
83
|
+
"prop-types": "^15.8.1",
|
|
84
|
+
"randomcolor": "^0.6.2",
|
|
85
|
+
"react-i18next": "^15.6.1",
|
|
86
|
+
"react-markdown": "^10.1.0",
|
|
87
|
+
"read-excel-file": "^5.8.8",
|
|
88
|
+
"request-ip": "^3.3.0",
|
|
89
|
+
"sanitize-html": "^2.17.0",
|
|
90
|
+
"sirv": "^3.0.1",
|
|
91
|
+
"swagger-ui-express": "^5.0.1",
|
|
92
|
+
"tar": "^7.4.3",
|
|
93
|
+
"uniqid": "^5.4.0",
|
|
94
|
+
"vitest": "^3.2.4",
|
|
95
|
+
"yaml": "^2.8.0"
|
|
96
|
+
},
|
|
97
|
+
"devDependencies": {
|
|
98
|
+
"@eslint/js": "^9.32.0",
|
|
99
|
+
"concurrently": "^9.2.0",
|
|
100
|
+
"cross-env": "^10.0.0",
|
|
101
|
+
"eslint": "^9.32.0",
|
|
102
|
+
"globals": "^16.3.0"
|
|
103
|
+
},
|
|
104
|
+
"subPackages": [
|
|
105
|
+
"client"
|
|
106
|
+
],
|
|
107
|
+
"keywords": [
|
|
108
|
+
"data-driven engine",
|
|
109
|
+
"headless CMS",
|
|
110
|
+
"backend",
|
|
111
|
+
"automation",
|
|
112
|
+
"AWS S3",
|
|
113
|
+
"MongoDB"
|
|
114
|
+
],
|
|
115
|
+
"author": "anonympins",
|
|
116
|
+
"license": "MIT",
|
|
117
|
+
"engines": {
|
|
118
|
+
"node": ">=20.0.0"
|
|
119
|
+
}
|
|
120
|
+
}
|
package/src/engine.js
CHANGED
package/src/filter.js
CHANGED
|
@@ -175,15 +175,45 @@ export const isConditionMet = (model, cond, formData, allModels, user) => {
|
|
|
175
175
|
|
|
176
176
|
if (!condition) return true;
|
|
177
177
|
|
|
178
|
+
// Cas 0: Condition est une valeur primitive (string, number, boolean)
|
|
179
|
+
// On la considère comme toujours vraie (comportement de searchData)
|
|
180
|
+
if (typeof condition !== 'object' || condition === null) {
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
|
|
178
184
|
// Cas 1: Condition simple {field: value} → transformée en {field: {$eq: value}}
|
|
179
185
|
if (typeof condition === 'object' && !Array.isArray(condition)) {
|
|
180
186
|
const keys = Object.keys(condition);
|
|
187
|
+
|
|
188
|
+
// Cas spécial pour les conditions de type {field: value} (pas d'opérateur $)
|
|
181
189
|
if (keys.length === 1 && !keys[0].startsWith('$') &&
|
|
182
190
|
typeof condition[keys[0]] !== 'object') {
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
191
|
+
const fieldName = keys[0];
|
|
192
|
+
const value = condition[fieldName];
|
|
193
|
+
|
|
194
|
+
// Si la valeur est null/undefined, on vérifie simplement l'existence
|
|
195
|
+
if (value === null || value === undefined) {
|
|
196
|
+
return formData[fieldName] === value;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Sinon on fait une comparaison d'égalité simple
|
|
200
|
+
return formData[fieldName] == value;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Cas spécial pour les tableaux - vérifie si la valeur est incluse
|
|
204
|
+
if (keys.length === 1 && !keys[0].startsWith('$') &&
|
|
205
|
+
Array.isArray(condition[keys[0]])) {
|
|
206
|
+
const fieldName = keys[0];
|
|
207
|
+
const values = condition[fieldName];
|
|
208
|
+
const fieldValue = formData[fieldName];
|
|
209
|
+
|
|
210
|
+
// Si le champ est aussi un tableau, vérifie l'intersection
|
|
211
|
+
if (Array.isArray(fieldValue)) {
|
|
212
|
+
return fieldValue.some(v => values.includes(v));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Sinon vérifie si la valeur est dans le tableau
|
|
216
|
+
return values.includes(fieldValue);
|
|
187
217
|
}
|
|
188
218
|
}
|
|
189
219
|
|
|
@@ -218,4 +248,4 @@ export const isConditionMet = (model, cond, formData, allModels, user) => {
|
|
|
218
248
|
|
|
219
249
|
// Cas 4: Tous les autres cas (conditions normales avec opérateurs)
|
|
220
250
|
return evaluateSingleCondition(model, condition, formData, allModels, user);
|
|
221
|
-
};
|
|
251
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import NodeCache from "node-cache";
|
|
2
|
-
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import {Worker} from 'worker_threads';
|
|
3
4
|
export const modelsCache = new NodeCache( { stdTTL: 100, checkperiod: 120 } );
|
|
4
5
|
|
|
5
6
|
export const mongoDBWhitelist = [
|
|
@@ -15,3 +16,73 @@ export const mongoDBWhitelist = [
|
|
|
15
16
|
];
|
|
16
17
|
export let importJobs = {};
|
|
17
18
|
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Exécute une tâche d'import/export (parsing, stringify) dans un worker thread.
|
|
22
|
+
* @param {('parse-json'|'parse-csv'|'stringify-json')} action - L'action à effectuer.
|
|
23
|
+
* @param {object} payload - Les données nécessaires pour l'action.
|
|
24
|
+
* @returns {Promise<any>} - Une promesse qui se résout avec les données traitées.
|
|
25
|
+
*/
|
|
26
|
+
export function runImportExportWorker(action, payload) {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const workerPath = path.resolve(process.cwd(), './src/workers/import-export-worker.js');
|
|
29
|
+
const worker = new Worker(workerPath);
|
|
30
|
+
|
|
31
|
+
worker.postMessage({ action, payload });
|
|
32
|
+
|
|
33
|
+
worker.on('message', (result) => {
|
|
34
|
+
if (result.success) {
|
|
35
|
+
resolve(result.data);
|
|
36
|
+
} else {
|
|
37
|
+
// Correction : On s'assure de toujours passer une chaîne de caractères à new Error()
|
|
38
|
+
const errorMessage = result.error || `Import/Export Worker failed with an unknown error. Action: ${action}.`;
|
|
39
|
+
reject(new Error(errorMessage));
|
|
40
|
+
}
|
|
41
|
+
worker.terminate();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
worker.on('error', (err) => {
|
|
45
|
+
reject(err);
|
|
46
|
+
worker.terminate();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
worker.on('exit', (code) => {
|
|
50
|
+
if (code !== 0) {
|
|
51
|
+
reject(new Error(`Import/Export Worker stopped with exit code ${code}`));
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/** Exécute une tâche de cryptographie dans un worker thread.
|
|
57
|
+
* @param {('encrypt'|'decrypt'|'hash')} action - L'action à effectuer.
|
|
58
|
+
* @param {object} payload - Les données nécessaires pour l'action.
|
|
59
|
+
* @returns {Promise<any>} - Une promesse qui se résout avec le résultat (si pertinent).
|
|
60
|
+
*/
|
|
61
|
+
export function runCryptoWorkerTask(action, payload) {
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
const workerPath = path.resolve(process.cwd(), './src/workers/crypto-worker.js');
|
|
64
|
+
const worker = new Worker(workerPath);
|
|
65
|
+
|
|
66
|
+
worker.postMessage({ action, payload });
|
|
67
|
+
|
|
68
|
+
worker.on('message', (result) => {
|
|
69
|
+
if (result.success) {
|
|
70
|
+
resolve(result.data); // Résout avec les données (ex: le hash) ou undefined si pas de retour
|
|
71
|
+
} else {
|
|
72
|
+
reject(new Error(result.error));
|
|
73
|
+
}
|
|
74
|
+
worker.terminate();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
worker.on('error', (err) => {
|
|
78
|
+
reject(err);
|
|
79
|
+
worker.terminate();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
worker.on('exit', (code) => {
|
|
83
|
+
if (code !== 0) {
|
|
84
|
+
reject(new Error(`Crypto Worker stopped with exit code ${code}`));
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
}
|