data-primals-engine 1.1.7-rc1 → 1.1.8-rc.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 +96 -14
- package/client/src/App.jsx +55 -49
- package/client/src/App.scss +3 -1
- package/client/src/RelationField.jsx +59 -47
- package/client/src/constants.js +20 -1
- package/client/src/filter.js +18 -0
- package/client/src/translations.js +46 -28
- package/package.json +6 -7
- package/src/engine.js +27 -9
- package/src/middlewares/middleware-mongodb.js +1 -1
- package/src/modules/data.js +169 -0
- package/test/data.integration.test.js +102 -3
|
@@ -52,6 +52,8 @@ export const websiteTranslations = {
|
|
|
52
52
|
"modelcreator.hiddenable.hint": "Une option sera disponible pour afficher cette donnée ou la masquer (il sera masqué par défaut).",
|
|
53
53
|
|
|
54
54
|
"tourpoint.importDatapack": "Importez des packs de données pré-construits avec notre galerie de packs : Sites internets, e-commerce, CRM, et plus encore !",
|
|
55
|
+
"tourpoint.tutorials": "Laisse-vous guider en suivant nos tutoriels pas à pas !",
|
|
56
|
+
|
|
55
57
|
"add_language": "Ajouter...",
|
|
56
58
|
"select_or_add_language": "Sélectionnez ou ajoutez une langue pour commencer.",
|
|
57
59
|
"field.richtext_t": "Texte enrichi traduit",
|
|
@@ -521,8 +523,8 @@ export const websiteTranslations = {
|
|
|
521
523
|
"prez1.5": "Support des traductions et langues",
|
|
522
524
|
"links.deploy_api": "Déployez votre API",
|
|
523
525
|
"links.documentation": "Documentation",
|
|
524
|
-
"links.demo": "
|
|
525
|
-
"filterstringfield.placeholder.regex": 'Expression régulière'
|
|
526
|
+
"links.demo": "Démarrer la démo",
|
|
527
|
+
"filterstringfield.placeholder.regex": 'Expression régulière'
|
|
526
528
|
|
|
527
529
|
}
|
|
528
530
|
},
|
|
@@ -594,6 +596,8 @@ export const websiteTranslations = {
|
|
|
594
596
|
"api.data.serverStorageFull": "The server has reached its maximum storage capacity. Please try again later.",
|
|
595
597
|
|
|
596
598
|
"tourpoint.importDatapack": "Import pre-built data packs with our pack gallery: Websites, e-commerce, CRM, and more!",
|
|
599
|
+
"tourpoint.tutorials": "Let yourself be guided by following our step-by-step tutorials!",
|
|
600
|
+
|
|
597
601
|
"add_language": "Add...",
|
|
598
602
|
"select_or_add_language": "Select or add a language to get started.",
|
|
599
603
|
"field.richtext_t": "Translated rich text",
|
|
@@ -1479,7 +1483,7 @@ export const websiteTranslations = {
|
|
|
1479
1483
|
"dataimporter.title": "Import data into {{model}}",
|
|
1480
1484
|
"dataimporter.hasCsvHeaders": "Does the CSV file contain a header row?",
|
|
1481
1485
|
"dataimporter.info": "Import your JSON or CSV file containing the required data for your model. Your data will be copied to your personal database. The transfer rate is {{constante}}",
|
|
1482
|
-
"links.demo": "
|
|
1486
|
+
"links.demo": "Start the demo",
|
|
1483
1487
|
"filterstringfield.placeholder.regex": 'Regular expression',
|
|
1484
1488
|
"cgu.h1": "Terms of Use (TOU)",
|
|
1485
1489
|
"cgu.h1.1": "1. Introduction",
|
|
@@ -1925,8 +1929,8 @@ export const websiteTranslations = {
|
|
|
1925
1929
|
// channel
|
|
1926
1930
|
field_channel_name: 'Name',
|
|
1927
1931
|
field_channel_description: 'Description',
|
|
1928
|
-
field_channel_type: 'Type'
|
|
1929
|
-
}
|
|
1932
|
+
field_channel_type: 'Type'
|
|
1933
|
+
}
|
|
1930
1934
|
},
|
|
1931
1935
|
es: {
|
|
1932
1936
|
translation: {
|
|
@@ -1996,6 +2000,8 @@ export const websiteTranslations = {
|
|
|
1996
2000
|
"api.data.serverStorageFull": "El servidor ha alcanzado su capacidad máxima de almacenamiento. Inténtelo de nuevo más tarde.",
|
|
1997
2001
|
|
|
1998
2002
|
"tourpoint.importDatapack": "Importa paquetes de datos prediseñados con nuestra galería de paquetes: sitios web, comercio electrónico, CRM y más",
|
|
2003
|
+
"tourpoint.tutorials": "¡Déjate guiar por nuestros tutoriales paso a paso!",
|
|
2004
|
+
|
|
1999
2005
|
"add_language": "Añadir...",
|
|
2000
2006
|
"select_or_add_language": "Seleccione o añada un idioma para empezar.",
|
|
2001
2007
|
"field.richtext_t": "Texto enriquecido traducido",
|
|
@@ -2881,7 +2887,7 @@ export const websiteTranslations = {
|
|
|
2881
2887
|
"dataimporter.title": "Importar datos a {{model}}",
|
|
2882
2888
|
"dataimporter.hasCsvHeaders": "¿El archivo CSV contiene una fila de encabezado?",
|
|
2883
2889
|
"dataimporter.info": "Importa tu archivo JSON o CSV con los datos necesarios para tu modelo. Tus datos se copiarán a tu base de datos personal. La velocidad de transferencia es {{constante}}",
|
|
2884
|
-
"links.demo": "
|
|
2890
|
+
"links.demo": "Iniciar la demostración",
|
|
2885
2891
|
"filterstringfield.placeholder.regex": 'Expresión regular',
|
|
2886
2892
|
"cgu.h1": "Condiciones Generales de Uso (CGU)",
|
|
2887
2893
|
"cgu.h1.1": "1. Introducción",
|
|
@@ -3332,8 +3338,8 @@ export const websiteTranslations = {
|
|
|
3332
3338
|
// channel
|
|
3333
3339
|
field_channel_name: 'Nombre',
|
|
3334
3340
|
field_channel_description: 'Descripción',
|
|
3335
|
-
field_channel_type: 'Tipo'
|
|
3336
|
-
}
|
|
3341
|
+
field_channel_type: 'Tipo'
|
|
3342
|
+
}
|
|
3337
3343
|
},
|
|
3338
3344
|
pt: {
|
|
3339
3345
|
translation: {
|
|
@@ -3403,6 +3409,7 @@ export const websiteTranslations = {
|
|
|
3403
3409
|
"api.data.serverStorageFull": "O servidor atingiu a sua capacidade máxima de armazenamento. Tente novamente mais tarde.",
|
|
3404
3410
|
|
|
3405
3411
|
"tourpoint.importDatapack": "Importe pacotes de dados pré-criados com a nossa galeria de pacotes: sites, comércio eletrónico, CRM e muito mais!",
|
|
3412
|
+
"tourpoint.tutorials": "Deixe-se guiar seguindo os nossos tutoriais passo a passo!",
|
|
3406
3413
|
"add_language": "Adicionar...",
|
|
3407
3414
|
"select_or_add_language": "Selecione ou adicione um idioma para começar.",
|
|
3408
3415
|
"field.richtext_t": "Texto rico traduzido",
|
|
@@ -4291,7 +4298,7 @@ export const websiteTranslations = {
|
|
|
4291
4298
|
"dataimporter.title": "Importar dados para {{model}}",
|
|
4292
4299
|
"dataimporter.hasCsvHeaders": "O ficheiro CSV contém uma linha de cabeçalho?",
|
|
4293
4300
|
"dataimporter.info": "Importe o seu ficheiro JSON ou CSV contendo os dados necessários para o seu modelo. Os seus dados serão copiados para a sua base de dados pessoal. A taxa de transferência é {{constante}}",
|
|
4294
|
-
"links.demo": "
|
|
4301
|
+
"links.demo": "Iniciar a demonstração",
|
|
4295
4302
|
"filterstringfield.placeholder.regex": 'Expressão regular',
|
|
4296
4303
|
"cgu.h1": "Termos e Condições Gerais de Uso (TCGU)",
|
|
4297
4304
|
"cgu.h1.1": "1. Introdução",
|
|
@@ -4736,8 +4743,8 @@ export const websiteTranslations = {
|
|
|
4736
4743
|
// channel
|
|
4737
4744
|
field_channel_name: 'Nome',
|
|
4738
4745
|
field_channel_description: 'Descrição',
|
|
4739
|
-
field_channel_type: 'Tipo'
|
|
4740
|
-
}
|
|
4746
|
+
field_channel_type: 'Tipo'
|
|
4747
|
+
}
|
|
4741
4748
|
},
|
|
4742
4749
|
de: {
|
|
4743
4750
|
translation: {
|
|
@@ -4795,6 +4802,8 @@ export const websiteTranslations = {
|
|
|
4795
4802
|
"api.data.serverStorageFull": "Der Server hat seine maximale Speicherkapazität erreicht. Bitte versuchen Sie es später erneut.",
|
|
4796
4803
|
|
|
4797
4804
|
"tourpoint.importDatapack": "Vorgefertigte Datenpakete mit unserer Packgalerie importieren: Websites, E-Commerce, CRM und mehr!",
|
|
4805
|
+
"tourpoint.tutorials": "Lassen Sie sich von unseren Schritt-für-Schritt-Tutorials leiten!",
|
|
4806
|
+
|
|
4798
4807
|
"add_language": "Hinzufügen...",
|
|
4799
4808
|
"select_or_add_language": "Wählen oder fügen Sie eine Sprache hinzu, um zu beginnen.",
|
|
4800
4809
|
"field.richtext_t": "Übersetzter Rich Text",
|
|
@@ -5672,7 +5681,7 @@ export const websiteTranslations = {
|
|
|
5672
5681
|
"dataimporter.title": "Daten in {{model}} importieren",
|
|
5673
5682
|
"dataimporter.hasCsvHeaders": "Enthält die CSV-Datei eine Kopfzeile?",
|
|
5674
5683
|
"dataimporter.info": "Importieren Sie Ihre JSON- oder CSV-Datei mit den benötigten Daten für Ihr Modell. Ihre Daten werden in Ihre persönliche Datenbank kopiert. Die Übertragungsrate beträgt {{constante}}",
|
|
5675
|
-
"links.demo": "
|
|
5684
|
+
"links.demo": "Demo starten",
|
|
5676
5685
|
"filterstringfield.placeholder.regex": 'Regulärer Ausdruck',
|
|
5677
5686
|
"cgu.h1": "Nutzungsbedingungen (TOU)",
|
|
5678
5687
|
"cgu.h1.1": "1. Einleitung",
|
|
@@ -6122,8 +6131,8 @@ export const websiteTranslations = {
|
|
|
6122
6131
|
// channel
|
|
6123
6132
|
field_channel_name: 'Name',
|
|
6124
6133
|
field_channel_description: 'Beschreibung',
|
|
6125
|
-
field_channel_type: 'Typ'
|
|
6126
|
-
}
|
|
6134
|
+
field_channel_type: 'Typ'
|
|
6135
|
+
}
|
|
6127
6136
|
},
|
|
6128
6137
|
it: {
|
|
6129
6138
|
translation: {
|
|
@@ -6193,6 +6202,7 @@ export const websiteTranslations = {
|
|
|
6193
6202
|
"api.data.serverStorageFull": "Il server ha raggiunto la capacità massima di archiviazione. Riprova più tardi.",
|
|
6194
6203
|
|
|
6195
6204
|
"tourpoint.importDatapack": "Importa pacchetti di dati predefiniti con la nostra galleria di pacchetti: siti web, e-commerce, CRM e altro ancora!",
|
|
6205
|
+
"tourpoint.tutorials": "Lasciati guidare seguendo i nostri tutorial passo dopo passo!",
|
|
6196
6206
|
"add_language": "Aggiungi...",
|
|
6197
6207
|
"select_or_add_language": "Seleziona o aggiungi una lingua per iniziare.",
|
|
6198
6208
|
"field.richtext_t": "Rich text tradotto",
|
|
@@ -7077,7 +7087,7 @@ export const websiteTranslations = {
|
|
|
7077
7087
|
"dataimporter.title": "Importa dati in {{model}}",
|
|
7078
7088
|
"dataimporter.hasCsvHeaders": "Il file CSV contiene una riga di intestazione?",
|
|
7079
7089
|
"dataimporter.info": "Importa il tuo file JSON o CSV contenente i dati necessari per il tuo modello. I tuoi dati verranno copiati nel tuo database personale. La velocità di trasferimento è {{constante}}",
|
|
7080
|
-
"links.demo": "
|
|
7090
|
+
"links.demo": "Avvia la demo",
|
|
7081
7091
|
"filterstringfield.placeholder.regex": 'Espressione regolare',
|
|
7082
7092
|
"cgu.h1": "Termini di Utilizzo (TOU)",
|
|
7083
7093
|
"cgu.h1.1": "1. Introduzione",
|
|
@@ -7522,8 +7532,8 @@ export const websiteTranslations = {
|
|
|
7522
7532
|
// channel
|
|
7523
7533
|
field_channel_name: 'Nome',
|
|
7524
7534
|
field_channel_description: 'Descrizione',
|
|
7525
|
-
field_channel_type: 'Tipo'
|
|
7526
|
-
}
|
|
7535
|
+
field_channel_type: 'Tipo'
|
|
7536
|
+
}
|
|
7527
7537
|
},
|
|
7528
7538
|
cs: {
|
|
7529
7539
|
translation: {
|
|
@@ -7593,6 +7603,7 @@ export const websiteTranslations = {
|
|
|
7593
7603
|
"api.data.serverStorageFull": "Server dosáhl maximální úložné kapacity. Zkuste to prosím znovu později.",
|
|
7594
7604
|
|
|
7595
7605
|
"tourpoint.importDatapack": "Importujte předpřipravené datové balíčky s naší galerií balíčků: Webové stránky, e-commerce, CRM a další!",
|
|
7606
|
+
"tourpoint.tutorials": "Nechte se vést našimi podrobnými tutoriály!",
|
|
7596
7607
|
"add_language": "Přidat...",
|
|
7597
7608
|
"select_or_add_language": "Vyberte nebo přidejte jazyk pro zahájení.",
|
|
7598
7609
|
"field.richtext_t": "Přeložený formátovaný text",
|
|
@@ -8472,7 +8483,7 @@ export const websiteTranslations = {
|
|
|
8472
8483
|
"dataimporter.title": "Importovat data do {{model}}",
|
|
8473
8484
|
"dataimporter.hasCsvHeaders": "Obsahuje soubor CSV řádek záhlaví?",
|
|
8474
8485
|
"dataimporter.info": "Importujte soubor JSON nebo CSV obsahující požadovaná data pro váš model. Vaše data budou zkopírována do vaší osobní databáze. Přenosová rychlost je {{constante}}",
|
|
8475
|
-
"links.demo": "
|
|
8486
|
+
"links.demo": "Spustit demo",
|
|
8476
8487
|
"filterstringfield.placeholder.regex": 'Regulární výraz',
|
|
8477
8488
|
"cgu.h1": "Podmínky použití (TOU)",
|
|
8478
8489
|
"cgu.h1.1": "1. Úvod",
|
|
@@ -8917,8 +8928,8 @@ export const websiteTranslations = {
|
|
|
8917
8928
|
// channel
|
|
8918
8929
|
field_channel_name: 'Jméno',
|
|
8919
8930
|
field_channel_description: 'Popis',
|
|
8920
|
-
field_channel_type: 'Typ'
|
|
8921
|
-
}
|
|
8931
|
+
field_channel_type: 'Typ'
|
|
8932
|
+
}
|
|
8922
8933
|
},
|
|
8923
8934
|
ru: {
|
|
8924
8935
|
translation: {
|
|
@@ -8988,6 +8999,7 @@ export const websiteTranslations = {
|
|
|
8988
8999
|
"api.data.serverStorageFull": "Сервер достиг максимального объёма хранилища. Повторите попытку позже.",
|
|
8989
9000
|
|
|
8990
9001
|
"tourpoint.importDatapack": "Импортируйте готовые пакеты данных с помощью нашей галереи пакетов: веб-сайты, электронная коммерция, CRM и многое другое!",
|
|
9002
|
+
"tourpoint.tutorials": "Доверьтесь нашим пошаговым инструкциям!",
|
|
8991
9003
|
"add_language": "Добавить...",
|
|
8992
9004
|
"select_or_add_language": "Выберите или добавьте язык для начала работы",
|
|
8993
9005
|
"field.richtext_t": "Переведенный форматированный текст",
|
|
@@ -9875,7 +9887,7 @@ export const websiteTranslations = {
|
|
|
9875
9887
|
"dataimporter.title": "Импортировать данные в {{model}}",
|
|
9876
9888
|
"dataimporter.hasCsvHeaders": "Содержит ли CSV-файл строку заголовка?",
|
|
9877
9889
|
"dataimporter.info": "Импортируйте файл JSON или CSV, содержащий необходимые данные для вашей модели. Ваши данные будут скопированы в вашу личную базу данных. Скорость передачи составляет {{constante}}",
|
|
9878
|
-
"links.demo": "
|
|
9890
|
+
"links.demo": "Запустить демо",
|
|
9879
9891
|
"filterstringfield.placeholder.regex": 'Регулярное выражение',
|
|
9880
9892
|
"cgu.h1": "Условия использования (TOU)",
|
|
9881
9893
|
"cgu.h1.1": "1. Введение",
|
|
@@ -10322,8 +10334,8 @@ export const websiteTranslations = {
|
|
|
10322
10334
|
// channel
|
|
10323
10335
|
field_channel_name: 'Имя',
|
|
10324
10336
|
field_channel_description: 'Описание',
|
|
10325
|
-
field_channel_type: 'Тип'
|
|
10326
|
-
}
|
|
10337
|
+
field_channel_type: 'Тип'
|
|
10338
|
+
}
|
|
10327
10339
|
},
|
|
10328
10340
|
ar: {
|
|
10329
10341
|
translation: {
|
|
@@ -10395,6 +10407,8 @@ export const websiteTranslations = {
|
|
|
10395
10407
|
"api.data.storageLimitExceeded": "سيتم تجاوز حد التخزين البالغ {{limit}} ميجابايت بهذه الإضافة.",
|
|
10396
10408
|
"api.data.serverStorageFull": "وصل الخادم إلى الحد الأقصى لسعة التخزين. يُرجى المحاولة مرة أخرى لاحقًا.",
|
|
10397
10409
|
"tourpoint.importDatapack": "استورد حزم البيانات المعدة مسبقًا باستخدام معرض الحزم لدينا: مواقع الويب، والتجارة الإلكترونية، وإدارة علاقات العملاء، والمزيد!",
|
|
10410
|
+
"tourpoint.tutorials": "دع نفسك تسترشد باتباع دروسنا التعليمية خطوة بخطوة!",
|
|
10411
|
+
|
|
10398
10412
|
"add_language": "إضافة...",
|
|
10399
10413
|
"select_or_add_language": "اختر أو أضف لغة للبدء.",
|
|
10400
10414
|
"field.richtext_t": "نص منسق مترجم",
|
|
@@ -11297,7 +11311,7 @@ export const websiteTranslations = {
|
|
|
11297
11311
|
"dataimporter.title": "استيراد البيانات إلى {{model}}",
|
|
11298
11312
|
"dataimporter.hasCsvHeaders": "هل يحتوي ملف CSV على صف رأس؟",
|
|
11299
11313
|
"dataimporter.info": "استورد ملف JSON أو CSV الذي يحتوي على البيانات المطلوبة لنموذجك. سيتم نسخ بياناتك إلى قاعدة بياناتك الشخصية. معدل النقل ثابت ({{constante}}).",
|
|
11300
|
-
"links.demo": "
|
|
11314
|
+
"links.demo": "ابدأ العرض التوضيحي",
|
|
11301
11315
|
"filterstringfield.placeholder.regex": 'تعبير عادي',
|
|
11302
11316
|
"cgu.h1": "الشروط العامة للاستخدام (GTU)",
|
|
11303
11317
|
"cgu.h1.1": "1. المقدمة",
|
|
@@ -11743,7 +11757,7 @@ export const websiteTranslations = {
|
|
|
11743
11757
|
// channel
|
|
11744
11758
|
field_channel_name: 'الاسم',
|
|
11745
11759
|
field_channel_description: 'الوصف',
|
|
11746
|
-
field_channel_type: 'النوع'
|
|
11760
|
+
field_channel_type: 'النوع'
|
|
11747
11761
|
}
|
|
11748
11762
|
},
|
|
11749
11763
|
sv: {
|
|
@@ -11814,6 +11828,7 @@ export const websiteTranslations = {
|
|
|
11814
11828
|
"api.data.serverStorageFull": "Servern har nått sin maximala lagringskapacitet. Försök igen senare.",
|
|
11815
11829
|
|
|
11816
11830
|
"tourpoint.importDatapack": "Importera färdiga datapaket med vårt paketgalleri: Webbplatser, e-handel, CRM och mer!",
|
|
11831
|
+
"tourpoint.tutorials": "Låt dig vägledas av våra steg-för-steg-handledningar!",
|
|
11817
11832
|
"add_language": "Lägg till...",
|
|
11818
11833
|
"select_or_add_language": "Välj eller lägg till ett språk för att komma igång.",
|
|
11819
11834
|
"field.richtext_t": "Översatt RTF",
|
|
@@ -12696,7 +12711,7 @@ export const websiteTranslations = {
|
|
|
12696
12711
|
"dataimporter.title": "Importera data till {{model}}",
|
|
12697
12712
|
"dataimporter.hasCsvHeaders": "Innehåller CSV-filen en rubrikrad?",
|
|
12698
12713
|
"dataimporter.info": "Importera din JSON- eller CSV-fil som innehåller den data som krävs för din modell. Dina data kommer att kopieras till din personliga databas. Överföringshastigheten är {{constante}}",
|
|
12699
|
-
"links.demo": "
|
|
12714
|
+
"links.demo": "Starta demon",
|
|
12700
12715
|
"filterstringfield.placeholder.regex": 'Reguljärt uttryck',
|
|
12701
12716
|
"cgu.h1": "Användarvillkor (Användarvillkor)",
|
|
12702
12717
|
"cgu.h1.1": "1. Introduktion",
|
|
@@ -13138,7 +13153,7 @@ export const websiteTranslations = {
|
|
|
13138
13153
|
// channel
|
|
13139
13154
|
field_channel_name: 'Namn',
|
|
13140
13155
|
field_channel_description: 'Beskrivning',
|
|
13141
|
-
field_channel_type: 'Typ'
|
|
13156
|
+
field_channel_type: 'Typ'
|
|
13142
13157
|
}
|
|
13143
13158
|
},
|
|
13144
13159
|
el: {
|
|
@@ -13209,6 +13224,7 @@ export const websiteTranslations = {
|
|
|
13209
13224
|
"api.data.serverStorageFull": "Ο διακομιστής έχει φτάσει στη μέγιστη χωρητικότητα αποθήκευσης. Παρακαλώ προσπαθήστε ξανά αργότερα.",
|
|
13210
13225
|
|
|
13211
13226
|
"tourpoint.importDatapack": "Εισαγάγετε προκατασκευασμένα πακέτα δεδομένων με τη συλλογή πακέτων μας: Ιστότοποι, ηλεκτρονικό εμπόριο, CRM και πολλά άλλα!",
|
|
13227
|
+
"tourpoint.tutorials": "Ακολουθώντας τα βήμα προς βήμα εκπαιδευτικά μας βίντεο, αφήστε τον εαυτό σας να σας καθοδηγήσει!",
|
|
13212
13228
|
"add_language": "Προσθήκη...",
|
|
13213
13229
|
"select_or_add_language": "Επιλέξτε ή προσθέστε μια γλώσσα για να ξεκινήσετε.",
|
|
13214
13230
|
"field.richtext_t": "Μεταφρασμένο εμπλουτισμένο κείμενο",
|
|
@@ -14096,7 +14112,7 @@ export const websiteTranslations = {
|
|
|
14096
14112
|
"dataimporter.title": "Εισαγωγή δεδομένων στο {{model}}",
|
|
14097
14113
|
"dataimporter.hasCsvHeaders": "Περιέχει το αρχείο CSV μια γραμμή κεφαλίδας;",
|
|
14098
14114
|
"dataimporter.info": "Εισαγάγετε το αρχείο JSON που περιέχει τα απαιτούμενα δεδομένα για το μοντέλο σας. Τα δεδομένα σας θα αντιγραφούν στην προσωπική σας βάση δεδομένων. Ο ρυθμός μεταφοράς είναι {{constante}}",
|
|
14099
|
-
"links.demo": "
|
|
14115
|
+
"links.demo": "Έναρξη της επίδειξης",
|
|
14100
14116
|
"filterstringfield.placeholder.regex": "Κανονική έκφραση",
|
|
14101
14117
|
"cgu.h1": "Γενικοί όροι χρήσης (GTU)",
|
|
14102
14118
|
"cgu.h1.1": "1. Εισαγωγή",
|
|
@@ -14545,6 +14561,7 @@ export const websiteTranslations = {
|
|
|
14545
14561
|
},
|
|
14546
14562
|
fa: {
|
|
14547
14563
|
"translation": {
|
|
14564
|
+
"links.demo": "شروع نسخهٔ نمایشی",
|
|
14548
14565
|
"cb.searchOperator": "جستجوی یک عملگر...",
|
|
14549
14566
|
"cb.changeOperator": "تغییر عملگر",
|
|
14550
14567
|
"cb.deleteBlock": "حذف این بلوک",
|
|
@@ -14611,6 +14628,7 @@ export const websiteTranslations = {
|
|
|
14611
14628
|
"api.data.serverStorageFull": "سرور به حداکثر ظرفیت ذخیرهسازی خود رسیده است. لطفاً بعداً دوباره امتحان کنید.",
|
|
14612
14629
|
|
|
14613
14630
|
"tourpoint.importDatapack": "بستههای داده از پیش ساخته شده را با گالری بستههای ما وارد کنید: وبسایتها، تجارت الکترونیک، CRM و موارد دیگر!",
|
|
14631
|
+
"tourpoint.tutorials": "با دنبال کردن آموزشهای گام به گام ما، خودتان را راهنمایی کنید!",
|
|
14614
14632
|
"add_language": "اضافه کردن...",
|
|
14615
14633
|
"select_or_add_language": "برای شروع، یک زبان را انتخاب یا اضافه کنید.",
|
|
14616
14634
|
"field.richtext_t": "متن غنی ترجمه شده",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "data-primals-engine",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.8-rc.1",
|
|
4
4
|
"description": "data-primals-engine is the package responsible from handling large amount of data in a practical and performant way. It can handle large amount of data in a practical and performant way. It can also get workflow models working (for automation), and fully supports internationalisation.",
|
|
5
5
|
"main": "src/engine.js",
|
|
6
6
|
"type": "module",
|
|
@@ -102,13 +102,12 @@
|
|
|
102
102
|
"client"
|
|
103
103
|
],
|
|
104
104
|
"keywords": [
|
|
105
|
-
"
|
|
106
|
-
"
|
|
107
|
-
"
|
|
105
|
+
"data-driven engine",
|
|
106
|
+
"headless CMS",
|
|
107
|
+
"backend",
|
|
108
108
|
"automation",
|
|
109
|
-
"
|
|
110
|
-
"
|
|
111
|
-
"S3"
|
|
109
|
+
"AWS S3",
|
|
110
|
+
"MongoDB"
|
|
112
111
|
],
|
|
113
112
|
"author": "anonympins",
|
|
114
113
|
"license": "MIT",
|
package/src/engine.js
CHANGED
|
@@ -41,17 +41,35 @@ const secureContext = tls.createSecureContext({
|
|
|
41
41
|
ca: caFile, cert: certFile, key: keyFile
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
+
export const dbUrl = process.env.CI ? 'mongodb://mongodb:27017' : (process.env.MONGO_DB_URL || 'mongodb://127.0.0.1:27017');
|
|
45
|
+
|
|
44
46
|
const isTlsActive = !(!process.env.TLS || ["0", "false"].includes(process.env.TLS.toLowerCase()));
|
|
45
47
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
const clientOptions = {
|
|
49
|
+
maxPoolSize: databasePoolSize
|
|
50
|
+
};
|
|
51
|
+
// On ajoute les options TLS si elles sont activées
|
|
52
|
+
if (isTlsActive) {
|
|
53
|
+
clientOptions.tls = true;
|
|
54
|
+
// Chemin vers le certificat de l'autorité de certification (pour faire confiance au serveur)
|
|
55
|
+
if (process.env.CA_CERT) {
|
|
56
|
+
clientOptions.tlsCAFile = process.env.CA_CERT;
|
|
57
|
+
}
|
|
58
|
+
// Chemin vers le certificat et la clé du CLIENT (pour que le serveur vous fasse confiance)
|
|
59
|
+
if (process.env.CERT_KEY) {
|
|
60
|
+
clientOptions.tlsCertificateKeyFile = process.env.CERT_KEY;
|
|
61
|
+
}
|
|
62
|
+
// Options pour le développement (à utiliser avec prudence)
|
|
63
|
+
if (tlsAllowInvalidCertificates) {
|
|
64
|
+
clientOptions.tlsAllowInvalidCertificates = true;
|
|
65
|
+
}
|
|
66
|
+
if (tlsAllowInvalidHostnames) {
|
|
67
|
+
clientOptions.tlsAllowInvalidHostnames = true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const MongoClient = new InternalMongoClient(dbUrl, clientOptions);
|
|
72
|
+
|
|
55
73
|
|
|
56
74
|
// Database Name
|
|
57
75
|
export const MongoDatabase = MongoClient.db(dbName);
|
|
@@ -112,7 +112,7 @@ function sanitize(target, options = {}) {
|
|
|
112
112
|
export function middleware(options = {}) {
|
|
113
113
|
const hasOnSanitize = typeof options.onSanitize === 'function';
|
|
114
114
|
return function (req, res, next) {
|
|
115
|
-
['body', 'params', 'headers', 'query'].forEach(function (key) {
|
|
115
|
+
['body', 'fields', 'params', 'headers', 'query'].forEach(function (key) {
|
|
116
116
|
if (req[key]) {
|
|
117
117
|
// The _sanitize function mutates the req[key] object in-place.
|
|
118
118
|
// We only need to capture whether it was sanitized to call the hook.
|
package/src/modules/data.js
CHANGED
|
@@ -89,6 +89,8 @@ import {getAllPacks} from "../packs.js";
|
|
|
89
89
|
import {throttleMiddleware} from "../middlewares/throttle.js";
|
|
90
90
|
import {Config} from "../config.js";
|
|
91
91
|
import {profiles} from "../../client/src/constants.js";
|
|
92
|
+
import {processFilterPlaceholders} from "../../client/src/filter.js";
|
|
93
|
+
import {tutorialsConfig} from "../../client/src/tutorials.js";
|
|
92
94
|
|
|
93
95
|
// Obtenir le chemin du répertoire courant de manière fiable avec ES Modules
|
|
94
96
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -1507,6 +1509,128 @@ export async function onInit(defaultEngine) {
|
|
|
1507
1509
|
}
|
|
1508
1510
|
});
|
|
1509
1511
|
|
|
1512
|
+
/**
|
|
1513
|
+
* Route pour vérifier si une condition de complétion est remplie.
|
|
1514
|
+
* Utilise la fonction `searchData` existante pour interroger les données.
|
|
1515
|
+
*/
|
|
1516
|
+
engine.post('/api/tutorials/check-completion', middlewareAuthenticator, async (req, res) => {
|
|
1517
|
+
try {
|
|
1518
|
+
const { model, filter, limit } = req.fields;
|
|
1519
|
+
const user = req.me;
|
|
1520
|
+
|
|
1521
|
+
if (!model || !filter || limit === undefined) {
|
|
1522
|
+
return res.status(400).json({ error: 'Payload de condition de complétion invalide.' });
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
const processedFilter = processFilterPlaceholders(filter, user);
|
|
1526
|
+
|
|
1527
|
+
// On utilise la fonction de recherche interne de l'application
|
|
1528
|
+
const searchResult = await searchData({
|
|
1529
|
+
query: {
|
|
1530
|
+
model,
|
|
1531
|
+
filter: processedFilter,
|
|
1532
|
+
limit, // Optimisation : pas besoin de plus de résultats
|
|
1533
|
+
page: 1
|
|
1534
|
+
},
|
|
1535
|
+
user: user
|
|
1536
|
+
});
|
|
1537
|
+
|
|
1538
|
+
// searchData devrait renvoyer un `count` total des documents correspondants
|
|
1539
|
+
const isCompleted = searchResult.count >= limit;
|
|
1540
|
+
|
|
1541
|
+
res.json({ isCompleted });
|
|
1542
|
+
|
|
1543
|
+
} catch (error) {
|
|
1544
|
+
console.error('[Tutoriel Check Error]', error);
|
|
1545
|
+
res.status(500).json({ error: 'Erreur lors de la vérification de la complétion.', details: error.message });
|
|
1546
|
+
}
|
|
1547
|
+
});
|
|
1548
|
+
|
|
1549
|
+
|
|
1550
|
+
|
|
1551
|
+
engine.post('/api/tutorials/set-active', middlewareAuthenticator, async (req, res) => {
|
|
1552
|
+
try {
|
|
1553
|
+
const { tutorialState } = req.fields;
|
|
1554
|
+
const user = req.me;
|
|
1555
|
+
|
|
1556
|
+
if (tutorialState !== null && (typeof tutorialState !== 'object' || !tutorialState.id)) {
|
|
1557
|
+
return res.status(400).json({ error: 'Invalid tutorial state payload.' });
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
// Créer une représentation de l'utilisateur mis à jour pour la réponse
|
|
1561
|
+
const updatedData = { activeTutorial: tutorialState };
|
|
1562
|
+
|
|
1563
|
+
await engine.userProvider.updateUser(user, updatedData);
|
|
1564
|
+
|
|
1565
|
+
// --- MODIFICATION ---
|
|
1566
|
+
res.json({
|
|
1567
|
+
success: true,
|
|
1568
|
+
updatedData // Renvoyer l'objet utilisateur complet
|
|
1569
|
+
});
|
|
1570
|
+
|
|
1571
|
+
} catch (error) {
|
|
1572
|
+
console.error('[Tutoriel Set Active Error]', error);
|
|
1573
|
+
res.status(500).json({ error: 'Error setting active tutorial.', details: error.message });
|
|
1574
|
+
}
|
|
1575
|
+
});
|
|
1576
|
+
|
|
1577
|
+
// ...
|
|
1578
|
+
|
|
1579
|
+
engine.post('/api/tutorials/:tutorialId/claim-rewards', middlewareAuthenticator, async (req, res) => {
|
|
1580
|
+
try {
|
|
1581
|
+
const { tutorialId } = req.params;
|
|
1582
|
+
const user = req.me; // L'objet utilisateur est déjà chargé
|
|
1583
|
+
const tutorial = tutorialsConfig.find(t => t.id === tutorialId);
|
|
1584
|
+
|
|
1585
|
+
if (!tutorial) return res.status(404).json({ error: 'Tutoriel non trouvé.' });
|
|
1586
|
+
if (!tutorial.rewards) return res.status(400).json({ error: 'Ce tutoriel n\'a pas de récompenses.' });
|
|
1587
|
+
if (user.completedTutorials?.includes(tutorialId)) {
|
|
1588
|
+
return res.status(400).json({ error: 'Tutoriel déjà terminé.' });
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
const { xpBonus, skill, achievement, notification } = tutorial.rewards;
|
|
1592
|
+
|
|
1593
|
+
// --- LOGIQUE CORRIGÉE ---
|
|
1594
|
+
let newData= {};
|
|
1595
|
+
newData.xp = newData.xp || 0;
|
|
1596
|
+
newData.achievements = newData.achievements || [];
|
|
1597
|
+
newData.skills = newData.skills || [];
|
|
1598
|
+
newData.completedTutorials = newData.completedTutorials || [];
|
|
1599
|
+
|
|
1600
|
+
// Appliquer les récompenses directement sur l'objet newData
|
|
1601
|
+
if (xpBonus) newData.xp += xpBonus;
|
|
1602
|
+
if (achievement && !newData.achievements.includes(achievement)) newData.achievements.push(achievement);
|
|
1603
|
+
if (skill) {
|
|
1604
|
+
const existingSkill = newData.skills.find(s => s.name === skill.name);
|
|
1605
|
+
if (existingSkill) {
|
|
1606
|
+
existingSkill.points += skill.points;
|
|
1607
|
+
} else {
|
|
1608
|
+
newData.skills.push({ name: skill.name, points: skill.points });
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
newData.completedTutorials.push(tutorialId);
|
|
1613
|
+
newData.activeTutorial = null;
|
|
1614
|
+
|
|
1615
|
+
await engine.userProvider.updateUser(user, newData);
|
|
1616
|
+
|
|
1617
|
+
const translatedNotification = {
|
|
1618
|
+
title: i18n.t(notification.title, notification.title),
|
|
1619
|
+
message: i18n.t(notification.message, notification.message)
|
|
1620
|
+
};
|
|
1621
|
+
|
|
1622
|
+
res.json({
|
|
1623
|
+
success: true,
|
|
1624
|
+
userUpdate: newData, // Renvoyer l'objet utilisateur complet et mis à jour
|
|
1625
|
+
notification: translatedNotification
|
|
1626
|
+
});
|
|
1627
|
+
|
|
1628
|
+
} catch (error) {
|
|
1629
|
+
console.error('[Tutoriel Rewards Error]', error);
|
|
1630
|
+
res.status(500).json({ error: 'Erreur lors de l\'attribution des récompenses.', details: error.message });
|
|
1631
|
+
}
|
|
1632
|
+
});
|
|
1633
|
+
|
|
1510
1634
|
engine.get('/api/import/progress/:jobId', [middlewareAuthenticator], async (req, res) => {
|
|
1511
1635
|
const { jobId } = req.params;
|
|
1512
1636
|
const user = req.me;
|
|
@@ -3365,6 +3489,24 @@ async function insertAndResolveRelations(doc, model, collection, me, idMap) {
|
|
|
3365
3489
|
return existingDoc._id;
|
|
3366
3490
|
}
|
|
3367
3491
|
|
|
3492
|
+
for (const field of model.fields) {
|
|
3493
|
+
if (field.type === 'relation' && field.relationFilter && docToProcess[field.name]) {
|
|
3494
|
+
const relatedIds = Array.isArray(docToProcess[field.name]) ? docToProcess[field.name] : [docToProcess[field.name]];
|
|
3495
|
+
for (const id of relatedIds) {
|
|
3496
|
+
const targetCollection = await getCollectionForUser(me, field.targetModel);
|
|
3497
|
+
const validationQuery = {
|
|
3498
|
+
_id: new ObjectId(id), // L'ID doit correspondre
|
|
3499
|
+
...field.relationFilter // ET le document doit respecter le filtre
|
|
3500
|
+
};
|
|
3501
|
+
const relatedDoc = await targetCollection.findOne(validationQuery);
|
|
3502
|
+
if (!relatedDoc) {
|
|
3503
|
+
// Si on ne trouve rien, c'est que l'ID est invalide ou ne respecte pas le filtre.
|
|
3504
|
+
throw new Error(`La valeur '${id}' pour le champ '${field.name}' ne respecte pas le filtre de relation défini.`);
|
|
3505
|
+
}
|
|
3506
|
+
}
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
|
|
3368
3510
|
// Insertion en conservant éventuellement l'ID original
|
|
3369
3511
|
const result = docToProcess._id
|
|
3370
3512
|
? await collection.insertOne(docToProcess)
|
|
@@ -3584,6 +3726,33 @@ const internalEditOrPatchData = async (modelName, filter, data, files, user, isP
|
|
|
3584
3726
|
}
|
|
3585
3727
|
}
|
|
3586
3728
|
|
|
3729
|
+
for (const field of model.fields) {
|
|
3730
|
+
// On ne vérifie que si un champ de relation avec un filtre est en cours de modification.
|
|
3731
|
+
if (field.type === 'relation' && field.relationFilter && updateData[field.name] !== undefined) {
|
|
3732
|
+
const relatedIds = Array.isArray(updateData[field.name])
|
|
3733
|
+
? updateData[field.name]
|
|
3734
|
+
: (updateData[field.name] ? [updateData[field.name]] : []);
|
|
3735
|
+
|
|
3736
|
+
for (const id of relatedIds) {
|
|
3737
|
+
if (!id || !isObjectId(id)) continue; // Ignorer les valeurs null/invalides
|
|
3738
|
+
|
|
3739
|
+
const targetCollection = await getCollectionForUser(user, field.relation);
|
|
3740
|
+
|
|
3741
|
+
const validationQuery = {
|
|
3742
|
+
_id: new ObjectId(id),
|
|
3743
|
+
...field.relationFilter
|
|
3744
|
+
};
|
|
3745
|
+
|
|
3746
|
+
const relatedDoc = await targetCollection.findOne(validationQuery);
|
|
3747
|
+
|
|
3748
|
+
if (!relatedDoc) {
|
|
3749
|
+
// Si on ne trouve rien, c'est que l'ID est invalide ou ne respecte pas le filtre.
|
|
3750
|
+
throw new Error(`La valeur '${id}' pour le champ '${field.name}' ne respecte pas le filtre de relation défini.`);
|
|
3751
|
+
}
|
|
3752
|
+
}
|
|
3753
|
+
}
|
|
3754
|
+
}
|
|
3755
|
+
|
|
3587
3756
|
// 8. Calcul du nouveau hash et préparation des données finales
|
|
3588
3757
|
const finalStateForHash = { ...existingDocs[0], ...updateData };
|
|
3589
3758
|
const newHash = getFieldValueHash(model, finalStateForHash);
|