data-primals-engine 1.2.3 → 1.2.4
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/CONTRIBUTING.md +91 -0
- package/README.md +33 -17
- package/client/src/App.jsx +0 -5
- package/client/src/ConditionBuilder.scss +34 -1
- package/client/src/ConditionBuilder2.jsx +179 -53
- package/client/src/ContentView.jsx +0 -3
- package/client/src/CronBuilder.jsx +0 -1
- package/client/src/CronPartBuilder.jsx +0 -2
- package/client/src/DashboardView.jsx +0 -5
- package/client/src/DataEditor.jsx +8 -10
- package/client/src/DataLayout.jsx +0 -1
- package/client/src/DataTable.jsx +1 -3
- package/client/src/Field.jsx +0 -5
- package/client/src/FlexBuilder.jsx +1 -1
- package/client/src/ModelCreatorField.jsx +1 -5
- package/client/src/RTE.jsx +1 -6
- package/client/src/RTETrans.jsx +0 -2
- package/client/src/RelationField.jsx +1 -1
- package/client/src/RelationValue.jsx +1 -2
- package/client/src/TourSpotlight.jsx +0 -2
- package/client/src/filter.js +87 -0
- package/client/src/hooks/data.js +1 -3
- package/client/src/hooks/useTutorials.jsx +0 -1
- package/package.json +3 -3
- package/server.js +2 -2
- package/src/email.js +2 -2
- package/src/engine.js +59 -20
- package/src/index.js +1 -1
- package/src/middlewares/middleware-mongodb.js +0 -1
- package/src/modules/assistant.js +1 -3
- package/src/modules/bucket.js +3 -4
- package/src/modules/{data.js → data/data.js} +34 -51
- package/src/modules/data/index.js +1 -0
- package/src/modules/file.js +1 -1
- package/src/modules/mongodb.js +0 -1
- package/src/modules/user.js +1 -1
- package/src/modules/workflow.js +38 -38
- package/src/packs.js +4 -1
- package/test/data.backup.integration.test.js +4 -5
- package/test/data.integration.test.js +2 -6
- package/test/events.test.js +1 -1
- package/test/file.test.js +1 -4
- package/test/import_export.integration.test.js +20 -13
- package/test/model.integration.test.js +8 -10
- package/test/user.test.js +2 -2
- package/test/vm.test.js +1 -1
- package/test/workflow.integration.test.js +17 -14
- package/test/workflow.robustness.test.js +15 -10
- package/src/modules/test +0 -147
|
@@ -25,7 +25,7 @@ import Draggable from "./Draggable.jsx";
|
|
|
25
25
|
import CronBuilder from "./CronBuilder.jsx";
|
|
26
26
|
import RTETrans from "./RTETrans.jsx";
|
|
27
27
|
import uniqid from "uniqid";
|
|
28
|
-
import {isConditionMet} from "
|
|
28
|
+
import {isConditionMet} from "../../src/filter";
|
|
29
29
|
|
|
30
30
|
// ... (fonction getInputType) ...
|
|
31
31
|
// Fonction pour obtenir le type d'input HTML basé sur le type de champ du modèle
|
|
@@ -62,7 +62,6 @@ export const DataEditor = forwardRef(function MyDataEditor({
|
|
|
62
62
|
formData,
|
|
63
63
|
setFormData, record, setRecord}, ref){
|
|
64
64
|
|
|
65
|
-
const [focusedField, setFocusedField] = useState({});
|
|
66
65
|
const {me} = useAuthContext()
|
|
67
66
|
const {models} = useModelContext()
|
|
68
67
|
|
|
@@ -103,7 +102,7 @@ export const DataEditor = forwardRef(function MyDataEditor({
|
|
|
103
102
|
case 'textarea':
|
|
104
103
|
return <textarea key={field.name} {...inputProps} />
|
|
105
104
|
case 'richtext':
|
|
106
|
-
return <RTE help={
|
|
105
|
+
return <RTE help={t('field_'+model.name+'_'+field.name+'_hint', field.hint || '')} key={field.name} {...inputProps} field={field} name={formData._id} />;
|
|
107
106
|
case 'richtext_t':
|
|
108
107
|
return <RTETrans
|
|
109
108
|
key={field.name}
|
|
@@ -144,7 +143,6 @@ export const DataEditor = forwardRef(function MyDataEditor({
|
|
|
144
143
|
// C'est un nom de modèle statique
|
|
145
144
|
builderModelName = field.targetModel;
|
|
146
145
|
}
|
|
147
|
-
console.log({builderModelName})
|
|
148
146
|
}
|
|
149
147
|
return <div className={"flex flex-1"} style={{width:'100%'}} key={field.name}>
|
|
150
148
|
{currentViewMode !== 'builder' && ( <div className="condition-builder-toggle">
|
|
@@ -219,10 +217,10 @@ export const DataEditor = forwardRef(function MyDataEditor({
|
|
|
219
217
|
inputProps["min"] = field.min;
|
|
220
218
|
if( field.max)
|
|
221
219
|
inputProps["max"] = field.max;
|
|
222
|
-
return <NumberField help={
|
|
220
|
+
return <NumberField help={t('field_'+model.name+'_'+field.name+'_hint', field.hint || '')} unit={field.unit} key={field.name} {...inputProps} onChange={(e) => handleChange({name: field.name, value: parseFloat(e.target.value.replace(',', '.'))})} />
|
|
223
221
|
case 'relation':
|
|
224
222
|
return (
|
|
225
|
-
<RelationField
|
|
223
|
+
<RelationField help={t('field_'+model.name+'_'+field.name+'_hint', field.hint || '')} key={field.name} model={model} field={field} value={value} onChange={(e) => {
|
|
226
224
|
handleChange(e)
|
|
227
225
|
}} refreshTime={refreshTime} />
|
|
228
226
|
);
|
|
@@ -255,8 +253,8 @@ export const DataEditor = forwardRef(function MyDataEditor({
|
|
|
255
253
|
const displayValue = (typeof value === 'object' && value !== null) ? value.key : (value || '');
|
|
256
254
|
|
|
257
255
|
return <TextField
|
|
258
|
-
help={
|
|
259
|
-
|
|
256
|
+
help={t('field_' + model.name + '_' + field.name + '_hint', field.hint || '')}
|
|
257
|
+
key={field.name}
|
|
260
258
|
type={getInputType(field.type)} {...inputProps}
|
|
261
259
|
value={displayValue}
|
|
262
260
|
onChange={(e) => handleChange({name: field.name, value: e.target.value})} />
|
|
@@ -266,9 +264,9 @@ export const DataEditor = forwardRef(function MyDataEditor({
|
|
|
266
264
|
case 'color':
|
|
267
265
|
return <ColorField help={t('field_'+model.name+'_'+field.name+'_hint', field.hint || '')} key={field.name} name={field.name} value={value} onChange={handleChange} />
|
|
268
266
|
case 'email':
|
|
269
|
-
return <EmailField
|
|
267
|
+
return <EmailField help={t('field_'+model.name+'_'+field.name+'_hint', field.hint || '')} key={field.name} type={getInputType(field.type)} {...inputProps} onChange={(e) => handleChange({name: field.name, value: e.target.value})} />
|
|
270
268
|
default:
|
|
271
|
-
return <TextField
|
|
269
|
+
return <TextField help={t('field_'+model.name+'_'+field.name+'_hint', field.hint || '')} key={field.name} type={getInputType(field.type)} {...inputProps} onChange={(e) => handleChange({name: field.name, value: e.target.value})} />
|
|
272
270
|
}
|
|
273
271
|
}
|
|
274
272
|
|
package/client/src/DataTable.jsx
CHANGED
|
@@ -57,7 +57,7 @@ import PackGallery from "./PackGallery.jsx";
|
|
|
57
57
|
import {HiddenableCell} from "./HiddenableCell.jsx";
|
|
58
58
|
import ConditionBuilder from "./ConditionBuilder.jsx";
|
|
59
59
|
import {pagedFilterToMongoConds} from "./filter.js";
|
|
60
|
-
import {isConditionMet} from "
|
|
60
|
+
import {isConditionMet} from "../../src/filter";
|
|
61
61
|
|
|
62
62
|
// Ajoutez cette constante pour la clé de sessionStorage
|
|
63
63
|
const SESSION_STORAGE_IMPORT_JOBS_KEY = 'activeImportJobs';
|
|
@@ -685,10 +685,8 @@ export function DataTable({
|
|
|
685
685
|
/>
|
|
686
686
|
<ExportDialog isOpen={showExportDialog} onClose={() => {
|
|
687
687
|
setExportDialogVisible(false);
|
|
688
|
-
console.log("close")
|
|
689
688
|
}} availableModels={models} currentModel={selectedModel.name} hasSelection={true} onExport={(data)=>{
|
|
690
689
|
exportMutation(data);
|
|
691
|
-
console.log(data);
|
|
692
690
|
}} />
|
|
693
691
|
{showPackGallery && (
|
|
694
692
|
<Dialog isClosable={true} isModal={true} onClose={() => setShowPackGallery(false)}>
|
package/client/src/Field.jsx
CHANGED
|
@@ -33,9 +33,7 @@ import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs';
|
|
|
33
33
|
|
|
34
34
|
import { PhoneInput } from 'react-international-phone';
|
|
35
35
|
import 'react-international-phone/style.css';
|
|
36
|
-
import CodeMirror, {basicSetup} from "@uiw/react-codemirror";
|
|
37
36
|
import {useAuthContext} from "./contexts/AuthContext.jsx";
|
|
38
|
-
import {maxStringLength} from "data-primals-engine/constants";
|
|
39
37
|
|
|
40
38
|
export const Form = ({
|
|
41
39
|
name,
|
|
@@ -893,7 +891,6 @@ const FileField = ({ inputProps, value, onChange, name, mimeTypes, maxSize, mult
|
|
|
893
891
|
}
|
|
894
892
|
}, [value]);
|
|
895
893
|
|
|
896
|
-
console.log(fileInfos)
|
|
897
894
|
return (
|
|
898
895
|
<div className="field field-file">
|
|
899
896
|
<input
|
|
@@ -1421,12 +1418,10 @@ export const ModelField = ({field, disableable=false, showModel=true, value, fie
|
|
|
1421
1418
|
const itemsFields = [...models.find(f=>f.name === modelValue && me?.username === f._user)?.fields.map(m => ({label: m.name, value: m.name})) || []];
|
|
1422
1419
|
|
|
1423
1420
|
useEffect(() => {
|
|
1424
|
-
console.log({value})
|
|
1425
1421
|
setModelValue(value)
|
|
1426
1422
|
}, [value]);
|
|
1427
1423
|
|
|
1428
1424
|
useEffect(() => {
|
|
1429
|
-
console.log(modelValue)
|
|
1430
1425
|
onChange({name: field.name, value: modelValue});
|
|
1431
1426
|
}, [modelValue]);
|
|
1432
1427
|
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
clearMappingsRecursive
|
|
20
20
|
} from './FlexTreeUtils.js';
|
|
21
21
|
import {Dialog, DialogProvider} from "./Dialog.jsx";
|
|
22
|
-
import {safeAssignObject} from "
|
|
22
|
+
import {safeAssignObject} from "../../src/core";
|
|
23
23
|
|
|
24
24
|
const FlexBuilder = ({ initialConfig = null, models = [], onChange, data = [], lang = 'fr' }) => {
|
|
25
25
|
const { me: user } = useAuthContext();
|
|
@@ -43,7 +43,6 @@ const ModelCreatorField = ({model, handleRenameField, handleRemoveField, handleU
|
|
|
43
43
|
const [showMore, setMoreVisible] = useState(false);
|
|
44
44
|
const hint = (description) => t(description, '') && <div className="hint-icon"><FaCircleInfo data-tooltip-id={`tooltipHint`} data-tooltip-content={description} /></div>
|
|
45
45
|
|
|
46
|
-
const [focusedField, setFocusedField] = useState({});
|
|
47
46
|
return (
|
|
48
47
|
<div className="field-edit">
|
|
49
48
|
|
|
@@ -73,9 +72,7 @@ const ModelCreatorField = ({model, handleRenameField, handleRemoveField, handleU
|
|
|
73
72
|
newFields[index].name = e.target.value;
|
|
74
73
|
setFields(newFields);
|
|
75
74
|
}}
|
|
76
|
-
help={
|
|
77
|
-
onFocus={() => setFocusedField(field)}
|
|
78
|
-
onBlur={() => setFocusedField(null)}
|
|
75
|
+
help={t('modelcreator.name.hint')}
|
|
79
76
|
required
|
|
80
77
|
/>
|
|
81
78
|
{!(!modelLocked && isLocalUser(me) && field.locked) && !field._isNewField && (
|
|
@@ -206,7 +203,6 @@ const ModelCreatorField = ({model, handleRenameField, handleRemoveField, handleU
|
|
|
206
203
|
initialSteps={field.calculation?.steps || []}
|
|
207
204
|
onCalculationChange={(calc) => {
|
|
208
205
|
const newFields = [...fields];
|
|
209
|
-
console.log({calc});
|
|
210
206
|
newFields[index].calculation = calc; // 'index' est l'index du champ calculé dans 'fields'
|
|
211
207
|
setFields(newFields);
|
|
212
208
|
}}
|
package/client/src/RTE.jsx
CHANGED
|
@@ -28,7 +28,7 @@ import css from 'highlight.js/lib/languages/css'
|
|
|
28
28
|
import js from 'highlight.js/lib/languages/javascript'
|
|
29
29
|
import ts from 'highlight.js/lib/languages/typescript'
|
|
30
30
|
import html from 'highlight.js/lib/languages/xml'
|
|
31
|
-
import {escapeHtml
|
|
31
|
+
import {escapeHtml} from "../../src/core";
|
|
32
32
|
// create a lowlight instance
|
|
33
33
|
const lowlight = createLowlight(all)
|
|
34
34
|
|
|
@@ -293,11 +293,6 @@ export const ExtendedImage = Image.extend({
|
|
|
293
293
|
}
|
|
294
294
|
})
|
|
295
295
|
|
|
296
|
-
const CodeBlockComponent = ({children, ...rest}) => {
|
|
297
|
-
console.log(rest);
|
|
298
|
-
return children;
|
|
299
|
-
};
|
|
300
|
-
|
|
301
296
|
const extensions = [
|
|
302
297
|
HardBreak,
|
|
303
298
|
CodeBlock,
|
package/client/src/RTETrans.jsx
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
// Dans C:/Dev/hackersonline-engine/client/src/RTETrans.jsx (Nouveau fichier)
|
|
2
1
|
import React, { useState, useEffect } from 'react';
|
|
3
2
|
import { useTranslation } from 'react-i18next';
|
|
4
3
|
import { RTE } from './RTE.jsx';
|
|
5
|
-
import {useModelContext} from "./contexts/ModelContext.jsx";
|
|
6
4
|
import {useQuery} from "react-query";
|
|
7
5
|
import {getUserId} from "../../src/data.js";
|
|
8
6
|
import {useAuthContext} from "./contexts/AuthContext.jsx";
|
|
@@ -3,7 +3,7 @@ import { useQuery, useQueryClient } from 'react-query';
|
|
|
3
3
|
import { useModelContext } from './contexts/ModelContext.jsx';
|
|
4
4
|
import { TextField } from './Field.jsx';
|
|
5
5
|
import { useAuthContext } from './contexts/AuthContext.jsx';
|
|
6
|
-
import { getDataAsString
|
|
6
|
+
import { getDataAsString } from '../../src/data.js';
|
|
7
7
|
import { FaEdit, FaTrash } from 'react-icons/fa';
|
|
8
8
|
import Button from './Button.jsx';
|
|
9
9
|
import { mainFieldsTypes } from "../../src/constants.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
2
|
import { useQuery } from 'react-query';
|
|
3
3
|
import { useModelContext } from './contexts/ModelContext.jsx';
|
|
4
|
-
import {getDataAsString, getUserId} from '
|
|
4
|
+
import {getDataAsString, getUserId} from '../../src/data.js';
|
|
5
5
|
import { Trans, useTranslation } from 'react-i18next';
|
|
6
6
|
import {Dialog, DialogProvider} from './Dialog.jsx';
|
|
7
7
|
import {FaMagnifyingGlass} from "react-icons/fa6";
|
|
@@ -133,7 +133,6 @@ const RelationValue = ({ field, data, align }) => {
|
|
|
133
133
|
} catch (e) {
|
|
134
134
|
console.log(e);
|
|
135
135
|
}
|
|
136
|
-
//console.log(model, rel, displayValue)
|
|
137
136
|
const bgColor = // Returns a bright color in RGB
|
|
138
137
|
randomColor({
|
|
139
138
|
seed: rel._hash+rel._id,
|
|
@@ -61,8 +61,6 @@ function TourSpotlight({ name, steps = [], isOpen, onComplete, onClose, initialS
|
|
|
61
61
|
useEffect(() => {
|
|
62
62
|
if (!isOpen || !name) return;
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
console.log({currentStep})
|
|
66
64
|
if (!currentStep?.selector) {
|
|
67
65
|
console.warn(`[TourSpotlight] Élément cible non défini pour le tour ${name}`);
|
|
68
66
|
return;
|
package/client/src/filter.js
CHANGED
|
@@ -62,6 +62,93 @@ export const MONGO_CALC_OPERATORS = {
|
|
|
62
62
|
$minute: { label: 'minute', multi: false, isDate: true },
|
|
63
63
|
$second: { label: 'second', multi: false, isDate: true },
|
|
64
64
|
$millisecond: { label: 'ms', multi: false, isDate: true },
|
|
65
|
+
// Date operators
|
|
66
|
+
$dateAdd: {
|
|
67
|
+
label: 'Add to date',
|
|
68
|
+
description: 'Adds a duration to a date',
|
|
69
|
+
isDate: true,
|
|
70
|
+
specialStructure: true,
|
|
71
|
+
args: [
|
|
72
|
+
{ name: 'startDate', label: 'Start Date', type: 'date' },
|
|
73
|
+
{ name: 'unit', label: 'Unit', type: 'select', options: ['year', 'month', 'day', 'hour', 'minute', 'second'] },
|
|
74
|
+
{ name: 'amount', label: 'Amount', type: 'number' },
|
|
75
|
+
{ name: 'timezone', label: 'Timezone', type: 'text', optional: true }
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
$dateSubtract: {
|
|
79
|
+
label: 'Subtract from date',
|
|
80
|
+
description: 'Subtracts a duration from a date',
|
|
81
|
+
isDate: true,
|
|
82
|
+
specialStructure: true,
|
|
83
|
+
args: [
|
|
84
|
+
{ name: 'startDate', label: 'Start Date', type: 'date' },
|
|
85
|
+
{ name: 'unit', label: 'Unit', type: 'select', options: ['year', 'month', 'day', 'hour', 'minute', 'second'] },
|
|
86
|
+
{ name: 'amount', label: 'Amount', type: 'number' },
|
|
87
|
+
{ name: 'timezone', label: 'Timezone', type: 'text', optional: true }
|
|
88
|
+
]
|
|
89
|
+
},
|
|
90
|
+
$dateDiff: {
|
|
91
|
+
label: 'Date Difference',
|
|
92
|
+
description: 'Calculates the difference between two dates in specified units',
|
|
93
|
+
isDate: true,
|
|
94
|
+
specialStructure: true,
|
|
95
|
+
args: [
|
|
96
|
+
{
|
|
97
|
+
name: 'startDate',
|
|
98
|
+
label: 'Start Date',
|
|
99
|
+
type: 'date',
|
|
100
|
+
description: 'The starting date (inclusive)'
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'endDate',
|
|
104
|
+
label: 'End Date',
|
|
105
|
+
type: 'date',
|
|
106
|
+
description: 'The ending date (exclusive)'
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: 'unit',
|
|
110
|
+
label: 'Unit',
|
|
111
|
+
type: 'select',
|
|
112
|
+
options: ['year', 'month', 'week', 'day', 'hour', 'minute', 'second'],
|
|
113
|
+
description: 'The unit for the result'
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: 'timezone',
|
|
117
|
+
label: 'Timezone',
|
|
118
|
+
type: 'text',
|
|
119
|
+
optional: true,
|
|
120
|
+
description: 'The timezone (e.g. "Europe/Paris")'
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
},
|
|
124
|
+
$dateToString: {
|
|
125
|
+
label: 'Format date as string',
|
|
126
|
+
description: 'Formats a date as a string',
|
|
127
|
+
isDate: true,
|
|
128
|
+
specialStructure: true,
|
|
129
|
+
args: [
|
|
130
|
+
{
|
|
131
|
+
name: 'date',
|
|
132
|
+
label: 'Date',
|
|
133
|
+
type: 'date'
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: 'format',
|
|
137
|
+
label: 'Format',
|
|
138
|
+
optional: true,
|
|
139
|
+
type: 'text'
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: 'timezone',
|
|
143
|
+
label: 'Timezone',
|
|
144
|
+
type: 'text',
|
|
145
|
+
optional: true,
|
|
146
|
+
description: 'The timezone (e.g. "Europe/Paris")'
|
|
147
|
+
}
|
|
148
|
+
]
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
// Converters
|
|
65
152
|
$toBool: { label: 'toBool', multi: false, converter: true },
|
|
66
153
|
$toString: { label: 'toString', multi: false, converter: true },
|
|
67
154
|
$toInt: { label: 'toInt', multi: false, converter: true },
|
package/client/src/hooks/data.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import {useQuery} from "react-query";
|
|
2
|
-
import {getObjectHash} from "data-primals-engine/core";
|
|
3
2
|
import {useAuthContext} from "../contexts/AuthContext.jsx";
|
|
4
|
-
import {
|
|
3
|
+
import {getUserId} from "../../../src/data";
|
|
5
4
|
import {useTranslation} from "react-i18next";
|
|
6
5
|
import {useEffect, useState} from "react";
|
|
7
|
-
import {useUI} from "../contexts/UIContext.jsx";
|
|
8
6
|
import {useNotificationContext} from "../NotificationProvider.jsx";
|
|
9
7
|
|
|
10
8
|
export const useData = (model, filter, options) => {
|
|
@@ -138,7 +138,6 @@ export const useTutorials = () => {
|
|
|
138
138
|
const tutorial = tutorials.find(t => t._id === currentActiveState.id);
|
|
139
139
|
if (!tutorial) break;
|
|
140
140
|
|
|
141
|
-
console.log({currentActiveState})
|
|
142
141
|
const stageConfig = tutorial.stages.find(s => s.stage === currentActiveState.stage);
|
|
143
142
|
if (!stageConfig) {
|
|
144
143
|
console.log('[Tutoriels] Fin du tutoriel, attribution des récompenses.');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "data-primals-engine",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"description": "data-primals-engine is a package responsible from handling large amount of data using MongoDB in a practical and performant way. It can also get workflow models working (for automation), and fully supports internationalisation. It also has integrated AI assistant.",
|
|
5
5
|
"main": "src/engine.js",
|
|
6
6
|
"type": "module",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"exports": {
|
|
41
41
|
".": "./src/index.js",
|
|
42
|
-
"./modules/*": "./src/modules
|
|
42
|
+
"./modules/*": "./src/modules/*/index.js",
|
|
43
43
|
"./client": "./client/index.js",
|
|
44
44
|
"./*": "./src/*.js"
|
|
45
45
|
},
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@langchain/core": "^0.3.66",
|
|
53
|
+
"@langchain/deepseek": "^0.1.0",
|
|
53
54
|
"@langchain/google-genai": "^0.2.16",
|
|
54
55
|
"@langchain/openai": "^0.6.3",
|
|
55
56
|
"archiver": "^7.0.1",
|
|
@@ -62,7 +63,6 @@
|
|
|
62
63
|
"cookie-parser": "^1.4.7",
|
|
63
64
|
"cronstrue": "^3.2.0",
|
|
64
65
|
"csv-parse": "^6.1.0",
|
|
65
|
-
"data-primals-engine": "^1.0.14",
|
|
66
66
|
"date-fns": "^4.1.0",
|
|
67
67
|
"express-csrf-double-submit-cookie": "^2.0.0",
|
|
68
68
|
"express-formidable": "^1.2.0",
|
package/server.js
CHANGED
|
@@ -8,7 +8,7 @@ import {Config, Engine, BenchmarkTool, GameObject, Logger} from "./src/index.js"
|
|
|
8
8
|
import sirv from "sirv";
|
|
9
9
|
import express from "express";
|
|
10
10
|
|
|
11
|
-
Config.Set("modules", ["
|
|
11
|
+
Config.Set("modules", ["data", "mongodb", "file", "bucket", "workflow","user", "assistant", "swagger"])
|
|
12
12
|
Config.Set("middlewares", []);
|
|
13
13
|
|
|
14
14
|
const bench = GameObject.Create("Benchmark");
|
|
@@ -30,7 +30,7 @@ if (process.argv.length === 3) {
|
|
|
30
30
|
const port = process.env.PORT || 7633;
|
|
31
31
|
engine.start(port, async (r) => {
|
|
32
32
|
const logger = engine.getComponent(Logger);
|
|
33
|
-
console.log("Server started on port" + port);
|
|
33
|
+
console.log("Server started on port " + port);
|
|
34
34
|
timer.stop();
|
|
35
35
|
|
|
36
36
|
});
|
package/src/email.js
CHANGED
|
@@ -75,9 +75,9 @@ export const sendEmail = async (email = "", data, smtpConfig = null, lang, tpl =
|
|
|
75
75
|
|
|
76
76
|
try {
|
|
77
77
|
await Promise.all(sendPromises);
|
|
78
|
-
console.log(`Email(s)
|
|
78
|
+
console.log(`Email(s) sent successfully to : ${emails.join(', ')}`);
|
|
79
79
|
} catch (error) {
|
|
80
|
-
console.error("
|
|
80
|
+
console.error("Error when sending to one ore more emails :", error);
|
|
81
81
|
// Vous pouvez relancer l'erreur si vous voulez que l'appelant la gère
|
|
82
82
|
throw error;
|
|
83
83
|
}
|
package/src/engine.js
CHANGED
|
@@ -13,13 +13,15 @@ import {
|
|
|
13
13
|
import http from "http";
|
|
14
14
|
import cookieParser from "cookie-parser";
|
|
15
15
|
import requestIp from 'request-ip';
|
|
16
|
-
import {createModel, deleteModels, getModels, installAllPacks, validateModelStructure} from "./modules/data.js";
|
|
16
|
+
import {createModel, deleteModels, getModels, installAllPacks, validateModelStructure} from "./modules/data/data.js";
|
|
17
17
|
import {defaultModels} from "./defaultModels.js";
|
|
18
18
|
import {DefaultUserProvider} from "./providers.js";
|
|
19
19
|
import formidableMiddleware from 'express-formidable';
|
|
20
20
|
import sirv from "sirv";
|
|
21
21
|
import * as tls from "node:tls";
|
|
22
22
|
import {Event} from "./events.js";
|
|
23
|
+
import path from "node:path";
|
|
24
|
+
import {isPathRelativeTo, isValidPath} from "./core.js";
|
|
23
25
|
|
|
24
26
|
// Constants
|
|
25
27
|
const isProduction = process.env.NODE_ENV === 'production'
|
|
@@ -49,23 +51,35 @@ const isTlsActive = !(!process.env.TLS || ["0", "false"].includes(process.env.TL
|
|
|
49
51
|
const clientOptions = {
|
|
50
52
|
maxPoolSize: databasePoolSize
|
|
51
53
|
};
|
|
52
|
-
|
|
54
|
+
|
|
55
|
+
// We add TLS options if enabled
|
|
53
56
|
if (isTlsActive) {
|
|
54
57
|
clientOptions.tls = true;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
|
|
59
|
+
// is mTLS ? (client certificate required instead of password)
|
|
60
|
+
if (process.env.CERT) {
|
|
61
|
+
clientOptions.secureContext = tls.createSecureContext({
|
|
62
|
+
ca: fs.readFileSync(process.env.CA_CERT),
|
|
63
|
+
cert: fs.readFileSync(process.env.CERT),
|
|
64
|
+
key: fs.readFileSync(process.env.CERT_KEY)
|
|
65
|
+
});
|
|
66
|
+
}else {
|
|
67
|
+
// Path to the authority certificate
|
|
68
|
+
if (process.env.CA_CERT) {
|
|
69
|
+
clientOptions.tlsCAFile = process.env.CA_CERT;
|
|
70
|
+
}
|
|
71
|
+
// Path to the certificate key
|
|
72
|
+
if (process.env.CERT_KEY) {
|
|
73
|
+
clientOptions.tlsCertificateKeyFile = process.env.CERT_KEY;
|
|
74
|
+
}
|
|
62
75
|
}
|
|
63
|
-
// Options pour le développement (à utiliser avec prudence)
|
|
64
76
|
if (tlsAllowInvalidCertificates) {
|
|
65
77
|
clientOptions.tlsAllowInvalidCertificates = true;
|
|
78
|
+
console.warn("🚨 [SECURITY WARNING] tlsAllowInvalidCertificates is ON. Server certificate will not be validated.");
|
|
66
79
|
}
|
|
67
80
|
if (tlsAllowInvalidHostnames) {
|
|
68
81
|
clientOptions.tlsAllowInvalidHostnames = true;
|
|
82
|
+
console.warn("🚨 [SECURITY WARNING] tlsAllowInvalidHostnames is ON. Server hostname will not be validated.");
|
|
69
83
|
}
|
|
70
84
|
}
|
|
71
85
|
|
|
@@ -143,18 +157,44 @@ export const Engine = {
|
|
|
143
157
|
}
|
|
144
158
|
};
|
|
145
159
|
|
|
146
|
-
await Promise.all(Config.Get('modules', []).map(async
|
|
160
|
+
await Promise.all(Config.Get('modules', []).map(async moduleIdentifier => {
|
|
147
161
|
try {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
162
|
+
let moduleDir;
|
|
163
|
+
const moduleName = path.basename(moduleIdentifier);
|
|
164
|
+
|
|
165
|
+
const directPath = path.resolve(moduleIdentifier);
|
|
166
|
+
let isDir = fs.existsSync(directPath) && fs.statSync(directPath).isDirectory();
|
|
167
|
+
if (isDir) {
|
|
168
|
+
moduleDir = directPath;
|
|
169
|
+
if (!fs.existsSync(moduleDir) || !fs.statSync(moduleDir).isDirectory()) {
|
|
170
|
+
logger.warn(`Le dossier du module est introuvable pour l'identifiant : '${moduleIdentifier}'. Chemin cherché : '${moduleDir}'.`);
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
moduleDir = path.resolve('./src/modules', moduleIdentifier);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
let moduleEntryPoint;
|
|
178
|
+
const jsPath = moduleDir+'.js';
|
|
179
|
+
const indexJsPath = path.join(moduleDir, 'index.js');
|
|
180
|
+
const moduleJsPath = path.join(moduleDir, `${moduleName}.js`);
|
|
181
|
+
|
|
182
|
+
if (fs.existsSync(jsPath)) {
|
|
183
|
+
moduleEntryPoint = 'file://'+jsPath;
|
|
184
|
+
} else if (fs.existsSync(indexJsPath)) {
|
|
185
|
+
moduleEntryPoint = 'file://'+indexJsPath;
|
|
186
|
+
} else if (fs.existsSync(moduleJsPath)) {
|
|
187
|
+
moduleEntryPoint = 'file://'+moduleJsPath;
|
|
152
188
|
}
|
|
153
|
-
|
|
154
|
-
|
|
189
|
+
|
|
190
|
+
return await importModule(moduleEntryPoint);
|
|
191
|
+
} catch (e) {
|
|
192
|
+
logger.error(`Échec du chargement du module '${moduleIdentifier}':`, e.stack);
|
|
193
|
+
return null;
|
|
155
194
|
}
|
|
156
|
-
})).then(async
|
|
157
|
-
|
|
195
|
+
})).then(async results => {
|
|
196
|
+
// On filtre les modules qui n'ont pas pu être chargés
|
|
197
|
+
engine._modules = results.filter(Boolean);
|
|
158
198
|
return Promise.resolve();
|
|
159
199
|
});
|
|
160
200
|
|
|
@@ -239,4 +279,3 @@ export const Engine = {
|
|
|
239
279
|
return engine;
|
|
240
280
|
}
|
|
241
281
|
}
|
|
242
|
-
|
package/src/index.js
CHANGED
|
@@ -10,6 +10,6 @@ export { UserProvider } from './providers.js';
|
|
|
10
10
|
|
|
11
11
|
// --- Database & Data Modules ---
|
|
12
12
|
export { datasCollection, filesCollection, modelsCollection, packsCollection } from './modules/mongodb.js';
|
|
13
|
-
export { searchData, insertData, editData, exportData, importData, scheduleAlerts, cancelAlerts, validateModelStructure, installPack, jobDumpUserData, loadFromDump, dumpUserData, validateRestoreRequest, patchData, deleteData, createModel, editModel, deleteModels, getModel, getModels } from './modules/data.js';
|
|
13
|
+
export { searchData, insertData, editData, exportData, importData, scheduleAlerts, cancelAlerts, validateModelStructure, installPack, jobDumpUserData, loadFromDump, dumpUserData, validateRestoreRequest, patchData, deleteData, createModel, editModel, deleteModels, getModel, getModels } from './modules/data/index.js';
|
|
14
14
|
|
|
15
15
|
|
package/src/modules/assistant.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
// server/src/modules/assistant.js
|
|
2
|
-
|
|
3
1
|
import { getCollectionForUser, modelsCollection } from "./mongodb.js";
|
|
4
2
|
import { Logger } from "../gameObject.js";
|
|
5
3
|
import { ChatOpenAI } from "@langchain/openai";
|
|
6
4
|
import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
|
|
7
5
|
import { HumanMessage, SystemMessage } from "@langchain/core/messages";
|
|
8
|
-
import {searchData, patchData, deleteData, insertData} from "./data.js";
|
|
6
|
+
import {searchData, patchData, deleteData, insertData} from "./data/index.js";
|
|
9
7
|
import { getDataAsString } from "../data.js";
|
|
10
8
|
import i18n from "../../src/i18n.js";
|
|
11
9
|
import {generateLimiter} from "./user.js";
|
package/src/modules/bucket.js
CHANGED
|
@@ -3,7 +3,7 @@ import AWS from "aws-sdk";
|
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import {decryptValue, encryptValue} from "../data.js";
|
|
6
|
-
import {loadFromDump, validateRestoreRequest} from "./data.js";
|
|
6
|
+
import {loadFromDump, validateRestoreRequest} from "./data/index.js";
|
|
7
7
|
import {Logger} from "../gameObject.js";
|
|
8
8
|
import {middlewareAuthenticator, userInitiator} from "./user.js";
|
|
9
9
|
import {awsDefaultConfig, maxBytesPerSecondThrottleData} from "../constants.js";
|
|
@@ -117,7 +117,6 @@ export async function getUserS3Config(user) {
|
|
|
117
117
|
bucketName: userConfig.bucketName || defaultConfig.bucketName
|
|
118
118
|
};
|
|
119
119
|
}
|
|
120
|
-
console.log({defaultConfig})
|
|
121
120
|
return defaultConfig;
|
|
122
121
|
}
|
|
123
122
|
const getS3Client = (s3Config) => {
|
|
@@ -212,9 +211,9 @@ export const deleteFromS3 = async (s3Config, remoteFilename) => {
|
|
|
212
211
|
|
|
213
212
|
try {
|
|
214
213
|
await s3.deleteObject(params).promise();
|
|
215
|
-
console.log(`
|
|
214
|
+
console.log(`File successfully removed from S3 : ${bucketPath}`);
|
|
216
215
|
} catch (err) {
|
|
217
|
-
console.error("
|
|
216
|
+
console.error("Error when deleting S3 file :", err);
|
|
218
217
|
throw err;
|
|
219
218
|
}
|
|
220
219
|
};
|