data-primals-engine 1.5.1 → 1.6.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.
Files changed (70) hide show
  1. package/README.md +924 -913
  2. package/client/README.md +136 -136
  3. package/client/index.js +0 -1
  4. package/client/public/doc/API.postman_collection.json +213 -213
  5. package/client/public/react.svg +9 -9
  6. package/client/src/AddWidgetTypeModal.jsx +47 -47
  7. package/client/src/App.css +42 -42
  8. package/client/src/App.jsx +92 -150
  9. package/client/src/App.scss +7 -1
  10. package/client/src/AssistantChat.jsx +362 -363
  11. package/client/src/Button.jsx +12 -12
  12. package/client/src/Dashboard.jsx +480 -480
  13. package/client/src/DashboardHtmlViewItem.jsx +146 -146
  14. package/client/src/DashboardView.jsx +654 -654
  15. package/client/src/DataEditor.jsx +383 -383
  16. package/client/src/DataLayout.jsx +779 -806
  17. package/client/src/DataTable.jsx +782 -822
  18. package/client/src/DataTable.scss +186 -186
  19. package/client/src/GeolocationField.jsx +93 -93
  20. package/client/src/HistoryDialog.jsx +307 -285
  21. package/client/src/HistoryDialog.scss +319 -319
  22. package/client/src/HtmlViewBuilderModal.jsx +90 -90
  23. package/client/src/HtmlViewBuilderModal.scss +17 -17
  24. package/client/src/HtmlViewCard.jsx +43 -43
  25. package/client/src/ModelCreatorField.jsx +950 -950
  26. package/client/src/ModelList.jsx +7 -2
  27. package/client/src/Notification.jsx +136 -136
  28. package/client/src/PackGallery.jsx +391 -391
  29. package/client/src/PackGallery.scss +231 -231
  30. package/client/src/Pagination.jsx +143 -143
  31. package/client/src/RelationField.jsx +354 -354
  32. package/client/src/RelationSelectorWidget.jsx +172 -172
  33. package/client/src/RelationValue.jsx +2 -1
  34. package/client/src/contexts/CommandContext.jsx +260 -0
  35. package/client/src/contexts/ModelContext.jsx +4 -1
  36. package/client/src/contexts/UIContext.jsx +72 -72
  37. package/client/src/filter.js +263 -262
  38. package/client/src/index.css +90 -90
  39. package/package.json +11 -10
  40. package/perf/artillery-hooks.js +37 -37
  41. package/perf/setup.yml +25 -25
  42. package/src/constants.js +4 -0
  43. package/src/core.js +8 -1
  44. package/src/engine.js +335 -293
  45. package/src/events.js +140 -21
  46. package/src/filter.js +274 -274
  47. package/src/index.js +3 -0
  48. package/src/modules/assistant/assistant.js +323 -192
  49. package/src/modules/assistant/constants.js +2 -1
  50. package/src/modules/auth-google/index.js +50 -50
  51. package/src/modules/auth-microsoft/index.js +81 -81
  52. package/src/modules/data/data.core.js +118 -118
  53. package/src/modules/data/data.history.js +555 -531
  54. package/src/modules/data/data.operations.js +145 -26
  55. package/src/modules/data/data.relations.js +686 -686
  56. package/src/modules/data/data.routes.js +1879 -1879
  57. package/src/modules/data/data.validation.js +2 -2
  58. package/src/modules/file.js +247 -247
  59. package/src/modules/user.js +1 -0
  60. package/src/modules/workflow.js +2 -2
  61. package/src/openai.jobs.js +3 -2
  62. package/src/packs.js +2 -2
  63. package/src/providers.js +2 -2
  64. package/src/services/stripe.js +196 -196
  65. package/src/sso.js +193 -193
  66. package/src/workers/import-export-worker.js +1 -1
  67. package/test/data.history.integration.test.js +72 -0
  68. package/test/data.integration.test.js +92 -1
  69. package/test/events.test.js +108 -10
  70. package/test/model.integration.test.js +377 -377
