data-primals-engine 1.7.1 → 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.
Files changed (99) hide show
  1. package/README.md +9 -9
  2. package/client/package-lock.json +8430 -8121
  3. package/client/package.json +7 -4
  4. package/client/src/APIInfo.jsx +1 -1
  5. package/client/src/App.jsx +2 -1
  6. package/client/src/App.scss +1635 -1626
  7. package/client/src/AssistantChat.jsx +2 -3
  8. package/client/src/CalendarView.jsx +1 -0
  9. package/client/src/ContentView.jsx +3 -3
  10. package/client/src/Dashboard.jsx +5 -2
  11. package/client/src/DashboardChart.jsx +1 -0
  12. package/client/src/DashboardFlexViewItem.jsx +1 -0
  13. package/client/src/DashboardHtmlViewItem.jsx +1 -0
  14. package/client/src/DashboardView.jsx +2 -0
  15. package/client/src/DataEditor.jsx +0 -1
  16. package/client/src/DataImporter.jsx +489 -468
  17. package/client/src/DataLayout.jsx +25 -23
  18. package/client/src/DataTable.jsx +6 -5
  19. package/client/src/Dialog.jsx +92 -90
  20. package/client/src/Dialog.scss +122 -116
  21. package/client/src/DisplayFlexNodeRenderer.jsx +1 -0
  22. package/client/src/DocumentationPageLayout.scss +1 -1
  23. package/client/src/FlexBuilderControls.jsx +1 -0
  24. package/client/src/FlexBuilderPreview.jsx +1 -1
  25. package/client/src/FlexNode.jsx +1 -1
  26. package/client/src/HistoryDialog.jsx +3 -2
  27. package/client/src/KPIWidget.jsx +1 -1
  28. package/client/src/KanbanView.jsx +1 -0
  29. package/client/src/ModelCreator.jsx +4 -0
  30. package/client/src/ModelList.jsx +1 -0
  31. package/client/src/PackGallery.jsx +5 -4
  32. package/client/src/RTETrans.jsx +1 -1
  33. package/client/src/RelationField.jsx +2 -0
  34. package/client/src/RelationSelectorWidget.jsx +2 -0
  35. package/client/src/RelationValue.jsx +1 -0
  36. package/client/src/RestoreDialog.jsx +1 -0
  37. package/client/src/ViewSwitcher.jsx +1 -1
  38. package/client/src/WorkflowEditor.jsx +3 -0
  39. package/client/src/contexts/CommandContext.jsx +2 -1
  40. package/client/src/contexts/ModelContext.jsx +3 -3
  41. package/client/src/hooks/data.js +1 -0
  42. package/client/src/hooks/useValidation.js +1 -0
  43. package/client/src/translations.js +24 -24
  44. package/client/vite.config.js +31 -30
  45. package/doc/AI-assistance.md +87 -63
  46. package/doc/Concepts.md +122 -0
  47. package/doc/Custom-Endpoints.md +31 -0
  48. package/doc/Event-system.md +13 -14
  49. package/doc/Home.md +33 -0
  50. package/doc/Modules.md +83 -0
  51. package/doc/Packs-gallery.md +8 -23
  52. package/doc/Workflows.md +32 -0
  53. package/doc/automation-workflows.md +141 -102
  54. package/doc/dashboards-kpis-charts.md +47 -49
  55. package/doc/data-management.md +126 -120
  56. package/doc/data-models.md +68 -75
  57. package/doc/roles-permissions.md +144 -43
  58. package/doc/sharding-replication.md +158 -0
  59. package/doc/users.md +54 -30
  60. package/package.json +27 -17
  61. package/server.js +37 -37
  62. package/src/ai.jobs.js +135 -0
  63. package/src/constants.js +560 -545
  64. package/src/data.js +521 -518
  65. package/src/email.js +157 -154
  66. package/src/engine.js +167 -49
  67. package/src/gameObject.js +6 -0
  68. package/src/i18n.js +0 -1
  69. package/src/modules/assistant/assistant.js +782 -763
  70. package/src/modules/assistant/constants.js +23 -16
  71. package/src/modules/assistant/providers.js +77 -37
  72. package/src/modules/auth-google/index.js +53 -50
  73. package/src/modules/bucket.js +346 -335
  74. package/src/modules/data/data.backup.js +400 -376
  75. package/src/modules/data/data.cluster.js +559 -0
  76. package/src/modules/data/data.core.js +11 -8
  77. package/src/modules/data/data.js +311 -311
  78. package/src/modules/data/data.operations.js +3666 -3555
  79. package/src/modules/data/data.relations.js +1 -0
  80. package/src/modules/data/data.replication.js +125 -0
  81. package/src/modules/data/data.routes.js +2206 -1879
  82. package/src/modules/data/data.scheduling.js +2 -1
  83. package/src/modules/file.js +248 -247
  84. package/src/modules/mongodb.js +76 -73
  85. package/src/modules/user.js +18 -2
  86. package/src/modules/worker-script-runner.js +97 -0
  87. package/src/modules/workflow.js +177 -52
  88. package/src/packs.js +5701 -5701
  89. package/src/providers.js +298 -297
  90. package/src/sso.js +91 -25
  91. package/test/assistant.test.js +207 -206
  92. package/test/cluster.test.js +221 -0
  93. package/test/core.test.js +0 -2
  94. package/test/data.integration.test.js +1425 -1416
  95. package/test/import_export.integration.test.js +210 -210
  96. package/test/replication.test.js +163 -0
  97. package/test/workflow.actions.integration.test.js +487 -475
  98. package/test/workflow.integration.test.js +332 -329
  99. package/doc/core-concepts.md +0 -33
