data-primals-engine 1.6.3 → 1.7.0
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 +52 -900
- package/client/index.js +3 -0
- package/client/package-lock.json +7563 -8811
- package/client/package.json +11 -1
- package/client/src/App.scss +29 -0
- package/client/src/AssistantChat.jsx +369 -362
- package/client/src/DataEditor.jsx +383 -383
- package/client/src/DataLayout.jsx +54 -20
- package/client/src/ModelList.jsx +280 -280
- package/client/src/ViewSwitcher.scss +0 -31
- package/client/src/_variables.scss +3 -0
- package/client/src/constants.js +81 -100
- package/client/src/contexts/CommandContext.jsx +274 -259
- package/client/vite.config.js +30 -30
- package/doc/AI-assistance.md +93 -0
- package/doc/Advanced-workflows.md +90 -0
- package/doc/Event-system.md +79 -0
- package/doc/Packs-gallery.md +73 -0
- package/doc/automation-workflows.md +102 -0
- package/doc/core-concepts.md +33 -0
- package/doc/custom-api-endpoints.md +40 -0
- package/doc/dashboards-kpis-charts.md +49 -0
- package/doc/data-management.md +120 -0
- package/doc/data-models.md +75 -0
- package/doc/index.md +14 -0
- package/doc/roles-permissions.md +43 -0
- package/doc/users.md +30 -0
- package/package.json +20 -10
- package/src/client.js +6 -4
- package/src/constants.js +1 -1
- package/src/core.js +31 -12
- package/src/defaultModels.js +1 -1
- package/src/engine.js +342 -335
- package/src/filter.js +72 -173
- package/src/migrate.js +1 -1
- package/src/modules/assistant/assistant.js +30 -20
- package/src/modules/data/data.backup.js +4 -4
- package/src/modules/data/data.js +8 -7
- package/src/modules/data/data.operations.js +186 -133
- package/src/modules/data/data.relations.js +3 -2
- package/src/modules/data/data.scheduling.js +1 -1
- package/src/modules/mongodb.js +17 -8
- package/src/modules/swagger.js +25 -5
- package/src/modules/user.js +108 -79
- package/src/modules/workflow.js +138 -3
- package/src/packs.js +5697 -5697
- package/src/profiles.js +19 -0
- package/swagger-en.yml +3391 -1550
- package/swagger-fr.yml +3385 -2896
- package/test/assistant.test.js +2 -2
- package/test/core.test.js +341 -0
- package/test/data.backup.integration.test.js +3 -1
- package/test/data.history.integration.test.js +0 -1
- package/test/data.integration.test.js +137 -2
- package/test/user.test.js +33 -29
package/src/engine.js
CHANGED
|
@@ -1,335 +1,342 @@
|
|
|
1
|
-
import {GameObject, Logger} from "./gameObject.js";
|
|
2
|
-
import {Config} from "./config.js";
|
|
3
|
-
import fs from 'node:fs'
|
|
4
|
-
import express from 'express'
|
|
5
|
-
import {MongoClient as InternalMongoClient} from 'mongodb'
|
|
6
|
-
import process from "process";
|
|
7
|
-
import {
|
|
8
|
-
cookiesSecret,
|
|
9
|
-
databasePoolSize,
|
|
10
|
-
dbName as dbNameBase,
|
|
11
|
-
tlsAllowInvalidCertificates, tlsAllowInvalidHostnames
|
|
12
|
-
} from "./constants.js";
|
|
13
|
-
import http from "http";
|
|
14
|
-
import cookieParser from "cookie-parser";
|
|
15
|
-
import requestIp from 'request-ip';
|
|
16
|
-
import {defaultModels} from "./defaultModels.js";
|
|
17
|
-
import {DefaultUserProvider} from "./providers.js";
|
|
18
|
-
import formidableMiddleware from 'express-formidable';
|
|
19
|
-
import sirv from "sirv";
|
|
20
|
-
import * as tls from "node:tls";
|
|
21
|
-
import {Event} from "./events.js";
|
|
22
|
-
import path from "node:path";
|
|
23
|
-
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
24
|
-
import {validateModelStructure} from "./modules/data/data.validation.js";
|
|
25
|
-
import { setSafeRegex } from "./filter.js";
|
|
26
|
-
import safeRegexCallback from "safe-regex";
|
|
27
|
-
import {createModel, deleteModels, getModels, installAllPacks} from "./modules/data/data.operations.js";
|
|
28
|
-
// Constants
|
|
29
|
-
|
|
30
|
-
// On définit __dirname pour obtenir le chemin absolu du répertoire courant,
|
|
31
|
-
// ce qui est la méthode standard en ES Modules.
|
|
32
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
33
|
-
const __dirname = path.dirname(__filename);
|
|
34
|
-
|
|
35
|
-
let dbName = Config.Get('dbName', dbNameBase);
|
|
36
|
-
let caFile, certFile, keyFile;
|
|
37
|
-
try {
|
|
38
|
-
if (process.env.CA_CERT)
|
|
39
|
-
caFile = fs.readFileSync(process.env.CA_CERT);
|
|
40
|
-
} catch (e) {}
|
|
41
|
-
try {
|
|
42
|
-
if (process.env.CERT)
|
|
43
|
-
certFile = fs.readFileSync(process.env.CERT);
|
|
44
|
-
}catch (e) {}
|
|
45
|
-
try{
|
|
46
|
-
if (process.env.CERT_KEY)
|
|
47
|
-
keyFile = fs.readFileSync(process.env.CERT_KEY);
|
|
48
|
-
} catch (e) {}
|
|
49
|
-
|
|
50
|
-
const secureContext = tls.createSecureContext({
|
|
51
|
-
ca: caFile, cert: certFile, key: keyFile
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
export const dbUrl = process.env.CI ? 'mongodb://mongodb:27017' : (process.env.MONGO_DB_URL || 'mongodb://127.0.0.1:27017');
|
|
55
|
-
|
|
56
|
-
const isTlsActive = !(!process.env.TLS || ["0", "false"].includes(process.env.TLS.toLowerCase()));
|
|
57
|
-
|
|
58
|
-
const clientOptions = {
|
|
59
|
-
maxPoolSize: databasePoolSize
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
// We add TLS options if enabled
|
|
63
|
-
if (isTlsActive) {
|
|
64
|
-
clientOptions.tls = true;
|
|
65
|
-
|
|
66
|
-
// is mTLS ? (client certificate required instead of password)
|
|
67
|
-
if (process.env.CERT) {
|
|
68
|
-
clientOptions.secureContext = tls.createSecureContext({
|
|
69
|
-
ca: fs.readFileSync(process.env.CA_CERT),
|
|
70
|
-
cert: fs.readFileSync(process.env.CERT),
|
|
71
|
-
key: fs.readFileSync(process.env.CERT_KEY)
|
|
72
|
-
});
|
|
73
|
-
}else {
|
|
74
|
-
// Path to the authority certificate
|
|
75
|
-
if (process.env.CA_CERT) {
|
|
76
|
-
clientOptions.tlsCAFile = process.env.CA_CERT;
|
|
77
|
-
}
|
|
78
|
-
// Path to the certificate key
|
|
79
|
-
if (process.env.CERT_KEY) {
|
|
80
|
-
clientOptions.tlsCertificateKeyFile = process.env.CERT_KEY;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
if (tlsAllowInvalidCertificates) {
|
|
84
|
-
clientOptions.tlsAllowInvalidCertificates = true;
|
|
85
|
-
console.warn("🚨 [SECURITY WARNING] tlsAllowInvalidCertificates is ON. Server certificate will not be validated.");
|
|
86
|
-
}
|
|
87
|
-
if (tlsAllowInvalidHostnames) {
|
|
88
|
-
clientOptions.tlsAllowInvalidHostnames = true;
|
|
89
|
-
console.warn("🚨 [SECURITY WARNING] tlsAllowInvalidHostnames is ON. Server hostname will not be validated.");
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export const MongoClient = new InternalMongoClient(dbUrl, clientOptions);
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
// Database Name
|
|
97
|
-
export const MongoDatabase =
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
engine
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
})
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
}
|
|
1
|
+
import {GameObject, Logger} from "./gameObject.js";
|
|
2
|
+
import {Config} from "./config.js";
|
|
3
|
+
import fs from 'node:fs'
|
|
4
|
+
import express from 'express'
|
|
5
|
+
import {MongoClient as InternalMongoClient} from 'mongodb'
|
|
6
|
+
import process from "process";
|
|
7
|
+
import {
|
|
8
|
+
cookiesSecret,
|
|
9
|
+
databasePoolSize,
|
|
10
|
+
dbName as dbNameBase,
|
|
11
|
+
tlsAllowInvalidCertificates, tlsAllowInvalidHostnames
|
|
12
|
+
} from "./constants.js";
|
|
13
|
+
import http from "http";
|
|
14
|
+
import cookieParser from "cookie-parser";
|
|
15
|
+
import requestIp from 'request-ip';
|
|
16
|
+
import {defaultModels} from "./defaultModels.js";
|
|
17
|
+
import {DefaultUserProvider} from "./providers.js";
|
|
18
|
+
import formidableMiddleware from 'express-formidable';
|
|
19
|
+
import sirv from "sirv";
|
|
20
|
+
import * as tls from "node:tls";
|
|
21
|
+
import {Event} from "./events.js";
|
|
22
|
+
import path from "node:path";
|
|
23
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
24
|
+
import {validateModelStructure} from "./modules/data/data.validation.js";
|
|
25
|
+
import { setSafeRegex } from "./filter.js";
|
|
26
|
+
import safeRegexCallback from "safe-regex";
|
|
27
|
+
import {createModel, deleteModels, getModels, installAllPacks} from "./modules/data/data.operations.js";
|
|
28
|
+
// Constants
|
|
29
|
+
|
|
30
|
+
// On définit __dirname pour obtenir le chemin absolu du répertoire courant,
|
|
31
|
+
// ce qui est la méthode standard en ES Modules.
|
|
32
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
33
|
+
const __dirname = path.dirname(__filename);
|
|
34
|
+
|
|
35
|
+
let dbName = Config.Get('dbName', dbNameBase);
|
|
36
|
+
let caFile, certFile, keyFile;
|
|
37
|
+
try {
|
|
38
|
+
if (process.env.CA_CERT)
|
|
39
|
+
caFile = fs.readFileSync(process.env.CA_CERT);
|
|
40
|
+
} catch (e) {}
|
|
41
|
+
try {
|
|
42
|
+
if (process.env.CERT)
|
|
43
|
+
certFile = fs.readFileSync(process.env.CERT);
|
|
44
|
+
}catch (e) {}
|
|
45
|
+
try{
|
|
46
|
+
if (process.env.CERT_KEY)
|
|
47
|
+
keyFile = fs.readFileSync(process.env.CERT_KEY);
|
|
48
|
+
} catch (e) {}
|
|
49
|
+
|
|
50
|
+
const secureContext = tls.createSecureContext({
|
|
51
|
+
ca: caFile, cert: certFile, key: keyFile
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export const dbUrl = process.env.CI ? 'mongodb://mongodb:27017' : (process.env.MONGO_DB_URL || 'mongodb://127.0.0.1:27017');
|
|
55
|
+
|
|
56
|
+
const isTlsActive = !(!process.env.TLS || ["0", "false"].includes(process.env.TLS.toLowerCase()));
|
|
57
|
+
|
|
58
|
+
const clientOptions = {
|
|
59
|
+
maxPoolSize: databasePoolSize
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// We add TLS options if enabled
|
|
63
|
+
if (isTlsActive) {
|
|
64
|
+
clientOptions.tls = true;
|
|
65
|
+
|
|
66
|
+
// is mTLS ? (client certificate required instead of password)
|
|
67
|
+
if (process.env.CERT) {
|
|
68
|
+
clientOptions.secureContext = tls.createSecureContext({
|
|
69
|
+
ca: fs.readFileSync(process.env.CA_CERT),
|
|
70
|
+
cert: fs.readFileSync(process.env.CERT),
|
|
71
|
+
key: fs.readFileSync(process.env.CERT_KEY)
|
|
72
|
+
});
|
|
73
|
+
}else {
|
|
74
|
+
// Path to the authority certificate
|
|
75
|
+
if (process.env.CA_CERT) {
|
|
76
|
+
clientOptions.tlsCAFile = process.env.CA_CERT;
|
|
77
|
+
}
|
|
78
|
+
// Path to the certificate key
|
|
79
|
+
if (process.env.CERT_KEY) {
|
|
80
|
+
clientOptions.tlsCertificateKeyFile = process.env.CERT_KEY;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (tlsAllowInvalidCertificates) {
|
|
84
|
+
clientOptions.tlsAllowInvalidCertificates = true;
|
|
85
|
+
console.warn("🚨 [SECURITY WARNING] tlsAllowInvalidCertificates is ON. Server certificate will not be validated.");
|
|
86
|
+
}
|
|
87
|
+
if (tlsAllowInvalidHostnames) {
|
|
88
|
+
clientOptions.tlsAllowInvalidHostnames = true;
|
|
89
|
+
console.warn("🚨 [SECURITY WARNING] tlsAllowInvalidHostnames is ON. Server hostname will not be validated.");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const MongoClient = new InternalMongoClient(dbUrl, clientOptions);
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
// Database Name
|
|
97
|
+
export const MongoDatabase = () => {
|
|
98
|
+
let dbName = Config.Get('dbName', dbNameBase);
|
|
99
|
+
return MongoClient.db(dbName);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
export const Engine = {
|
|
104
|
+
Create: async (options = { app : null}) => {
|
|
105
|
+
// On injecte la dépendance safe-regex dans le module de filtrage au tout début.
|
|
106
|
+
setSafeRegex(safeRegexCallback);
|
|
107
|
+
|
|
108
|
+
const engine = GameObject.Create("Engine");
|
|
109
|
+
console.log("Creating engine", Config.Get('modules'));
|
|
110
|
+
const logger = engine.addComponent(Logger);
|
|
111
|
+
|
|
112
|
+
// Expose the Event bus on the engine instance for dependency injection
|
|
113
|
+
engine.Event = Event;
|
|
114
|
+
|
|
115
|
+
engine.userProvider = new DefaultUserProvider(engine);
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
engine.setUserProvider = (providerInstance) => {
|
|
119
|
+
engine.userProvider = providerInstance;
|
|
120
|
+
logger.info(`Custom UserProvider '${providerInstance.constructor.name}' has been set.`);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
if (!options.app) {
|
|
124
|
+
options.app = express();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const { app } = options;
|
|
128
|
+
// Allows you to set port in the project properties.
|
|
129
|
+
app.set('port', process.env.PORT || 3000);
|
|
130
|
+
app.set('engine', engine);
|
|
131
|
+
|
|
132
|
+
app.use(formidableMiddleware({
|
|
133
|
+
encoding: 'utf-8',
|
|
134
|
+
uploadDir: process.cwd()+'/uploads/tmp',
|
|
135
|
+
multiples: true // req.files to be arrays of files
|
|
136
|
+
}));
|
|
137
|
+
|
|
138
|
+
const cs = Config.Get('cookieSecret', process.env.COOKIES_SECRET || cookiesSecret)
|
|
139
|
+
app.use(cookieParser(cs));
|
|
140
|
+
app.use(requestIp.mw())
|
|
141
|
+
|
|
142
|
+
engine.use = (...args) => {
|
|
143
|
+
return app.use(...args);
|
|
144
|
+
}
|
|
145
|
+
engine.post = (...args) => {
|
|
146
|
+
return app.post(...args);
|
|
147
|
+
};
|
|
148
|
+
engine.get = (...args) => {
|
|
149
|
+
return app.get(...args);
|
|
150
|
+
};
|
|
151
|
+
engine.delete = (...args) => {
|
|
152
|
+
return app.delete(...args);
|
|
153
|
+
};
|
|
154
|
+
engine.patch = (...args) => {
|
|
155
|
+
return app.patch(...args);
|
|
156
|
+
};
|
|
157
|
+
engine.put = (...args) => {
|
|
158
|
+
return app.put(...args);
|
|
159
|
+
};
|
|
160
|
+
engine.all = (...args) => {
|
|
161
|
+
return app.all(...args);
|
|
162
|
+
};
|
|
163
|
+
engine.getModule = (module) => {
|
|
164
|
+
return engine._modules.find(m => m.module === module);
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const importAndPrepareModule = async (moduleEntryPoint, moduleName) => {
|
|
168
|
+
const moduleA = await import(moduleEntryPoint);
|
|
169
|
+
|
|
170
|
+
let moduleInstance = null;
|
|
171
|
+
let onInitFunction = null;
|
|
172
|
+
|
|
173
|
+
// Cas 1: `export default { onInit }`
|
|
174
|
+
if (moduleA.default && typeof moduleA.default.onInit === 'function') {
|
|
175
|
+
moduleInstance = moduleA.default;
|
|
176
|
+
onInitFunction = moduleA.default.onInit;
|
|
177
|
+
}
|
|
178
|
+
// Cas 2: `export async function onInit() {}`
|
|
179
|
+
else if (typeof moduleA.onInit === 'function') {
|
|
180
|
+
moduleInstance = moduleA;
|
|
181
|
+
onInitFunction = moduleA.onInit;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (moduleInstance) {
|
|
185
|
+
// On stocke la fonction onInit pour plus tard et on retourne l'instance
|
|
186
|
+
// avec le nom court du module.
|
|
187
|
+
return { ...moduleInstance, onInit: onInitFunction, module: moduleName };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
logger.warn(`Module loaded from ${moduleEntryPoint} does not export an onInit function or a default object with onInit.`);
|
|
191
|
+
return null;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
engine._modules = [];
|
|
195
|
+
|
|
196
|
+
// On charge uniquement les modules spécifiés dans la configuration.
|
|
197
|
+
const allModules = Config.Get('modules', []);
|
|
198
|
+
logger.info("Modules to load : ", JSON.stringify(allModules));
|
|
199
|
+
const loadedModules = []; // Liste temporaire pour la phase 1
|
|
200
|
+
for (const moduleIdentifier of allModules) {
|
|
201
|
+
let moduleEntryPoint = null;
|
|
202
|
+
const moduleName = path.basename(moduleIdentifier, '.js');
|
|
203
|
+
try {
|
|
204
|
+
// 1. Tenter de résoudre comme un chemin (relatif au projet ou absolu)
|
|
205
|
+
const externalPath = path.resolve(process.cwd(), moduleIdentifier);
|
|
206
|
+
if (fs.existsSync(externalPath)) {
|
|
207
|
+
const stats = fs.statSync(externalPath);
|
|
208
|
+
if (stats.isDirectory()) {
|
|
209
|
+
// C'est un répertoire, on cherche le point d'entrée
|
|
210
|
+
const indexJsPath = path.join(externalPath, 'index.js');
|
|
211
|
+
const moduleJsPath = path.join(externalPath, `${moduleName}.js`);
|
|
212
|
+
if (fs.existsSync(indexJsPath)) moduleEntryPoint = pathToFileURL(indexJsPath).href;
|
|
213
|
+
else if (fs.existsSync(moduleJsPath)) moduleEntryPoint = pathToFileURL(moduleJsPath).href;
|
|
214
|
+
} else {
|
|
215
|
+
// C'est un fichier
|
|
216
|
+
moduleEntryPoint = pathToFileURL(externalPath).href;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (!moduleEntryPoint) {
|
|
221
|
+
// 2. Si ce n'est pas un chemin, tenter de résoudre comme un module interne
|
|
222
|
+
const internalDir = path.resolve(__dirname, 'modules', moduleIdentifier);
|
|
223
|
+
const internalFile = path.resolve(__dirname, 'modules', `${moduleIdentifier}.js`);
|
|
224
|
+
|
|
225
|
+
if (fs.existsSync(internalDir) && fs.statSync(internalDir).isDirectory()) {
|
|
226
|
+
// C'est un répertoire de module interne
|
|
227
|
+
const indexJsPath = path.join(internalDir, 'index.js');
|
|
228
|
+
const moduleJsPath = path.join(internalDir, `${moduleIdentifier}.js`);
|
|
229
|
+
if (fs.existsSync(indexJsPath)) moduleEntryPoint = pathToFileURL(indexJsPath).href;
|
|
230
|
+
else if (fs.existsSync(moduleJsPath)) moduleEntryPoint = pathToFileURL(moduleJsPath).href;
|
|
231
|
+
} else if (fs.existsSync(internalFile)) {
|
|
232
|
+
// C'est un fichier de module interne
|
|
233
|
+
moduleEntryPoint = pathToFileURL(internalFile).href;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (moduleEntryPoint) {
|
|
238
|
+
const loadedModule = await importAndPrepareModule(moduleEntryPoint, moduleName);
|
|
239
|
+
if (loadedModule) {
|
|
240
|
+
loadedModules.push(loadedModule);
|
|
241
|
+
}
|
|
242
|
+
} else {
|
|
243
|
+
logger.warn(`Could not resolve or find an entry point for module '${moduleIdentifier}'.`);
|
|
244
|
+
}
|
|
245
|
+
} catch (e) {
|
|
246
|
+
logger.error(`Could not load module '${moduleName}' (${moduleIdentifier}):`, e.stack);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Phase 2: Enregistrer et Initialiser tous les modules chargés
|
|
251
|
+
engine._modules = loadedModules; // On enregistre tous les modules dans le moteur
|
|
252
|
+
for (const moduleInstance of engine._modules) {
|
|
253
|
+
if (typeof moduleInstance.onInit === 'function') {
|
|
254
|
+
logger.info(`Initializing module '${moduleInstance.module}'...`);
|
|
255
|
+
await moduleInstance.onInit(engine);
|
|
256
|
+
logger.info(`Module '${moduleInstance.module}' loaded and initialized.`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
let server;
|
|
261
|
+
|
|
262
|
+
engine.start = async (port, cb) =>{
|
|
263
|
+
// Use connect method to connect to the server
|
|
264
|
+
|
|
265
|
+
// Start http server
|
|
266
|
+
server = http.createServer(app);
|
|
267
|
+
|
|
268
|
+
// Server Timeout Settings
|
|
269
|
+
server.timeout = 120000;
|
|
270
|
+
server.headersTimeout = 20000;
|
|
271
|
+
server.requestTimeout = 30000;
|
|
272
|
+
server.keepAliveTimeout = 5000;
|
|
273
|
+
|
|
274
|
+
server.listen(port);
|
|
275
|
+
|
|
276
|
+
await setupInitialModels();
|
|
277
|
+
await installAllPacks();
|
|
278
|
+
|
|
279
|
+
if (cb)
|
|
280
|
+
await cb();
|
|
281
|
+
|
|
282
|
+
engine.get('/api/health', (req, res) => {
|
|
283
|
+
res.status(200).json({
|
|
284
|
+
status: 'ok',
|
|
285
|
+
timestamp: new Date().toISOString()
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
if( fs.existsSync('client/dist') ){
|
|
290
|
+
app.use(sirv('client/dist', {
|
|
291
|
+
single: true,
|
|
292
|
+
dev: process.env.NODE_ENV === 'development'
|
|
293
|
+
}));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
process.on('uncaughtException', function (exception) {
|
|
297
|
+
console.error(exception);
|
|
298
|
+
fs.appendFile('issues.txt', JSON.stringify({ code: exception.code, message: exception.message, stack: exception.stack }), function (err) {
|
|
299
|
+
if (err){
|
|
300
|
+
throw err;
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
process.exit(1);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
await Event.Trigger("OnServerStart", "event", "system", engine);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
engine.stop = async () => {
|
|
310
|
+
await server.close();
|
|
311
|
+
await Event.Trigger("OnServerStop", "event", "system", engine);
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
async function setupInitialModels() {
|
|
315
|
+
logger.info("Validating structures of default models...");
|
|
316
|
+
const ms = Object.values(Config.Get('defaultModels', defaultModels));
|
|
317
|
+
|
|
318
|
+
let dbModels = await getModels();
|
|
319
|
+
|
|
320
|
+
for(let i = 0; i < ms.length; ++i){
|
|
321
|
+
const model = ms[i];
|
|
322
|
+
await validateModelStructure(model);
|
|
323
|
+
// Création des modèles
|
|
324
|
+
if( !dbModels.find(m =>m.name === model.name) )
|
|
325
|
+
{
|
|
326
|
+
model.locked = true;
|
|
327
|
+
const r = await createModel(model);
|
|
328
|
+
dbModels.push({...model, _id: r.insertedId });
|
|
329
|
+
logger.info(`Model ${model.name} inserted.`);
|
|
330
|
+
}else
|
|
331
|
+
logger.info(`Model ${model.name} loaded`);
|
|
332
|
+
}
|
|
333
|
+
logger.info("All models loaded.");
|
|
334
|
+
await Event.Trigger("OnModelsLoaded", "event", "system", engine, dbModels);
|
|
335
|
+
}
|
|
336
|
+
engine.resetModels = async () => {
|
|
337
|
+
await deleteModels();
|
|
338
|
+
await Event.Trigger("OnModelsDeleted", "event", "system", engine);
|
|
339
|
+
};
|
|
340
|
+
return engine;
|
|
341
|
+
}
|
|
342
|
+
}
|