package/src/engine.js CHANGED
@@ -1,293 +1,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 } 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 = MongoClient.db(dbName);
98
-
99
-
100
- export const Engine = {
101
- Create: async (options = { app : null}) => {
102
- // On injecte la dépendance safe-regex dans le module de filtrage au tout début.
103
- setSafeRegex(safeRegexCallback);
104
-
105
- const engine = GameObject.Create("Engine");
106
- console.log("Creating engine", Config.Get('modules'));
107
- const logger = engine.addComponent(Logger);
108
-
109
- engine.userProvider = new DefaultUserProvider(engine);
110
-
111
- engine.setUserProvider = (providerInstance) => {
112
- engine.userProvider = providerInstance;
113
- logger.info(`Custom UserProvider '${providerInstance.constructor.name}' has been set.`);
114
- };
115
-
116
- if (!options.app) {
117
- options.app = express();
118
- }
119
-
120
- const { app } = options;
121
- // Allows you to set port in the project properties.
122
- app.set('port', process.env.PORT || 3000);
123
- app.set('engine', engine);
124
-
125
- app.use(formidableMiddleware({
126
- encoding: 'utf-8',
127
- uploadDir: process.cwd()+'/uploads/tmp',
128
- multiples: true // req.files to be arrays of files
129
- }));
130
-
131
- const cs = Config.Get('cookieSecret', process.env.COOKIES_SECRET || cookiesSecret)
132
- app.use(cookieParser(cs));
133
- app.use(requestIp.mw())
134
-
135
- engine.use = (...args) => {
136
- return app.use(...args);
137
- }
138
- engine.post = (...args) => {
139
- return app.post(...args);
140
- };
141
- engine.get = (...args) => {
142
- return app.get(...args);
143
- };
144
- engine.delete = (...args) => {
145
- return app.delete(...args);
146
- };
147
- engine.patch = (...args) => {
148
- return app.patch(...args);
149
- };
150
- engine.put = (...args) => {
151
- return app.put(...args);
152
- };
153
- engine.all = (...args) => {
154
- return app.all(...args);
155
- };
156
- engine.getModule = (module) => {
157
- return engine._modules.find(m => m.module === module);
158
- };
159
-
160
- const importModule = async (module) => {
161
- const moduleA = await import(module);
162
- if (moduleA.onInit){
163
- await moduleA.onInit(engine);
164
- return {...moduleA, module};
165
- }else {
166
- const mod = moduleA.default();
167
- await mod?.onInit(engine);
168
- return { ...mod, module};
169
- }
170
- };
171
-
172
- engine._modules = [];
173
- for (const moduleIdentifier of Config.Get('modules', [])) {
174
- try {
175
- let moduleDir;
176
- const moduleName = path.basename(moduleIdentifier);
177
-
178
- const directPath = path.resolve(moduleIdentifier);
179
- let isDir = fs.existsSync(directPath) && fs.statSync(directPath).isDirectory();
180
-
181
- if (isDir) {
182
- moduleDir = directPath;
183
- } else {
184
- moduleDir = path.resolve(__dirname, 'modules', moduleIdentifier);
185
- }
186
-
187
- let moduleEntryPoint;
188
- const jsPath = moduleDir + '.js';
189
- const indexJsPath = path.join(moduleDir, 'index.js');
190
- const moduleJsPath = path.join(moduleDir, `${moduleName}.js`);
191
-
192
- if (fs.existsSync(jsPath)) {
193
- moduleEntryPoint = 'file://' + jsPath;
194
- } else if (fs.existsSync(indexJsPath)) {
195
- moduleEntryPoint = 'file://' + indexJsPath;
196
- } else if (fs.existsSync(moduleJsPath)) {
197
- moduleEntryPoint = 'file://' + moduleJsPath;
198
- }
199
-
200
- if (moduleEntryPoint) {
201
- const loadedModule = await importModule(moduleEntryPoint);
202
- if (loadedModule) {
203
- engine._modules.push(loadedModule);
204
- }
205
- } else {
206
- logger.warn(`Aucun point d'entrée trouvé pour le module '${moduleIdentifier}'.`);
207
- }
208
- } catch (e) {
209
- logger.error(`Could not load module '${moduleIdentifier}':`, e.stack);
210
- }
211
- }
212
-
213
- let server;
214
-
215
- engine.start = async (port, cb) =>{
216
- // Use connect method to connect to the server
217
-
218
- // Start http server
219
- server = http.createServer(app);
220
-
221
- // Server Timeout Settings
222
- server.timeout = 120000;
223
- server.headersTimeout = 20000;
224
- server.requestTimeout = 30000;
225
- server.keepAliveTimeout = 5000;
226
-
227
- server.listen(port);
228
-
229
- await setupInitialModels();
230
- await installAllPacks();
231
-
232
- if (cb)
233
- await cb();
234
-
235
- engine.get('/api/health', (req, res) => {
236
- res.status(200).json({
237
- status: 'ok',
238
- timestamp: new Date().toISOString()
239
- });
240
- });
241
-
242
- app.use(sirv('client/dist', {
243
- single: true,
244
- dev: process.env.NODE_ENV === 'development'
245
- }));
246
-
247
- process.on('uncaughtException', function (exception) {
248
- console.error(exception);
249
- fs.appendFile('issues.txt', JSON.stringify({ code: exception.code, message: exception.message, stack: exception.stack }), function (err) {
250
- if (err){
251
- throw err;
252
- }
253
- });
254
- process.exit(1);
255
- });
256
-
257
- await Event.Trigger("OnServerStart", "event", "system", engine);
258
- }
259
-
260
- engine.stop = async () => {
261
- await server.close();
262
- await Event.Trigger("OnServerStop", "event", "system", engine);
263
- };
264
-
265
- async function setupInitialModels() {
266
- logger.info("Validating structures of default models...");
267
- const ms = Object.values(Config.Get('defaultModels', defaultModels));
268
-
269
- let dbModels = await getModels();
270
-
271
- for(let i = 0; i < ms.length; ++i){
272
- const model = ms[i];
273
- await validateModelStructure(model);
274
- // Création des modèles
275
- if( !dbModels.find(m =>m.name === model.name) )
276
- {
277
- model.locked = true;
278
- const r = await createModel(model);
279
- dbModels.push({...model, _id: r.insertedId });
280
- logger.info('Model inserted (' + model.name + ')');
281
- }else
282
- logger.info('Model loaded (' + model.name + ')');
283
- }
284
- logger.info("All models loaded.");
285
- await Event.Trigger("OnModelsLoaded", "event", "system", engine, dbModels);
286
- }
287
- engine.resetModels = async () => {
288
- await deleteModels();
289
- await Event.Trigger("OnModelsDeleted", "event", "system", engine);
290
- };
291
- return engine;
292
- }
293
- }
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 = MongoClient.db(dbName);
98
+
99
+
100
+ export const Engine = {
101
+ Create: async (options = { app : null}) => {
102
+ // On injecte la dépendance safe-regex dans le module de filtrage au tout début.
103
+ setSafeRegex(safeRegexCallback);
104
+
105
+ const engine = GameObject.Create("Engine");
106
+ console.log("Creating engine", Config.Get('modules'));
107
+ const logger = engine.addComponent(Logger);
108
+
109
+ // Expose the Event bus on the engine instance for dependency injection
110
+ engine.Event = Event;
111
+
112
+ engine.userProvider = new DefaultUserProvider(engine);
113
+
114
+ engine.setUserProvider = (providerInstance) => {
115
+ engine.userProvider = providerInstance;
116
+ logger.info(`Custom UserProvider '${providerInstance.constructor.name}' has been set.`);
117
+ };
118
+
119
+ if (!options.app) {
120
+ options.app = express();
121
+ }
122
+
123
+ const { app } = options;
124
+ // Allows you to set port in the project properties.
125
+ app.set('port', process.env.PORT || 3000);
126
+ app.set('engine', engine);
127
+
128
+ app.use(formidableMiddleware({
129
+ encoding: 'utf-8',
130
+ uploadDir: process.cwd()+'/uploads/tmp',
131
+ multiples: true // req.files to be arrays of files
132
+ }));
133
+
134
+ const cs = Config.Get('cookieSecret', process.env.COOKIES_SECRET || cookiesSecret)
135
+ app.use(cookieParser(cs));
136
+ app.use(requestIp.mw())
137
+
138
+ engine.use = (...args) => {
139
+ return app.use(...args);
140
+ }
141
+ engine.post = (...args) => {
142
+ return app.post(...args);
143
+ };
144
+ engine.get = (...args) => {
145
+ return app.get(...args);
146
+ };
147
+ engine.delete = (...args) => {
148
+ return app.delete(...args);
149
+ };
150
+ engine.patch = (...args) => {
151
+ return app.patch(...args);
152
+ };
153
+ engine.put = (...args) => {
154
+ return app.put(...args);
155
+ };
156
+ engine.all = (...args) => {
157
+ return app.all(...args);
158
+ };
159
+ engine.getModule = (module) => {
160
+ return engine._modules.find(m => m.module === module);
161
+ };
162
+
163
+ const importAndPrepareModule = async (moduleEntryPoint, moduleName) => {
164
+ const moduleA = await import(moduleEntryPoint);
165
+
166
+ let moduleInstance = null;
167
+ let onInitFunction = null;
168
+
169
+ // Cas 1: `export default { onInit }`
170
+ if (moduleA.default && typeof moduleA.default.onInit === 'function') {
171
+ moduleInstance = moduleA.default;
172
+ onInitFunction = moduleA.default.onInit;
173
+ }
174
+ // Cas 2: `export async function onInit() {}`
175
+ else if (typeof moduleA.onInit === 'function') {
176
+ moduleInstance = moduleA;
177
+ onInitFunction = moduleA.onInit;
178
+ }
179
+
180
+ if (moduleInstance) {
181
+ // On stocke la fonction onInit pour plus tard et on retourne l'instance
182
+ // avec le nom court du module.
183
+ return { ...moduleInstance, onInit: onInitFunction, module: moduleName };
184
+ }
185
+
186
+ logger.warn(`Module loaded from ${moduleEntryPoint} does not export an onInit function or a default object with onInit.`);
187
+ return null;
188
+ };
189
+
190
+ engine._modules = [];
191
+
192
+ // On charge uniquement les modules spécifiés dans la configuration.
193
+ const allModules = Config.Get('modules', []);
194
+ const loadedModules = []; // Liste temporaire pour la phase 1
195
+ for (const moduleIdentifier of allModules) {
196
+ let moduleEntryPoint = null;
197
+ const moduleName = path.basename(moduleIdentifier, '.js');
198
+ try {
199
+ // 1. Tenter de résoudre comme un chemin (relatif au projet ou absolu)
200
+ const externalPath = path.resolve(process.cwd(), moduleIdentifier);
201
+ if (fs.existsSync(externalPath)) {
202
+ const stats = fs.statSync(externalPath);
203
+ if (stats.isDirectory()) {
204
+ // C'est un répertoire, on cherche le point d'entrée
205
+ const indexJsPath = path.join(externalPath, 'index.js');
206
+ const moduleJsPath = path.join(externalPath, `${moduleName}.js`);
207
+ if (fs.existsSync(indexJsPath)) moduleEntryPoint = pathToFileURL(indexJsPath).href;
208
+ else if (fs.existsSync(moduleJsPath)) moduleEntryPoint = pathToFileURL(moduleJsPath).href;
209
+ } else {
210
+ // C'est un fichier
211
+ moduleEntryPoint = pathToFileURL(externalPath).href;
212
+ }
213
+ } else {
214
+ // 2. Si ce n'est pas un chemin, tenter de résoudre comme un module interne
215
+ const internalDir = path.resolve(__dirname, 'modules', moduleIdentifier);
216
+ const internalFile = path.resolve(__dirname, 'modules', `${moduleIdentifier}.js`);
217
+
218
+ if (fs.existsSync(internalDir) && fs.statSync(internalDir).isDirectory()) {
219
+ // C'est un répertoire de module interne
220
+ const indexJsPath = path.join(internalDir, 'index.js');
221
+ const moduleJsPath = path.join(internalDir, `${moduleIdentifier}.js`);
222
+ if (fs.existsSync(indexJsPath)) moduleEntryPoint = pathToFileURL(indexJsPath).href;
223
+ else if (fs.existsSync(moduleJsPath)) moduleEntryPoint = pathToFileURL(moduleJsPath).href;
224
+ } else if (fs.existsSync(internalFile)) {
225
+ // C'est un fichier de module interne
226
+ moduleEntryPoint = pathToFileURL(internalFile).href;
227
+ }
228
+ }
229
+
230
+ if (moduleEntryPoint) {
231
+ const loadedModule = await importAndPrepareModule(moduleEntryPoint, moduleName);
232
+ if (loadedModule) {
233
+ loadedModules.push(loadedModule);
234
+ }
235
+ } else {
236
+ logger.warn(`Could not resolve or find an entry point for module '${moduleIdentifier}'.`);
237
+ }
238
+ } catch (e) {
239
+ logger.error(`Could not load module '${moduleName}' (${moduleIdentifier}):`, e.stack);
240
+ }
241
+ }
242
+
243
+ // Phase 2: Enregistrer et Initialiser tous les modules chargés
244
+ engine._modules = loadedModules; // On enregistre tous les modules dans le moteur
245
+ for (const moduleInstance of engine._modules) {
246
+ if (typeof moduleInstance.onInit === 'function') {
247
+ logger.info(`Initializing module '${moduleInstance.module}'...`);
248
+ await moduleInstance.onInit(engine);
249
+ logger.info(`Module '${moduleInstance.module}' loaded and initialized.`);
250
+ }
251
+ }
252
+
253
+ let server;
254
+
255
+ engine.start = async (port, cb) =>{
256
+ // Use connect method to connect to the server
257
+
258
+ // Start http server
259
+ server = http.createServer(app);
260
+
261
+ // Server Timeout Settings
262
+ server.timeout = 120000;
263
+ server.headersTimeout = 20000;
264
+ server.requestTimeout = 30000;
265
+ server.keepAliveTimeout = 5000;
266
+
267
+ server.listen(port);
268
+
269
+ await setupInitialModels();
270
+ await installAllPacks();
271
+
272
+ if (cb)
273
+ await cb();
274
+
275
+ engine.get('/api/health', (req, res) => {
276
+ res.status(200).json({
277
+ status: 'ok',
278
+ timestamp: new Date().toISOString()
279
+ });
280
+ });
281
+
282
+ if( fs.existsSync('client/dist') ){
283
+ app.use(sirv('client/dist', {
284
+ single: true,
285
+ dev: process.env.NODE_ENV === 'development'
286
+ }));
287
+ }
288
+
289
+ process.on('uncaughtException', function (exception) {
290
+ console.error(exception);
291
+ fs.appendFile('issues.txt', JSON.stringify({ code: exception.code, message: exception.message, stack: exception.stack }), function (err) {
292
+ if (err){
293
+ throw err;
294
+ }
295
+ });
296
+ process.exit(1);
297
+ });
298
+
299
+ await Event.Trigger("OnServerStart", "event", "system", engine);
300
+ }
301
+
302
+ engine.stop = async () => {
303
+ await server.close();
304
+ await Event.Trigger("OnServerStop", "event", "system", engine);
305
+ };
306
+
307
+ async function setupInitialModels() {
308
+ logger.info("Validating structures of default models...");
309
+ const ms = Object.values(Config.Get('defaultModels', defaultModels));
310
+
311
+ let dbModels = await getModels();
312
+
313
+ for(let i = 0; i < ms.length; ++i){
314
+ const model = ms[i];
315
+ await validateModelStructure(model);
316
+ // Création des modèles
317
+ if( !dbModels.find(m =>m.name === model.name) )
318
+ {
319
+ model.locked = true;
320
+ const r = await createModel(model);
321
+ dbModels.push({...model, _id: r.insertedId });
322
+ logger.info(`Model ${model.name} inserted.`);
323
+ }else
324
+ logger.info(`Model ${model.name} loaded`);
325
+ }
326
+ logger.info("All models loaded.");
327
+ await Event.Trigger("OnModelsLoaded", "event", "system", engine, dbModels);
328
+ }
329
+ engine.resetModels = async () => {
330
+ await deleteModels();
331
+ await Event.Trigger("OnModelsDeleted", "event", "system", engine);
332
+ };
333
+ return engine;
334
+ }
335
+ }