@@ -28,7 +28,7 @@ const FlexBuilderPreview = ({
28
28
  setIsModalOpen(true);
29
29
 
30
30
  try {
31
- const response = await fetch(`/api/actions/${node.endpointPath}`, { method: 'GET' });
31
+ const response = await fetch(`/api/actions/${node.endpointPath}`, {credentials: "include", method: 'GET' });
32
32
  const data = await response.json();
33
33
  setModalContent({ path: node.endpointPath, status: response.status, data });
34
34
  } catch (error) {
@@ -92,7 +92,7 @@ export const FlexNodeRenderer = React.forwardRef((props, ref) => {
92
92
  setResult(null);
93
93
 
94
94
  try {
95
- const response = await fetch(`/api/actions/${endpointPath}`, { method: 'GET' });
95
+ const response = await fetch(`/api/actions/${endpointPath}`, { credentials: "include",method: 'GET' });
96
96
  const data = await response.json();
97
97
  setResult({ status: response.status, data });
98
98
  } catch (error) {
@@ -153,7 +153,7 @@ export const HistoryDialog = ({ modelName, recordId, onClose }) => {
153
153
  const params = new URLSearchParams({ page, limit: elementsPerPage, lang: i18n.language });
154
154
  if (startDate) params.append('startDate', startDate);
155
155
  if (endDate) params.append('endDate', endDate);
156
- const query = await fetch(`/api/data/history/${modelName}/${recordId}?${params.toString()}`);
156
+ const query = await fetch(`/api/data/history/${modelName}/${recordId}?${params.toString()}`, {credentials: "include"});
157
157
  const response = await query.json();
158
158
  if (response.success) {
159
159
  setHistory(response.data);
@@ -185,7 +185,7 @@ export const HistoryDialog = ({ modelName, recordId, onClose }) => {
185
185
  setPreviewLoading(true);
186
186
  setPreviewData(null);
187
187
  try {
188
- const res = await fetch(`/api/data/history/${modelName}/${recordId}/${version}`);
188
+ const res = await fetch(`/api/data/history/${modelName}/${recordId}/${version}`, {credentials: "include"});
189
189
  const response = await res.json();
190
190
  if (response.success) {
191
191
  setPreviewData(response.data);
@@ -209,6 +209,7 @@ export const HistoryDialog = ({ modelName, recordId, onClose }) => {
209
209
  try {
210
210
  const res = await fetch(`/api/data/history/${modelName}/${recordId}/revert/${version}`, {
211
211
  method: 'POST',
212
+ credentials: "include",
212
213
  headers: { 'Content-Type': 'application/json' }
213
214
  });
214
215
  const response = await res.json();
@@ -9,7 +9,7 @@ import { Tooltip } from 'react-tooltip';
9
9
  // Fonction pour récupérer la valeur calculée d'un KPI (MODIFIÉE)
10
10
  const fetchKpiValue = async (kpiId) => {
11
11
  if (!kpiId) return null;
12
- const response = await fetch(`/api/kpis/calculate/${kpiId}`);
12
+ const response = await fetch(`/api/kpis/calculate/${kpiId}`, { credentials: "include",});
13
13
  if (!response.ok) {
14
14
  const errorData = await response.json().catch(() => ({}));
15
15
  throw new Error(errorData.error || `Network response was not ok (${response.status})`);
@@ -62,6 +62,7 @@ const KanbanView = ({ settings, model }) => {
62
62
  ['kanbanData', model.name], // Clé de requête unique pour ce modèle
63
63
  () => fetch(`/api/data/search?_user=${me.username}&depth=2`, { // La fonction qui appelle l'API
64
64
  method: 'POST',
65
+ credentials: "include",
65
66
  headers: { 'Content-Type': 'application/json' },
66
67
  // On demande toutes les données pour le modèle spécifié (limit: 0 pour "tout")
67
68
  body: JSON.stringify({ model: model.name, page: 1 })
@@ -94,6 +94,7 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
94
94
  const url = `/api/model/${initialModel._id}/renameField?_user=${encodeURIComponent(getUserId(me))}`;
95
95
  return fetch(url, {
96
96
  method: 'PATCH',
97
+ credentials: "include",
97
98
  headers: { 'Content-Type': 'application/json' },
98
99
  body: JSON.stringify({ oldFieldName: oldName, newFieldName: newName }),
99
100
  }).then((res) => res.json());
@@ -120,6 +121,7 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
120
121
  const method = initialModel?._id ? 'PUT' : 'POST';
121
122
  return fetch(url+'?_user='+me.username, {
122
123
  method: method,
124
+ credentials: "include",
123
125
  headers: { 'Content-Type': 'application/json' },
124
126
  body: JSON.stringify(modelData),
125
127
  }).then((res) => res.json());
@@ -173,6 +175,7 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
173
175
  (modelName) =>
174
176
  fetch(`/api/model?name=${modelName}&_user=${encodeURIComponent(getUserId(me))}`, {
175
177
  method: 'DELETE',
178
+ credentials:"include",
176
179
  }).then((res) => res.json()),
177
180
  {
178
181
  onSuccess: () => {
@@ -386,6 +389,7 @@ const ModelCreator = forwardRef(({ initialPrompt = '', onModelGenerated, autoGen
386
389
 
387
390
  const response = await fetch(`/api/model/generate?lang=${lang}`, {
388
391
  method: 'POST',
392
+ credentials:"include",
389
393
  headers: { 'Content-Type': 'application/json' },
390
394
  body: JSON.stringify(bodyPayload),
391
395
  });
@@ -95,6 +95,7 @@ export function ModelList({ editionMode, onModelSelect, onCreateModel, onImportM
95
95
  const demoInitMutation = useMutation((profile) => {
96
96
  return fetch('/api/demo/initialize', {
97
97
  method: 'POST',
98
+ credentials:"include",
98
99
  headers: { "Content-Type": "application/json"},
99
100
  body: JSON.stringify({profile: profile, packs: profiles[profile].packs}),
100
101
  });
@@ -16,7 +16,7 @@ import {TextField, CheckboxField} from "./Field.jsx";
16
16
  // --- API Fetching Functions ---
17
17
  const fetchPacks = async (sortBy, lang, filterByUser = false, user) => {
18
18
  const url = `/api/packs?lang=${lang}&sortBy=${sortBy.field}&order=${sortBy.order}${filterByUser ? '&user='+user.username : ''}`;
19
- const response = await fetch(url);
19
+ const response = await fetch(url, {credentials:"include"});
20
20
  if (!response.ok) {
21
21
  throw new Error('Network response was not ok');
22
22
  }
@@ -25,7 +25,7 @@ const fetchPacks = async (sortBy, lang, filterByUser = false, user) => {
25
25
 
26
26
  const fetchPackDetails = async (packId, lang) => {
27
27
  if (!packId) return null;
28
- const response = await fetch(`/api/packs/${packId}?lang=${lang}`);
28
+ const response = await fetch(`/api/packs/${packId}?lang=${lang}`, {credentials:"include"});
29
29
  if (!response.ok) {
30
30
  throw new Error('Failed to fetch pack details');
31
31
  }
@@ -182,7 +182,7 @@ const PackDetail = ({ packId, onBack }) => {
182
182
 
183
183
  const installPackMutationFn = async ({packId, lang}) => {
184
184
  const response = await fetch(`/api/packs/${packId}/install?lang=${lang}`, {
185
- method: 'POST',
185
+ method: 'POST',credentials: "include",
186
186
  headers: {
187
187
  'Content-Type': 'application/json',
188
188
  },
@@ -196,7 +196,7 @@ const installPackMutationFn = async ({packId, lang}) => {
196
196
 
197
197
  const updatePackMutationFn = async ({ packId, updateData }) => {
198
198
  const response = await fetch(`/api/packs/${packId}`, {
199
- method: 'PATCH',
199
+ method: 'PATCH',credentials: "include",
200
200
  headers: {
201
201
  'Content-Type': 'application/json',
202
202
  },
@@ -241,6 +241,7 @@ const PackGallery = () => {
241
241
 
242
242
  const response = await fetch('/api/packs/install', {
243
243
  method: 'POST',
244
+ credentials: "include",
244
245
  headers: {
245
246
  'Content-Type': 'application/json',
246
247
  },
@@ -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', body: JSON.stringify({}), headers: { "Content-Type": "application/json"}})
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
  });
@@ -44,6 +44,7 @@ const RelationValue = ({ field, data, align }) => {
44
44
 
45
45
  return fetch(`/api/data/search?${params.toString()}`, {
46
46
  method: 'POST',
47
+ credentials:"include",
47
48
  headers: { 'Content-Type': 'application/json' },
48
49
  })
49
50
  .then(res => res.json())
@@ -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)
@@ -17,7 +17,7 @@ const ViewSwitcher = ({ currentView, onViewChange, configuredViews, onConfigureV
17
17
  const { t } = useTranslation();
18
18
 
19
19
  return (
20
- <div className="view-switcher flex items-center gap-1 p-1 bg-gray-200 rounded-md">
20
+ <div className="view-switcher flex items-center gap-1 p-1 rounded-md">
21
21
  {viewOptions.map(view => {
22
22
  const isConfigured = view.id === 'table' || configuredViews[view.id];
23
23
  const isActive = currentView === view.id;
@@ -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))}`).then((res) => {
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 {
@@ -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
  )
@@ -13,6 +13,7 @@ import { useState, useCallback, useMemo } from 'react';
13
13
  */
14
14
  const validateFieldOnServer = async (api, payload) => {
15
15
  return fetch('/api/data/validate', { method: 'POST',
16
+ credentials:"include",
16
17
  headers: {
17
18
  'Content-Type': 'application/json'
18
19
  }
@@ -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
- "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>
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
- "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>
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
- "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>
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
- "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>
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
- "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>
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
- "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>
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
- "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>
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
- "email.backup.restoreRequest.subject": "Ваш запрос на восстановление резервной копии",
10413
- "email.backup.restoreRequest.content": `<p>Вы запросили восстановление из резервной копии. Пожалуйста, выберите вариант:</p>
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
- "email.backup.restoreRequest.subject": "طلب استعادة النسخة الاحتياطية الخاص بك",
11904
- "email.backup.restoreRequest.content": `<p>لقد طلبت استعادة نسخة احتياطية. يرجى اختيار خيار:</p>
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
- "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>
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
- "email.backup.restoreRequest.subject": "Το αίτημα επαναφοράς αντιγράφων ασφαλείας",
14858
- "email.backup.restoreRequest.content": `<p>Έχετε αιτηθεί επαναφορά από αντίγραφο ασφαλείας. Παρακαλώ επιλέξτε μια επιλογή:</p>
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
- "email.backup.restoreRequest.subject": "درخواست بازیابی پشتیبان‌گیری شما",
16287
- "email.backup.restoreRequest.content": `<p>درخواست بازیابی نسخه پشتیبان را داده‌اید. لطفاً یک گزینه انتخاب کنید:</p>
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>
@@ -1,30 +1,31 @@
1
- import { defineConfig } from 'vite'
2
- import react from '@vitejs/plugin-react'
3
-
4
- // https://vite.dev/config/
5
- export default defineConfig({
6
- plugins: [react()],
7
- base: "/",
8
- build: {
9
- outDir: "dist"
10
- },
11
- server : {
12
- proxy: {
13
- '/api': {
14
- target: 'http://localhost:7633',
15
- secure: false,
16
- changeOrigin: true,
17
- // Optionnel mais utile pour le débogage :
18
- // Affiche les requêtes proxy dans la console de Vite.
19
- configure: (proxy, options) => {
20
- proxy.on('proxyReq', (proxyReq, req, res) => {
21
- console.log(`[Vite Proxy] Forwarding request: ${req.method} ${req.url} -> ${options.target}${proxyReq.path}`);
22
- });
23
- }
24
- }
25
- }
26
- },
27
- fs: {
28
- allow: ["./src", "../src"]
29
- }
30
- })
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+ import { fileURLToPath, URL } from "node:url";
4
+
5
+ // https://vite.dev/config/
6
+ export default defineConfig({
7
+ plugins: [react()],
8
+ base: "/",
9
+ build: {
10
+ outDir: "dist"
11
+ },
12
+ server : {
13
+ proxy: {
14
+ '/api': {
15
+ target: 'http://localhost:7633',
16
+ secure: false,
17
+ changeOrigin: true,
18
+ // Optionnel mais utile pour le débogage :
19
+ // Affiche les requêtes proxy dans la console de Vite.
20
+ configure: (proxy, options) => {
21
+ proxy.on('proxyReq', (proxyReq, req, res) => {
22
+ console.log(`[Vite Proxy] Forwarding request: ${req.method} ${req.url} -> ${options.target}${proxyReq.path}`);
23
+ });
24
+ }
25
+ }
26
+ }
27
+ },
28
+ fs: {
29
+ allow: ["./src", "../src"]
30
+ }
31
+ })