data-primals-engine 1.6.5 → 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/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 = 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
- }
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
+ }