data-primals-engine 1.7.2 → 1.7.3
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 +160 -160
- package/client/package-lock.json +8430 -8430
- package/client/src/APIInfo.jsx +1 -1
- package/client/src/App.jsx +2 -1
- package/client/src/App.scss +1635 -1626
- package/client/src/AssistantChat.jsx +1 -0
- package/client/src/CalendarView.jsx +1 -0
- package/client/src/ContentView.jsx +3 -3
- package/client/src/Dashboard.jsx +5 -2
- package/client/src/DashboardChart.jsx +1 -0
- package/client/src/DashboardFlexViewItem.jsx +1 -0
- package/client/src/DashboardHtmlViewItem.jsx +1 -0
- package/client/src/DashboardView.jsx +2 -0
- package/client/src/DataEditor.jsx +0 -1
- package/client/src/DataImporter.jsx +489 -468
- package/client/src/DataLayout.jsx +6 -3
- package/client/src/DataTable.jsx +4 -3
- package/client/src/Dialog.jsx +92 -90
- package/client/src/Dialog.scss +122 -116
- package/client/src/DisplayFlexNodeRenderer.jsx +1 -0
- package/client/src/FlexBuilderControls.jsx +1 -0
- package/client/src/FlexBuilderPreview.jsx +1 -1
- package/client/src/FlexNode.jsx +1 -1
- package/client/src/HistoryDialog.jsx +3 -2
- package/client/src/KPIWidget.jsx +1 -1
- package/client/src/KanbanView.jsx +1 -0
- package/client/src/ModelCreator.jsx +4 -0
- package/client/src/ModelList.jsx +1 -0
- package/client/src/PackGallery.jsx +5 -4
- package/client/src/RTETrans.jsx +1 -1
- package/client/src/RelationField.jsx +2 -0
- package/client/src/RelationSelectorWidget.jsx +2 -0
- package/client/src/RelationValue.jsx +1 -0
- package/client/src/RestoreDialog.jsx +1 -0
- package/client/src/WorkflowEditor.jsx +3 -0
- package/client/src/contexts/CommandContext.jsx +2 -1
- package/client/src/contexts/ModelContext.jsx +3 -3
- package/client/src/hooks/data.js +1 -0
- package/client/src/hooks/useValidation.js +1 -0
- package/client/src/translations.js +24 -24
- package/doc/AI-assistance.md +87 -63
- package/doc/Concepts.md +122 -0
- package/doc/Custom-Endpoints.md +31 -0
- package/doc/Event-system.md +13 -14
- package/doc/Home.md +33 -0
- package/doc/Modules.md +83 -0
- package/doc/Packs-gallery.md +8 -23
- package/doc/Workflows.md +32 -0
- package/doc/automation-workflows.md +141 -102
- package/doc/dashboards-kpis-charts.md +47 -49
- package/doc/data-management.md +126 -120
- package/doc/data-models.md +68 -75
- package/doc/roles-permissions.md +144 -43
- package/doc/sharding-replication.md +158 -0
- package/doc/users.md +54 -30
- package/package.json +1 -1
- package/server.js +37 -37
- package/src/constants.js +7 -8
- package/src/data.js +521 -520
- package/src/email.js +157 -154
- package/src/engine.js +117 -7
- package/src/gameObject.js +6 -0
- package/src/i18n.js +0 -1
- package/src/modules/auth-google/index.js +53 -50
- package/src/modules/bucket.js +346 -339
- package/src/modules/data/data.backup.js +400 -376
- package/src/modules/data/data.cluster.js +410 -42
- package/src/modules/data/data.operations.js +3666 -3635
- package/src/modules/data/data.replication.js +106 -64
- package/src/modules/data/data.routes.js +87 -64
- package/src/modules/data/data.scheduling.js +2 -1
- package/src/modules/file.js +248 -247
- package/src/modules/user.js +11 -1
- package/src/sso.js +91 -25
- package/test/cluster.test.js +221 -0
- package/test/core.test.js +0 -2
- package/test/replication.test.js +163 -0
- package/doc/core-concepts.md +0 -33
package/client/src/RTETrans.jsx
CHANGED
|
@@ -15,7 +15,7 @@ const RTETrans = ({ value, onChange, field }) => {
|
|
|
15
15
|
params.append('model', 'lang');
|
|
16
16
|
params.append("_user", getUserId(me));
|
|
17
17
|
params.append("depth", '1');
|
|
18
|
-
return fetch(`/api/data/search?${params.toString()}`, { signal, method: 'POST',
|
|
18
|
+
return fetch(`/api/data/search?${params.toString()}`, { credentials:"include", signal, method: 'POST', body: JSON.stringify({}), headers: { "Content-Type": "application/json"}})
|
|
19
19
|
.then((res) => res.json())
|
|
20
20
|
.then((data) => {
|
|
21
21
|
setAvailableLangs(data.data || []);
|
|
@@ -49,6 +49,7 @@ const RelationField = ({ field, help, onFocus, onBlur, onChange, value = null })
|
|
|
49
49
|
|
|
50
50
|
const response = await fetch(`/api/data/search?${params.toString()}`, {
|
|
51
51
|
method: 'POST',
|
|
52
|
+
credentials:"include",
|
|
52
53
|
headers: { 'Content-Type': 'application/json' },
|
|
53
54
|
body: JSON.stringify({ filter: {} }) // Le filtre peut être vide quand on cherche par IDs
|
|
54
55
|
});
|
|
@@ -126,6 +127,7 @@ const RelationField = ({ field, help, onFocus, onBlur, onChange, value = null })
|
|
|
126
127
|
body: JSON.stringify({ filter: finalFilter }),
|
|
127
128
|
method: 'POST',
|
|
128
129
|
signal: signal,
|
|
130
|
+
credentials:"include",
|
|
129
131
|
headers: { 'Content-Type': 'application/json' },
|
|
130
132
|
})
|
|
131
133
|
.then(e => e.json())
|
|
@@ -13,6 +13,7 @@ import { useAuthContext } from './contexts/AuthContext.jsx';
|
|
|
13
13
|
const insertDataAPI = async (modelName, data) => {
|
|
14
14
|
const response = await fetch(`/api/data`, {
|
|
15
15
|
method: 'POST',
|
|
16
|
+
credentials:"include",
|
|
16
17
|
headers: { 'Content-Type': 'application/json' },
|
|
17
18
|
body: JSON.stringify({model: modelName,data})
|
|
18
19
|
});
|
|
@@ -27,6 +28,7 @@ const searchDataAPI = async (modelName, filter) => {
|
|
|
27
28
|
const params = new URLSearchParams({ model: modelName, depth: '1' });
|
|
28
29
|
const response = await fetch(`/api/data/search?${params.toString()}`, {
|
|
29
30
|
method: 'POST',
|
|
31
|
+
credentials:"include",
|
|
30
32
|
headers: { 'Content-Type': 'application/json' },
|
|
31
33
|
body: JSON.stringify({ filter })
|
|
32
34
|
});
|
|
@@ -42,6 +42,7 @@ function RestoreDialog() {
|
|
|
42
42
|
try {
|
|
43
43
|
const response = await fetch(apiUrl, {
|
|
44
44
|
method: 'GET', // Ou 'POST' si l'API l'exige, mais GET semble plus probable ici
|
|
45
|
+
credentials:"include",
|
|
45
46
|
headers: {
|
|
46
47
|
'Accept': 'application/json',
|
|
47
48
|
// Ajoute d'autres headers si nécessaire (ex: Authorization si requis)
|
|
@@ -84,6 +84,7 @@ const WorkflowEditor = ({ workflowId }) => {
|
|
|
84
84
|
['mainWorkflow', workflowId],
|
|
85
85
|
() => fetch('/api/data/search?_user='+me.username, {
|
|
86
86
|
method: 'POST',
|
|
87
|
+
credentials:"include",
|
|
87
88
|
headers: { 'Content-Type': 'application/json' },
|
|
88
89
|
body: JSON.stringify({
|
|
89
90
|
model: 'workflow',
|
|
@@ -102,6 +103,7 @@ const WorkflowEditor = ({ workflowId }) => {
|
|
|
102
103
|
['workflowSteps', workflowId],
|
|
103
104
|
() => fetch('/api/data/search?_user=' + me.username, {
|
|
104
105
|
method: 'POST',
|
|
106
|
+
credentials:"include",
|
|
105
107
|
headers: { 'Content-Type': 'application/json' },
|
|
106
108
|
body: JSON.stringify({
|
|
107
109
|
model: 'workflowStep',
|
|
@@ -122,6 +124,7 @@ const WorkflowEditor = ({ workflowId }) => {
|
|
|
122
124
|
['workflowActions', workflowId],
|
|
123
125
|
() => fetch('/api/data/search?_user=' + me.username, {
|
|
124
126
|
method: 'POST',
|
|
127
|
+
credentials:"include",
|
|
125
128
|
headers: { 'Content-Type': 'application/json' },
|
|
126
129
|
body: JSON.stringify({
|
|
127
130
|
model: 'workflowAction',
|
|
@@ -116,7 +116,7 @@ export class InsertCommand {
|
|
|
116
116
|
async undo() {
|
|
117
117
|
// On garde une copie de l'item avant de le supprimer pour le redo.
|
|
118
118
|
if (!this.insertedItem?._id) throw new Error("Cannot undo insert: item ID is missing.");
|
|
119
|
-
const response = await fetch(`/api/data/${this.insertedItem._id}`, { method: 'DELETE' });
|
|
119
|
+
const response = await fetch(`/api/data/${this.insertedItem._id}`, { credentials:"include",method: 'DELETE' });
|
|
120
120
|
const result = await response.json();
|
|
121
121
|
if (!response.ok || !result.success) {
|
|
122
122
|
throw new Error(result.error || 'Undo (delete) failed');
|
|
@@ -184,6 +184,7 @@ export class DeleteCommand {
|
|
|
184
184
|
|
|
185
185
|
return fetch('/api/data?_user=system', { // On ajoute _user=system pour tracer l'origine de l'action
|
|
186
186
|
method: 'POST',
|
|
187
|
+
credentials:"include",
|
|
187
188
|
headers: { 'Content-Type': 'application/json' },
|
|
188
189
|
body: JSON.stringify({ model: this.modelName, data: reinsertData }),
|
|
189
190
|
}).then(res => res.json().then(data => {
|
|
@@ -45,7 +45,7 @@ export const ModelProvider = ({ children }) => {
|
|
|
45
45
|
const { data: dataModels = [], isFetched } = useQuery(['api/models', me], () => {
|
|
46
46
|
if(!me)
|
|
47
47
|
return Promise.reject();
|
|
48
|
-
return fetch(`/api/models?lang=${lang}&_user=${encodeURIComponent(getUserId(me))}
|
|
48
|
+
return fetch(`/api/models?lang=${lang}&_user=${encodeURIComponent(getUserId(me))}`, {credentials:"include"}).then((res) => {
|
|
49
49
|
if( !res.ok ){
|
|
50
50
|
return [];
|
|
51
51
|
}
|
|
@@ -118,7 +118,7 @@ export const ModelProvider = ({ children }) => {
|
|
|
118
118
|
if( rel){
|
|
119
119
|
return Promise.resolve(rel);
|
|
120
120
|
}
|
|
121
|
-
return fetch(`/api/data/search?${params.toString()}`, { signal, method: 'POST', headers: { "Content-Type": "application/json"}})
|
|
121
|
+
return fetch(`/api/data/search?${params.toString()}`, { credentials:"include",signal, method: 'POST', headers: { "Content-Type": "application/json"}})
|
|
122
122
|
.then((res) => res.json())
|
|
123
123
|
.then((e) => ({
|
|
124
124
|
count: e.count,
|
|
@@ -204,7 +204,7 @@ export const ModelProvider = ({ children }) => {
|
|
|
204
204
|
const c = pagedFilterToMongoConds(pagedFilters, model)
|
|
205
205
|
const filter= JSON.stringify({filter:{$and:c}});
|
|
206
206
|
|
|
207
|
-
return fetch(`/api/data/search?${params.toString()}`, { signal, method: 'POST', body: filter, headers: { "Content-Type": "application/json"}})
|
|
207
|
+
return fetch(`/api/data/search?${params.toString()}`, { signal, credentials: "include", method: 'POST', body: filter, headers: { "Content-Type": "application/json"}})
|
|
208
208
|
.then((res) => res.json())
|
|
209
209
|
.then((e) => {
|
|
210
210
|
return {
|
package/client/src/hooks/data.js
CHANGED
|
@@ -12,6 +12,7 @@ export const useData = (model, filter, options) => {
|
|
|
12
12
|
return useQuery([options.queryKey || model, filter, me], () => {
|
|
13
13
|
return fetch('/api/data/search?limit='+(options.limit||1)+'&lang='+(options.lang||lang)+'&_user=' + encodeURIComponent(getUserId(me)) + '&model='+model, {
|
|
14
14
|
method: 'POST',
|
|
15
|
+
credentials:"include",
|
|
15
16
|
body: JSON.stringify({filter}),
|
|
16
17
|
headers: { "Content-Type": "application/json"}}
|
|
17
18
|
)
|
|
@@ -457,8 +457,8 @@ export const websiteTranslations = {
|
|
|
457
457
|
"btns.backup": "Restaurer",
|
|
458
458
|
"backup.restore.title": "Envoi de votre lien de restauration",
|
|
459
459
|
"backup.restore.confirm": "Un lien pour restaurer votre sauvegarde vous sera envoyé à votre adresse e-mail. Ce lien expirera dans 30 minutes. Voulez-vous vraiment continuer?",
|
|
460
|
-
"
|
|
461
|
-
"
|
|
460
|
+
"email_backup_restoreRequest_subject": "Votre demande de restauration de sauvegarde",
|
|
461
|
+
"email_backup_restoreRequest_content": `<p>Vous avez demandé une restauration de sauvegarde. Veuillez choisir une option :</p>
|
|
462
462
|
<p><strong>ATTENTION : Toute restauration écrasera vos données actuelles (modèles et/ou données).</strong></p>
|
|
463
463
|
<ul>
|
|
464
464
|
<li><a href="https://{{host}}/backup/restore?token={{modelsToken}}&username={{user}}&type=full">Restauration Complète (Modèles ET Données)</a></li>
|
|
@@ -1570,8 +1570,8 @@ export const websiteTranslations = {
|
|
|
1570
1570
|
"backup.restore.title": "Sending your restore link",
|
|
1571
1571
|
"btns.backup": "Restore",
|
|
1572
1572
|
"backup.restore.confirm": "A link to restore your backup will be sent to your email address. This link will expire in 30 minutes. Are you sure you want to proceed?",
|
|
1573
|
-
"
|
|
1574
|
-
"
|
|
1573
|
+
"email_backup_restoreRequest_subject": "Your Backup restoration request",
|
|
1574
|
+
"email_backup_restoreRequest_content": `<p>You have requested a backup restoration. Please choose an option:</p>
|
|
1575
1575
|
<p><strong>WARNING: Any restoration will overwrite your current data (models and/or data).</strong></p>
|
|
1576
1576
|
<ul>
|
|
1577
1577
|
<li><a href="https://{{host}}/backup/restore?token={{fullToken}}&username={{user}}&type=full">Full Restoration (Models AND Data)</a></li>
|
|
@@ -3046,8 +3046,8 @@ export const websiteTranslations = {
|
|
|
3046
3046
|
"btns.cancel": "Cancelar",
|
|
3047
3047
|
"btns.backup": "Restaurar",
|
|
3048
3048
|
"backup.restore.confirm": "Se enviará un enlace para restaurar su copia de seguridad a su correo electrónico. Este enlace caducará en 30 minutos. ¿Está seguro de que desea continuar?",
|
|
3049
|
-
"
|
|
3050
|
-
"
|
|
3049
|
+
"email_backup_restoreRequest_subject": "Su solicitud de restauración de copia de seguridad",
|
|
3050
|
+
"email_backup_restoreRequest_content": `<p>Has solicitado una restauración de copia de seguridad. Por favor, elige una opción:</p>
|
|
3051
3051
|
<p><strong>ADVERTENCIA: Cualquier restauración sobrescribirá tus datos actuales (modelos y/o datos).</strong></p>
|
|
3052
3052
|
<ul>
|
|
3053
3053
|
<li><a href="https://{{host}}/backup/restore?token={{fullToken}}&username={{user}}&type=full">Restauración completa (Modelos Y Datos)</a></li>
|
|
@@ -4528,8 +4528,8 @@ export const websiteTranslations = {
|
|
|
4528
4528
|
"btns.cancel": "Cancelar",
|
|
4529
4529
|
"btns.backup": "Restaurar",
|
|
4530
4530
|
"backup.restore.confirm": "Será enviado um link para restaurar o seu backup para o seu endereço de e-mail. Este link expirará em 30 minutos. Tem a certeza de que pretende prosseguir?",
|
|
4531
|
-
"
|
|
4532
|
-
"
|
|
4531
|
+
"email_backup_restoreRequest_subject": "O seu pedido de restauro de cópia de segurança",
|
|
4532
|
+
"email_backup_restoreRequest_content": `<p>Solicitou a restauração de uma cópia de segurança. Por favor, escolha uma opção:</p>
|
|
4533
4533
|
<p><strong>ATENÇÃO: Qualquer restauração irá substituir os seus dados atuais (modelos e/ou dados).</strong></p>
|
|
4534
4534
|
<ul>
|
|
4535
4535
|
<li><a href="https://{{host}}/backup/restore?token={{fullToken}}&username={{user}}&type=full">Restauração Completa (Modelos E Dados)</a></li>
|
|
@@ -5984,8 +5984,8 @@ export const websiteTranslations = {
|
|
|
5984
5984
|
"btns.cancel": "Abbrechen",
|
|
5985
5985
|
"btns.backup": "Wiederherstellen",
|
|
5986
5986
|
"backup.restore.confirm": "Ein Link zur Wiederherstellung Ihres Backups wird an Ihre E-Mail-Adresse gesendet. Dieser Link läuft in 30 Minuten ab. Möchten Sie wirklich fortfahren?",
|
|
5987
|
-
"
|
|
5988
|
-
"
|
|
5987
|
+
"email_backup_restoreRequest_subject": "Ihre Anfrage zur Wiederherstellung des Backups",
|
|
5988
|
+
"email_backup_restoreRequest_content": `<p>Sie haben die Wiederherstellung eines Backups angefordert. Bitte wählen Sie eine Option:</p>
|
|
5989
5989
|
<p><strong>ACHTUNG: Jede Wiederherstellung überschreibt Ihre aktuellen Daten (Modelle und/oder Daten).</strong></p>
|
|
5990
5990
|
<ul>
|
|
5991
5991
|
<li><a href="https://{{host}}/backup/restore?token={{fullToken}}&username={{user}}&type=full">Vollständige Wiederherstellung (Modelle UND Daten)</a></li>
|
|
@@ -7463,8 +7463,8 @@ export const websiteTranslations = {
|
|
|
7463
7463
|
"btns.cancel": "Annulla",
|
|
7464
7464
|
"btns.backup": "Ripristina",
|
|
7465
7465
|
"backup.restore.confirm": "Un link per ripristinare il backup verrà inviato al tuo indirizzo email. Questo link scadrà tra 30 minuti. Vuoi procedere?",
|
|
7466
|
-
"
|
|
7467
|
-
"
|
|
7466
|
+
"email_backup_restoreRequest_subject": "La tua richiesta di ripristino del backup",
|
|
7467
|
+
"email_backup_restoreRequest_content": `<p>Hai richiesto il ripristino di un backup. Scegli un'opzione:</p>
|
|
7468
7468
|
<p><strong>ATTENZIONE: Qualsiasi ripristino sovrascriverà i tuoi dati attuali (modelli e/o dati).</strong></p>
|
|
7469
7469
|
<ul>
|
|
7470
7470
|
<li><a href="https://{{host}}/backup/restore?token={{fullToken}}&username={{user}}&type=full">Ripristino Completo (Modelli E Dati)</a></li>
|
|
@@ -8937,8 +8937,8 @@ export const websiteTranslations = {
|
|
|
8937
8937
|
"btns.cancel": "Zrušit",
|
|
8938
8938
|
"btns.backup": "Obnovit",
|
|
8939
8939
|
"backup.restore.confirm": "Na vaši e-mailovou adresu bude odeslán odkaz pro obnovení zálohy. Platnost tohoto odkazu vyprší za 30 minut. Opravdu chcete pokračovat?",
|
|
8940
|
-
"
|
|
8941
|
-
"
|
|
8940
|
+
"email_backup_restoreRequest_subject": "Váš požadavek na obnovení zálohy",
|
|
8941
|
+
"email_backup_restoreRequest_content": `<p>Požádali jste o obnovení ze zálohy. Vyberte prosím možnost:</p>
|
|
8942
8942
|
<p><strong>VAROVÁNÍ: Jakákoli obnova přepíše vaše aktuální data (modely a/nebo data).</strong></p>
|
|
8943
8943
|
<ul>
|
|
8944
8944
|
<li><a href="https://{{host}}/backup/restore?token={{fullToken}}&username={{user}}&type=full">Kompletní obnova (Modely I Data)</a></li>
|
|
@@ -10409,8 +10409,8 @@ export const websiteTranslations = {
|
|
|
10409
10409
|
"btns.cancel": "Отмена",
|
|
10410
10410
|
"btns.backup": "Восстановить",
|
|
10411
10411
|
"backup.restore.confirm": "Ссылка для восстановления резервной копии будет отправлена на ваш адрес электронной почты. Срок действия этой ссылки истекает через 30 минут. Вы уверены, что хотите продолжить?",
|
|
10412
|
-
"
|
|
10413
|
-
"
|
|
10412
|
+
"email_backup_restoreRequest_subject": "Ваш запрос на восстановление резервной копии",
|
|
10413
|
+
"email_backup_restoreRequest_content": `<p>Вы запросили восстановление из резервной копии. Пожалуйста, выберите вариант:</p>
|
|
10414
10414
|
<p><strong>ВНИМАНИЕ: Любое восстановление перезапишет ваши текущие данные (модели и/или данные).</strong></p>
|
|
10415
10415
|
<ul>
|
|
10416
10416
|
<li><a href="https://{{host}}/backup/restore?token={{fullToken}}&username={{user}}&type=full">Полное восстановление (Модели И Данные)</a></li>
|
|
@@ -11900,8 +11900,8 @@ export const websiteTranslations = {
|
|
|
11900
11900
|
"btns.cancel": "إلغاء",
|
|
11901
11901
|
"btns.backup": "استعادة",
|
|
11902
11902
|
"backup.restore.confirm": "سيتم إرسال رابط لاستعادة النسخة الاحتياطية إلى بريدك الإلكتروني. سينتهي هذا الرابط خلال 30 دقيقة. هل أنت متأكد من رغبتك في المتابعة؟",
|
|
11903
|
-
"
|
|
11904
|
-
"
|
|
11903
|
+
"email_backup_restoreRequest_subject": "طلب استعادة النسخة الاحتياطية الخاص بك",
|
|
11904
|
+
"email_backup_restoreRequest_content": `<p>لقد طلبت استعادة نسخة احتياطية. يرجى اختيار خيار:</p>
|
|
11905
11905
|
<p><strong>تحذير: أي عملية استعادة ستستبدل بياناتك الحالية (النماذج و/أو البيانات).</strong></p>
|
|
11906
11906
|
<ul>
|
|
11907
11907
|
<li><a href="https://{{host}}/backup/restore?token={{fullToken}}&username={{user}}&type=full">استعادة كاملة (النماذج والبيانات)</a></li>
|
|
@@ -13380,8 +13380,8 @@ export const websiteTranslations = {
|
|
|
13380
13380
|
"btns.cancel": "Avbryt",
|
|
13381
13381
|
"btns.backup": "Återställ",
|
|
13382
13382
|
"backup.restore.confirm": "En länk för att återställa din säkerhetskopia kommer att skickas till din e-postadress. Den här länken upphör att gälla om 30 minuter. Är du säker på att du vill fortsätta?",
|
|
13383
|
-
"
|
|
13384
|
-
"
|
|
13383
|
+
"email_backup_restoreRequest_subject": "Din begäran om återställning av säkerhetskopia",
|
|
13384
|
+
"email_backup_restoreRequest_content": `<p>Du har begärt en återställning från backup. Vänligen välj ett alternativ:</p>
|
|
13385
13385
|
<p><strong>OBS: All återställning kommer att skriva över din nuvarande data (modeller och/eller data).</strong></p>
|
|
13386
13386
|
<ul>
|
|
13387
13387
|
<li><a href="https://{{host}}/backup/restore?token={{fullToken}}&username={{user}}&type=full">Fullständig återställning (Modeller OCH Data)</a></li>
|
|
@@ -14854,8 +14854,8 @@ export const websiteTranslations = {
|
|
|
14854
14854
|
"btns.cancel": "Ακύρωση",
|
|
14855
14855
|
"btns.backup": "Επαναφορά",
|
|
14856
14856
|
"backup.restore.confirm": "Ένας σύνδεσμος για την επαναφορά του αντιγράφου ασφαλείας θα σταλεί στη διεύθυνση email σας. Αυτός ο σύνδεσμος θα λήξει σε 30 λεπτά. Είστε βέβαιοι ότι θέλετε να συνεχίσετε;",
|
|
14857
|
-
"
|
|
14858
|
-
"
|
|
14857
|
+
"email_backup_restoreRequest_subject": "Το αίτημα επαναφοράς αντιγράφων ασφαλείας",
|
|
14858
|
+
"email_backup_restoreRequest_content": `<p>Έχετε αιτηθεί επαναφορά από αντίγραφο ασφαλείας. Παρακαλώ επιλέξτε μια επιλογή:</p>
|
|
14859
14859
|
<p><strong>ΠΡΟΣΟΧΗ: Οποιαδήποτε επαναφορά θα αντικαταστήσει τα τρέχοντα δεδομένα σας (μοντέλα και/ή δεδομένα).</strong></p>
|
|
14860
14860
|
<ul>
|
|
14861
14861
|
<li><a href="https://{{host}}/backup/restore?token={{fullToken}}&username={{user}}&type=full">Πλήρης Επαναφορά (Μοντέλα ΚΑΙ Δεδομένα)</a></li>
|
|
@@ -16283,8 +16283,8 @@ export const websiteTranslations = {
|
|
|
16283
16283
|
"btns.backup": "بازیابی",
|
|
16284
16284
|
"backup.restore.title": "ارسال لینک بازیابی شما",
|
|
16285
16285
|
"backup.restore.confirm": "لینکی برای بازیابی پشتیبانگیری شما به آدرس ایمیل شما ارسال خواهد شد. این لینک پس از 30 دقیقه منقضی میشود. آیا واقعاً میخواهید ادامه دهید؟",
|
|
16286
|
-
"
|
|
16287
|
-
"
|
|
16286
|
+
"email_backup_restoreRequest_subject": "درخواست بازیابی پشتیبانگیری شما",
|
|
16287
|
+
"email_backup_restoreRequest_content": `<p>درخواست بازیابی نسخه پشتیبان را دادهاید. لطفاً یک گزینه انتخاب کنید:</p>
|
|
16288
16288
|
<p><strong>هشدار: هرگونه بازیابی، دادههای فعلی شما (مدلها و/یا دادهها) را بازنویسی خواهد کرد.</strong></p>
|
|
16289
16289
|
<ul>
|
|
16290
16290
|
<li><a href="https://{{host}}/backup/restore?token={{fullToken}}&username={{user}}&type=full">بازیابی کامل (هم مدلها و هم دادهها)</a></li>
|
package/doc/AI-assistance.md
CHANGED
|
@@ -1,93 +1,117 @@
|
|
|
1
1
|
# AI Assistance: Leverage Artificial Intelligence
|
|
2
2
|
|
|
3
|
-
`data-primals-engine`
|
|
3
|
+
The `data-primals-engine` integrates powerful generative AI capabilities directly into its core, allowing you to automate content creation, analyze data, and enrich your business processes.
|
|
4
4
|
|
|
5
5
|
## Enabling the AI Assistant
|
|
6
6
|
|
|
7
|
-
To use the AI
|
|
7
|
+
To use the AI features, you must first provide API keys for one or more supported AI providers. The engine natively supports providers like **OpenAI, Google (Gemini), DeepSeek, and Anthropic**.
|
|
8
8
|
|
|
9
9
|
You can configure your keys in two ways:
|
|
10
10
|
|
|
11
11
|
1. **Environment Variables**: Set the appropriate variable in your `.env` file. This is the recommended approach for production.
|
|
12
|
-
```env
|
|
13
|
-
# Choose one or more providers
|
|
14
|
-
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxx
|
|
15
|
-
GOOGLE_API_KEY=AIzaSyxxxxxxxxxxxxxxxxxxxx
|
|
16
|
-
DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxxxxxx
|
|
17
|
-
```
|
|
18
12
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
### 1. Data Querying and Visualization
|
|
28
|
-
|
|
29
|
-
You can ask the assistant to find, filter, and display your data. It can present the results in various formats.
|
|
30
|
-
|
|
31
|
-
- **Simple Search**:
|
|
32
|
-
> "Show me the latest 5 orders from French customers."
|
|
33
|
-
|
|
34
|
-
- **Chart Generation**: Create charts on the fly.
|
|
35
|
-
> "Create a bar chart of user sign-ups per month for the last year."
|
|
36
|
-
|
|
37
|
-
- **Custom HTML Views**: Generate sophisticated, styled views of your data using Handlebars templates.
|
|
38
|
-
> "Build a dashboard of my active projects with their status and due dates, using a modern dark theme."
|
|
39
|
-
|
|
40
|
-
### 2. Data Modification (with Confirmation)
|
|
13
|
+
```env
|
|
14
|
+
# Choose one or more providers
|
|
15
|
+
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxx
|
|
16
|
+
GOOGLE_API_KEY=AIzaSyxxxxxxxxxxxxxxxxxxxx
|
|
17
|
+
DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxxxxxx
|
|
18
|
+
ANTHROPIC_API_KEY=sk-xxxxxxxxxxxxxxxxxxxx
|
|
19
|
+
```
|
|
41
20
|
|
|
42
|
-
|
|
21
|
+
2. **`env` Model**: For user-specific keys, you can store them in the `env` model within the application. The assistant will automatically look for a key belonging to the current user.
|
|
43
22
|
|
|
44
|
-
|
|
45
|
-
> "Create a new task to 'Follow up with Client X' due tomorrow."
|
|
23
|
+
Once a key is configured, AI capabilities become available, most notably within the automation engine.
|
|
46
24
|
|
|
47
|
-
|
|
48
|
-
> "Update the status of all tickets in the 'Support' category to 'resolved'."
|
|
25
|
+
## AI in Workflows: The `GenerateAIContent` Action
|
|
49
26
|
|
|
50
|
-
-
|
|
51
|
-
> "Delete all draft products created before last month."
|
|
27
|
+
The most powerful way to use AI in `data-primals-engine` is by integrating it into your **Workflows**. The `GenerateAIContent` action type allows you to call an AI model as a step in any automated process.
|
|
52
28
|
|
|
53
|
-
|
|
29
|
+
This is perfect for tasks like:
|
|
30
|
+
- Summarizing a new support ticket.
|
|
31
|
+
- Generating a product description when a new product is added.
|
|
32
|
+
- Classifying incoming data based on its content.
|
|
33
|
+
- Translating text.
|
|
54
34
|
|
|
55
|
-
###
|
|
35
|
+
### Example: Automatically Generating a Product Description
|
|
56
36
|
|
|
57
|
-
|
|
37
|
+
Let's create a complete workflow that automatically generates an SEO description whenever a new product is added without one.
|
|
58
38
|
|
|
59
|
-
|
|
39
|
+
This workflow can be created programmatically by making the following API calls from your JavaScript code.
|
|
60
40
|
|
|
61
|
-
## How It Works: The Reasoning Loop
|
|
62
41
|
|
|
63
|
-
|
|
42
|
+
#### 1. The `workflow`
|
|
64
43
|
|
|
65
|
-
|
|
66
|
-
2. **Search Models (Internal Step)**: Its first action is always to call the `search_models` tool internally. This gives it the exact structure, field names, and types for the relevant data models. This step is crucial for preventing errors and "hallucinations."
|
|
67
|
-
3. **Formulate Action**: Based on your intent and the model structures it found, it formulates a final action, such as `search`, `generateChart`, or a `post` request.
|
|
68
|
-
4. **Execute or Confirm**:
|
|
69
|
-
- If it's a read-only action (like `search` or `generateChart`), it executes it and displays the result.
|
|
70
|
-
- If it's a write action (like `post` or `delete`), it presents the action to you for confirmation.
|
|
44
|
+
First, we define the main workflow. This document acts as a container for the steps and logic.
|
|
71
45
|
|
|
72
|
-
|
|
46
|
+
```javascript
|
|
47
|
+
await insertData("workflow", {
|
|
48
|
+
"name": "Generate Product SEO Description",
|
|
49
|
+
"description": "When a new product is created without an SEO description, this workflow uses AI to generate one based on the product's name.",
|
|
50
|
+
"startStep": { "$find": { "name": "Generate and Update Description" } }
|
|
51
|
+
});
|
|
52
|
+
```
|
|
73
53
|
|
|
74
|
-
|
|
54
|
+
#### 2. The `workflowAction` documents
|
|
75
55
|
|
|
76
|
-
|
|
77
|
-
- Summarizing a new support ticket.
|
|
78
|
-
- Generating a product description when a new product is added.
|
|
79
|
-
- Classifying incoming data based on its content.
|
|
80
|
-
- Translating text.
|
|
56
|
+
Next, we define the individual actions that will be performed. We need two actions: one to generate the content with AI, and another to update the product with that content. We use `insertData` to create both in a single call.
|
|
81
57
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
{
|
|
85
|
-
"name": "
|
|
58
|
+
```javascript
|
|
59
|
+
await insertData("workflowAction", [
|
|
60
|
+
{
|
|
61
|
+
"name": "Generate SEO Content",
|
|
86
62
|
"type": "GenerateAIContent",
|
|
87
63
|
"aiProvider": "OpenAI",
|
|
88
|
-
"aiModel": "gpt-
|
|
89
|
-
"prompt": "
|
|
90
|
-
}
|
|
64
|
+
"aiModel": "gpt-4o-mini",
|
|
65
|
+
"prompt": "Write a short, engaging, and SEO-optimized product description (30-40 words) for the following product: '{triggerData.name}'. Return only the description text, without any introductory or concluding phrases."
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"name": "Update Product with AI Content",
|
|
69
|
+
"type": "UpdateData",
|
|
70
|
+
"targetModel": "product",
|
|
71
|
+
"targetSelector": { "_id": "{triggerData._id}" },
|
|
72
|
+
"fieldsToUpdate": { "seoDescription": "{context.aiContent}" }
|
|
73
|
+
}
|
|
74
|
+
]);
|
|
75
|
+
```
|
|
76
|
+
> **Note**: The `targetSelector` and `fieldsToUpdate` use placeholders. `{triggerData._id}` refers to the ID of the product that started the workflow, and `{context.aiContent}` refers to the text generated by the previous AI action.
|
|
77
|
+
|
|
78
|
+
#### 3. The `workflowStep`
|
|
79
|
+
|
|
80
|
+
Now, we create a step that groups our actions together. A workflow can have multiple steps that execute in sequence. For this simple case, we only need one, which we create with `insertData`.
|
|
81
|
+
|
|
82
|
+
```javascript
|
|
83
|
+
await insertData("workflowStep", {
|
|
84
|
+
"name": "Generate and Update Description",
|
|
85
|
+
"workflow": { "$find": { "name": "Generate Product SEO Description" } },
|
|
86
|
+
"actions": [
|
|
87
|
+
{ "$find": { "name": "Generate SEO Content" } },
|
|
88
|
+
{ "$find": { "name": "Update Product with AI Content" } }
|
|
89
|
+
],
|
|
90
|
+
"isTerminal": true
|
|
91
|
+
});
|
|
91
92
|
```
|
|
92
93
|
|
|
94
|
+
#### 4. The `workflowTrigger`
|
|
95
|
+
|
|
96
|
+
Finally, we define the trigger that will start the workflow. This trigger will listen for new documents being added (`DataAdded`) to the `product` model. It also includes a `dataFilter` to ensure it only runs if the `seoDescription` field is empty. We create it with `insertData`.
|
|
97
|
+
|
|
98
|
+
```javascript
|
|
99
|
+
await insertData("workflowTrigger", {
|
|
100
|
+
"name": "On New Product Added without SEO Description",
|
|
101
|
+
"workflow": { "$find": { "name": "Generate Product SEO Description" } },
|
|
102
|
+
"type": "manual",
|
|
103
|
+
"onEvent": "DataAdded",
|
|
104
|
+
"targetModel": "product",
|
|
105
|
+
"dataFilter": {
|
|
106
|
+
"$or": [
|
|
107
|
+
{ "seoDescription": { "$exists": false } },
|
|
108
|
+
{ "seoDescription": "" }
|
|
109
|
+
]
|
|
110
|
+
},
|
|
111
|
+
"isActive": true
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
With these four API calls, your automated AI content generation workflow is now active. Any new product created without an SEO description will automatically have one generated and saved.
|
|
116
|
+
|
|
93
117
|
This integration of AI both as an interactive assistant and as an automation component makes `data-primals-engine` a powerful platform for building intelligent applications.
|