@reldens/cms 0.27.0 → 0.29.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 +117 -13
- package/bin/reldens-cms-generate-entities.js +7 -2
- package/bin/reldens-cms.js +154 -50
- package/install/css/installer.css +1 -0
- package/install/index.html +128 -124
- package/lib/frontend.js +274 -268
- package/lib/installer.js +636 -497
- package/lib/manager.js +540 -507
- package/lib/mysql-installer.js +93 -0
- package/lib/prisma-subprocess-worker.js +97 -0
- package/lib/template-engine/asset-transformer.js +43 -41
- package/lib/template-engine/date-transformer.js +4 -5
- package/lib/template-engine/system-variables-provider.js +127 -108
- package/lib/template-engine/url-transformer.js +41 -41
- package/lib/template-engine.js +223 -213
- package/migrations/default-homepage.sql +1 -1
- package/package.json +5 -5
- package/templates/.env.dist +1 -0
- package/templates/index.js.dist +30 -32
- package/templates/js/cookie-consent.js +411 -0
package/lib/installer.js
CHANGED
|
@@ -1,497 +1,636 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
* Reldens - CMS - Installer
|
|
4
|
-
*
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const {
|
|
8
|
-
const {
|
|
9
|
-
const {
|
|
10
|
-
const {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
this.
|
|
20
|
-
this.
|
|
21
|
-
this.
|
|
22
|
-
this.
|
|
23
|
-
this.
|
|
24
|
-
this.
|
|
25
|
-
this.
|
|
26
|
-
this.
|
|
27
|
-
this.
|
|
28
|
-
this.
|
|
29
|
-
this.
|
|
30
|
-
this.
|
|
31
|
-
this.
|
|
32
|
-
this.
|
|
33
|
-
this.
|
|
34
|
-
this.
|
|
35
|
-
this.
|
|
36
|
-
this.
|
|
37
|
-
this.
|
|
38
|
-
this.
|
|
39
|
-
this.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
this.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
});
|
|
78
|
-
app.
|
|
79
|
-
return await this.
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return res.
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
'
|
|
116
|
-
'
|
|
117
|
-
'
|
|
118
|
-
'
|
|
119
|
-
'sql-
|
|
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
|
-
if(
|
|
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
|
-
return
|
|
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
|
-
if(
|
|
314
|
-
Logger.
|
|
315
|
-
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
if(!
|
|
381
|
-
Logger.error('
|
|
382
|
-
return false;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
{
|
|
407
|
-
return
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
)
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
);
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
FileHandler.
|
|
444
|
-
|
|
445
|
-
'
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
);
|
|
469
|
-
FileHandler.copyFolderSync(
|
|
470
|
-
|
|
471
|
-
FileHandler.joinPaths(this.
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
FileHandler.joinPaths(this.
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
'
|
|
492
|
-
'
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - CMS - Installer
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { fork, execSync } = require('child_process');
|
|
8
|
+
const { EntitiesLoader } = require('./entities-loader');
|
|
9
|
+
const { MySQLInstaller } = require('./mysql-installer');
|
|
10
|
+
const { FileHandler, Encryptor } = require('@reldens/server-utils');
|
|
11
|
+
const { DriversMap, DriversClassMap, EntitiesGenerator, PrismaSchemaGenerator } = require('@reldens/storage');
|
|
12
|
+
const { Logger, sc } = require('@reldens/utils');
|
|
13
|
+
|
|
14
|
+
class Installer
|
|
15
|
+
{
|
|
16
|
+
|
|
17
|
+
constructor(props)
|
|
18
|
+
{
|
|
19
|
+
this.app = sc.get(props, 'app', false);
|
|
20
|
+
this.appServer = sc.get(props, 'appServer', false);
|
|
21
|
+
this.appServerFactory = sc.get(props, 'appServerFactory', false);
|
|
22
|
+
this.renderEngine = sc.get(props, 'renderEngine', false);
|
|
23
|
+
this.projectRoot = sc.get(props, 'projectRoot', './');
|
|
24
|
+
this.projectTemplatesPath = FileHandler.joinPaths(this.projectRoot, 'templates');
|
|
25
|
+
this.projectPublicPath = FileHandler.joinPaths(this.projectRoot, 'public');
|
|
26
|
+
this.projectPublicAssetsPath = FileHandler.joinPaths(this.projectPublicPath, 'assets');
|
|
27
|
+
this.projectCssPath = FileHandler.joinPaths(this.projectPublicPath, 'css');
|
|
28
|
+
this.projectJsPath = FileHandler.joinPaths(this.projectPublicPath, 'js');
|
|
29
|
+
this.installLockPath = FileHandler.joinPaths(this.projectRoot, 'install.lock');
|
|
30
|
+
this.envFilePath = FileHandler.joinPaths(this.projectRoot, '.env');
|
|
31
|
+
this.modulePath = FileHandler.joinPaths(__dirname, '..');
|
|
32
|
+
this.installerPath = FileHandler.joinPaths(this.modulePath, 'install');
|
|
33
|
+
this.migrationsPath = FileHandler.joinPaths(this.modulePath, 'migrations');
|
|
34
|
+
this.defaultTemplatesPath = FileHandler.joinPaths(this.modulePath, 'templates');
|
|
35
|
+
this.moduleAdminPath = FileHandler.joinPaths(this.modulePath, 'admin');
|
|
36
|
+
this.moduleAdminAssetsPath = FileHandler.joinPaths(this.moduleAdminPath, 'assets');
|
|
37
|
+
this.moduleAdminTemplatesPath = FileHandler.joinPaths(this.moduleAdminPath, 'templates');
|
|
38
|
+
this.indexTemplatePath = FileHandler.joinPaths(this.defaultTemplatesPath, 'index.js.dist');
|
|
39
|
+
this.postInstallCallback = sc.get(props, 'postInstallCallback', false);
|
|
40
|
+
this.prismaClient = sc.get(props, 'prismaClient', false);
|
|
41
|
+
this.entitiesLoader = new EntitiesLoader({projectRoot: this.projectRoot});
|
|
42
|
+
this.subprocessMaxAttempts = sc.get(props, 'subprocessMaxAttempts', 1800);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
isInstalled()
|
|
46
|
+
{
|
|
47
|
+
return FileHandler.exists(this.installLockPath);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async configureAppServerRoutes(app, appServer, appServerFactory, renderEngine)
|
|
51
|
+
{
|
|
52
|
+
if(!app){
|
|
53
|
+
Logger.error('Missing app on configureAppServerRoutes for Installer.');
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
if(!appServer){
|
|
57
|
+
Logger.error('Missing appServer on configureAppServerRoutes for Installer.');
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
if(!appServerFactory){
|
|
61
|
+
Logger.error('Missing appServerFactory on configureAppServerRoutes for Installer.');
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
if(!renderEngine){
|
|
65
|
+
Logger.error('Missing renderEngine on configureAppServerRoutes for Installer.');
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
this.app = app;
|
|
69
|
+
this.appServer = appServer;
|
|
70
|
+
this.appServerFactory = appServerFactory;
|
|
71
|
+
this.renderEngine = renderEngine;
|
|
72
|
+
app.use('/install-assets', appServerFactory.applicationFramework.static(this.installerPath, {index: false}));
|
|
73
|
+
app.use(appServerFactory.session({
|
|
74
|
+
secret: Encryptor.generateSecretKey(),
|
|
75
|
+
resave: true,
|
|
76
|
+
saveUninitialized: true
|
|
77
|
+
}));
|
|
78
|
+
app.use(async (req, res, next) => {
|
|
79
|
+
return await this.executeForEveryRequest(req, res, next);
|
|
80
|
+
});
|
|
81
|
+
app.post('/install', async (req, res) => {
|
|
82
|
+
return await this.executeInstallProcess(req, res);
|
|
83
|
+
});
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async executeForEveryRequest(req, res, next)
|
|
88
|
+
{
|
|
89
|
+
if(this.isInstalled()){
|
|
90
|
+
return next();
|
|
91
|
+
}
|
|
92
|
+
let urlPath = req._parsedUrl.pathname;
|
|
93
|
+
if('' === urlPath || '/' === urlPath){
|
|
94
|
+
let installerIndexPath = FileHandler.joinPaths(this.installerPath, 'index.html');
|
|
95
|
+
if(!FileHandler.exists(installerIndexPath)){
|
|
96
|
+
return res.status(500).send('Installer template not found.');
|
|
97
|
+
}
|
|
98
|
+
let content = FileHandler.readFile(installerIndexPath);
|
|
99
|
+
let contentParams = req.session?.templateVariables || this.fetchDefaults();
|
|
100
|
+
let errorParam = req.query?.error;
|
|
101
|
+
if(errorParam){
|
|
102
|
+
contentParams.errorMessage = this.getErrorMessage(errorParam);
|
|
103
|
+
}
|
|
104
|
+
return res.send(this.renderEngine.render(content, contentParams));
|
|
105
|
+
}
|
|
106
|
+
if('/install' !== urlPath){
|
|
107
|
+
return res.redirect('/');
|
|
108
|
+
}
|
|
109
|
+
next();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
getErrorMessage(errorCode)
|
|
113
|
+
{
|
|
114
|
+
let errorMessages = {
|
|
115
|
+
'invalid-driver': 'Invalid storage driver selected.',
|
|
116
|
+
'installation-dependencies-failed': 'Required dependencies failed to install.',
|
|
117
|
+
'connection-failed': 'Database connection failed. Please check your credentials.',
|
|
118
|
+
'raw-query-not-found': 'Query method not found in driver.',
|
|
119
|
+
'sql-file-not-found': 'SQL installation file not found.',
|
|
120
|
+
'sql-cms-tables-creation-failed': 'Failed to create CMS tables.',
|
|
121
|
+
'sql-user-auth-creation-failed': 'Failed to create user authentication tables.',
|
|
122
|
+
'sql-default-user-error': 'Failed to create default user.',
|
|
123
|
+
'sql-default-homepage-error': 'Failed to create default homepage.',
|
|
124
|
+
'installation-entities-generation-failed': 'Failed to generate entities.',
|
|
125
|
+
'installation-entities-callback-failed': 'Failed to process entities for callback.',
|
|
126
|
+
'configuration-error': 'Configuration error while completing installation.',
|
|
127
|
+
'prisma-generation-subprocess-failed': 'Prisma generation subprocess failed.',
|
|
128
|
+
'temporal-mysql-only-supported': 'Only MySQL is supported at installation time.',
|
|
129
|
+
'already-installed': 'The application is already installed.'
|
|
130
|
+
};
|
|
131
|
+
return sc.get(errorMessages, errorCode, 'An unknown error occurred during installation.');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async executeInstallProcess(req, res)
|
|
135
|
+
{
|
|
136
|
+
if(this.isInstalled()){
|
|
137
|
+
return res.redirect('/?redirect=already-installed');
|
|
138
|
+
}
|
|
139
|
+
// map database configuration variables:
|
|
140
|
+
let templateVariables = req.body;
|
|
141
|
+
req.session.templateVariables = templateVariables;
|
|
142
|
+
let selectedDriver = templateVariables['db-storage-driver'];
|
|
143
|
+
let driverClass = DriversMap[selectedDriver];
|
|
144
|
+
if(!driverClass){
|
|
145
|
+
Logger.error('Invalid storage driver: ' + selectedDriver);
|
|
146
|
+
return res.redirect('/?error=invalid-driver');
|
|
147
|
+
}
|
|
148
|
+
let dbConfig = {
|
|
149
|
+
client: sc.get(templateVariables, 'db-client', 'mysql'),
|
|
150
|
+
config: {
|
|
151
|
+
host: templateVariables['db-host'],
|
|
152
|
+
port: Number(templateVariables['db-port']),
|
|
153
|
+
database: templateVariables['db-name'],
|
|
154
|
+
user: templateVariables['db-username'],
|
|
155
|
+
password: templateVariables['db-password'],
|
|
156
|
+
multipleStatements: true
|
|
157
|
+
},
|
|
158
|
+
debug: false
|
|
159
|
+
};
|
|
160
|
+
if(!await this.checkAndInstallPackages(['@reldens/cms'])){
|
|
161
|
+
Logger.error('Required @reldens/cms dependency installation failed.');
|
|
162
|
+
return res.redirect('/?error=installation-dependencies-failed');
|
|
163
|
+
}
|
|
164
|
+
if('prisma' === selectedDriver){
|
|
165
|
+
if(!await this.checkAndInstallPackages(['@prisma/client'])){
|
|
166
|
+
Logger.error('Required @prisma/client dependency installation failed.');
|
|
167
|
+
return res.redirect('/?error=installation-dependencies-failed');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
let dbDriver = new driverClass(dbConfig);
|
|
171
|
+
if(!sc.isObjectFunction(dbDriver, 'rawQuery')){
|
|
172
|
+
Logger.error('Method "rawQuery" not found in driver.', driverClass);
|
|
173
|
+
return res.redirect('/?error=raw-query-not-found');
|
|
174
|
+
}
|
|
175
|
+
let queryFilesResult = await this.executeQueryFiles(selectedDriver, dbDriver, dbConfig, templateVariables);
|
|
176
|
+
if('' !== queryFilesResult){
|
|
177
|
+
Logger.critical('Invalid query result: ' + queryFilesResult);
|
|
178
|
+
if('prisma' === selectedDriver){
|
|
179
|
+
res.redirect('/?'+queryFilesResult);
|
|
180
|
+
res.on('finish', () => { process.exit(); });
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
return res.redirect('/?'+queryFilesResult);
|
|
184
|
+
}
|
|
185
|
+
// @IMPORTANT: if the selectedDriver is 'prisma', then at this point the executeQueryFiles already created the
|
|
186
|
+
// client through the sub-process worker.
|
|
187
|
+
// Search: FileHandler.joinPaths(this.projectRoot, 'prisma', 'client')
|
|
188
|
+
let entitiesGenerationResult = await this.generateEntities(dbDriver, false, true, false, dbConfig);
|
|
189
|
+
if(!entitiesGenerationResult){
|
|
190
|
+
Logger.error('Entities generation error.');
|
|
191
|
+
if('prisma' === selectedDriver){
|
|
192
|
+
res.redirect('/?error=installation-entities-generation-failed');
|
|
193
|
+
res.on('finish', () => { process.exit(); });
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
return res.redirect('/?error=installation-entities-generation-failed');
|
|
197
|
+
}
|
|
198
|
+
Logger.info('Generated entities.');
|
|
199
|
+
try {
|
|
200
|
+
let mappedVariablesForConfig = this.mapVariablesForConfig(templateVariables);
|
|
201
|
+
await this.createEnvFile(this.mapVariablesForTemplate(mappedVariablesForConfig));
|
|
202
|
+
await this.prepareProjectDirectories();
|
|
203
|
+
await this.copyAdminDirectory();
|
|
204
|
+
await this.createIndexJsFile(templateVariables);
|
|
205
|
+
if(sc.isFunction(this.postInstallCallback)){
|
|
206
|
+
if(this.appServer && sc.isFunction(this.appServer.close)){
|
|
207
|
+
// @TODO - CHECK IF THIS SHOULD BE REMOVED.
|
|
208
|
+
await this.appServer.close();
|
|
209
|
+
}
|
|
210
|
+
Logger.debug('Running postInstallCallback.');
|
|
211
|
+
let callbackResult = await this.postInstallCallback({
|
|
212
|
+
loadedEntities: this.entitiesLoader.loadEntities(selectedDriver),
|
|
213
|
+
mappedVariablesForConfig,
|
|
214
|
+
dataServer: dbDriver
|
|
215
|
+
});
|
|
216
|
+
if(false === callbackResult){
|
|
217
|
+
Logger.error('Post-install callback failed.');
|
|
218
|
+
if('prisma' === selectedDriver){
|
|
219
|
+
res.redirect('/?error=installation-entities-callback-failed');
|
|
220
|
+
res.on('finish', () => { process.exit(); });
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
return res.redirect('/?error=installation-entities-callback-failed');
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
await this.createLockFile();
|
|
227
|
+
Logger.info('Installation successful!');
|
|
228
|
+
let successContent = 'Installation successful! Run "node ." to start your CMS.';
|
|
229
|
+
let successFileContent = FileHandler.readFile(FileHandler.joinPaths(this.installerPath, 'success.html'));
|
|
230
|
+
if(successFileContent){
|
|
231
|
+
successContent = this.renderEngine.render(
|
|
232
|
+
successFileContent,
|
|
233
|
+
{adminPath: templateVariables['app-admin-path']}
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
return res.send(successContent);
|
|
237
|
+
} catch (error) {
|
|
238
|
+
Logger.critical('Configuration error: '+error.message);
|
|
239
|
+
if('prisma' === selectedDriver){
|
|
240
|
+
res.redirect('/?error=configuration-error');
|
|
241
|
+
res.on('finish', () => { process.exit(); });
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
return res.redirect('/?error=configuration-error');
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async executeQueryFiles(selectedDriver, dbDriver, dbConfig, templateVariables)
|
|
249
|
+
{
|
|
250
|
+
if('prisma' === selectedDriver){
|
|
251
|
+
let subProcessResult = await this.runSubprocessInstallation(dbConfig, templateVariables);
|
|
252
|
+
if(!subProcessResult){
|
|
253
|
+
return 'error=prisma-generation-subprocess-failed';
|
|
254
|
+
}
|
|
255
|
+
return '';
|
|
256
|
+
}
|
|
257
|
+
if(!await dbDriver.connect()){
|
|
258
|
+
Logger.error('Connection failed');
|
|
259
|
+
return 'error=connection-failed';
|
|
260
|
+
}
|
|
261
|
+
if(-1 === dbConfig.client.indexOf('mysql')){
|
|
262
|
+
return 'error=temporal-mysql-only-supported';
|
|
263
|
+
}
|
|
264
|
+
let migrationFiles = MySQLInstaller.migrationFiles();
|
|
265
|
+
for(let checkboxName of Object.keys(migrationFiles)){
|
|
266
|
+
let fileName = migrationFiles[checkboxName];
|
|
267
|
+
let redirectError = await MySQLInstaller.executeQueryFile(
|
|
268
|
+
sc.get(templateVariables, checkboxName, 'off'),
|
|
269
|
+
fileName,
|
|
270
|
+
dbDriver,
|
|
271
|
+
this.migrationsPath
|
|
272
|
+
);
|
|
273
|
+
if('' !== redirectError){
|
|
274
|
+
return redirectError;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return '';
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
async generateEntities(
|
|
281
|
+
server,
|
|
282
|
+
isOverride = false,
|
|
283
|
+
isInstallationMode = false,
|
|
284
|
+
isDryPrisma = false,
|
|
285
|
+
dbConfig = null
|
|
286
|
+
){
|
|
287
|
+
let driverType = sc.get(DriversClassMap, server.constructor.name, '');
|
|
288
|
+
Logger.debug('Driver type detected: '+driverType+', Server constructor: '+server.constructor.name);
|
|
289
|
+
if('prisma' === driverType && !isDryPrisma){
|
|
290
|
+
Logger.info('Running prisma introspect "npx prisma db pull"...');
|
|
291
|
+
if(!dbConfig){
|
|
292
|
+
dbConfig = this.extractDbConfigFromServer(server);
|
|
293
|
+
Logger.debug('Extracted DB config.');
|
|
294
|
+
}
|
|
295
|
+
Logger.debug('DB config:', dbConfig);
|
|
296
|
+
if(dbConfig){
|
|
297
|
+
let generatedPrismaSchema = await this.generatePrismaSchema(dbConfig);
|
|
298
|
+
if(!generatedPrismaSchema){
|
|
299
|
+
Logger.error('Prisma schema generation failed.');
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
Logger.info('Generated Prisma schema for entities generation.');
|
|
303
|
+
if(isInstallationMode){
|
|
304
|
+
Logger.info('Creating local Prisma client for entities generation...');
|
|
305
|
+
let localPrismaClient = await MySQLInstaller.createPrismaClient(this.projectRoot);
|
|
306
|
+
if(localPrismaClient){
|
|
307
|
+
this.prismaClient = localPrismaClient;
|
|
308
|
+
server.prisma = localPrismaClient;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if('prisma' === driverType && isDryPrisma){
|
|
314
|
+
Logger.info('Skipping Prisma schema generation due to --dry-prisma flag.');
|
|
315
|
+
}
|
|
316
|
+
let generatorConfig = {server, projectPath: this.projectRoot, isOverride};
|
|
317
|
+
if('prisma' === driverType && this.prismaClient){
|
|
318
|
+
generatorConfig.prismaClient = this.prismaClient;
|
|
319
|
+
}
|
|
320
|
+
let generator = new EntitiesGenerator(generatorConfig);
|
|
321
|
+
let success = await generator.generate();
|
|
322
|
+
if(!success){
|
|
323
|
+
Logger.error('Entities generation failed.');
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
return true;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
extractDbConfigFromServer(server)
|
|
330
|
+
{
|
|
331
|
+
let config = sc.get(server, 'config');
|
|
332
|
+
if(!config){
|
|
333
|
+
Logger.warning('Could not extract database config from server.');
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
let dbConfig = {
|
|
337
|
+
client: sc.get(server, 'client', 'mysql'),
|
|
338
|
+
config: {
|
|
339
|
+
host: sc.get(config, 'host', 'localhost'),
|
|
340
|
+
port: sc.get(config, 'port', 3306),
|
|
341
|
+
database: sc.get(config, 'database', ''),
|
|
342
|
+
user: sc.get(config, 'user', ''),
|
|
343
|
+
password: sc.get(config, 'password', ''),
|
|
344
|
+
multipleStatements: true
|
|
345
|
+
},
|
|
346
|
+
debug: false
|
|
347
|
+
};
|
|
348
|
+
Logger.debug('Extracted DB config structure:', {
|
|
349
|
+
client: dbConfig.client,
|
|
350
|
+
host: dbConfig.config.host,
|
|
351
|
+
database: dbConfig.config.database
|
|
352
|
+
});
|
|
353
|
+
return dbConfig;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
async generatePrismaSchema(connectionData, useDataProxy = false)
|
|
357
|
+
{
|
|
358
|
+
if(!connectionData){
|
|
359
|
+
Logger.error('Missing "connectionData" to generate Prisma Schema.');
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
let generator = new PrismaSchemaGenerator({
|
|
363
|
+
...connectionData,
|
|
364
|
+
dataProxy: useDataProxy,
|
|
365
|
+
clientOutputPath: FileHandler.joinPaths(this.projectRoot, 'prisma', 'client'),
|
|
366
|
+
prismaSchemaPath: FileHandler.joinPaths(this.projectRoot, 'prisma')
|
|
367
|
+
});
|
|
368
|
+
let success = await generator.generate();
|
|
369
|
+
if(!success){
|
|
370
|
+
Logger.error('Prisma schema generation failed.');
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
async createEnvFile(templateVariables)
|
|
377
|
+
{
|
|
378
|
+
let envTemplatePath = FileHandler.joinPaths(this.defaultTemplatesPath, '.env.dist');
|
|
379
|
+
let envTemplateContent = FileHandler.readFile(envTemplatePath);
|
|
380
|
+
if(!envTemplateContent){
|
|
381
|
+
Logger.error('Template ".env.dist" not found: '+envTemplatePath);
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
384
|
+
return FileHandler.writeFile(this.envFilePath, this.renderEngine.render(envTemplateContent, templateVariables));
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
mapVariablesForTemplate(configVariables)
|
|
388
|
+
{
|
|
389
|
+
return {
|
|
390
|
+
host: configVariables.host,
|
|
391
|
+
port: configVariables.port,
|
|
392
|
+
publicUrl: configVariables.publicUrl,
|
|
393
|
+
adminPath: configVariables.adminPath,
|
|
394
|
+
adminSecret: configVariables.adminSecret,
|
|
395
|
+
dbClient: configVariables.database.client,
|
|
396
|
+
dbHost: configVariables.database.host,
|
|
397
|
+
dbPort: configVariables.database.port,
|
|
398
|
+
dbName: configVariables.database.name,
|
|
399
|
+
dbUser: configVariables.database.user,
|
|
400
|
+
dbPassword: configVariables.database.password,
|
|
401
|
+
dbDriver: configVariables.database.driver
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
mapVariablesForConfig(templateVariables)
|
|
406
|
+
{
|
|
407
|
+
return {
|
|
408
|
+
host: sc.get(templateVariables, 'app-host', 'http://localhost'),
|
|
409
|
+
port: Number(sc.get(templateVariables, 'app-port', 8080)),
|
|
410
|
+
publicUrl: sc.get(templateVariables, 'app-public-url', ''),
|
|
411
|
+
adminPath: sc.get(templateVariables, 'app-admin-path', '/reldens-admin'),
|
|
412
|
+
adminSecret: sc.get(templateVariables, 'app-admin-secret', Encryptor.generateSecretKey()),
|
|
413
|
+
database: {
|
|
414
|
+
client: sc.get(templateVariables, 'db-client', 'mysql'),
|
|
415
|
+
host: sc.get(templateVariables, 'db-host', 'localhost'),
|
|
416
|
+
port: Number(sc.get(templateVariables, 'db-port', 3306)),
|
|
417
|
+
name: sc.get(templateVariables, 'db-name', 'reldens_cms'),
|
|
418
|
+
user: sc.get(templateVariables, 'db-username', ''),
|
|
419
|
+
password: sc.get(templateVariables, 'db-password', ''),
|
|
420
|
+
driver: sc.get(templateVariables, 'db-storage-driver', 'prisma')
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
async createIndexJsFile(templateVariables)
|
|
426
|
+
{
|
|
427
|
+
if(!FileHandler.exists(this.indexTemplatePath)){
|
|
428
|
+
Logger.error('Index.js template not found: ' + this.indexTemplatePath);
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
let indexTemplate = FileHandler.readFile(this.indexTemplatePath);
|
|
432
|
+
let driverKey = templateVariables['db-storage-driver'];
|
|
433
|
+
let templateParams = {driverKey};
|
|
434
|
+
if('prisma' === driverKey){
|
|
435
|
+
templateParams.prismaClientImports = 'const { PrismaClient } = require(\'./prisma/client\');';
|
|
436
|
+
templateParams.prismaClientParam = ',\n prismaClient: new PrismaClient()';
|
|
437
|
+
}
|
|
438
|
+
if('prisma' !== driverKey){
|
|
439
|
+
templateParams.prismaClientImports = '';
|
|
440
|
+
templateParams.prismaClientParam = '';
|
|
441
|
+
}
|
|
442
|
+
let indexContent = this.renderEngine.render(indexTemplate, templateParams);
|
|
443
|
+
let indexFilePath = FileHandler.joinPaths(this.projectRoot, 'index.js');
|
|
444
|
+
if(FileHandler.exists(indexFilePath)){
|
|
445
|
+
Logger.info('Index.js file already exists, the CMS installer will not override the existent one.');
|
|
446
|
+
return true;
|
|
447
|
+
}
|
|
448
|
+
return FileHandler.writeFile(indexFilePath, indexContent);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
async createLockFile()
|
|
452
|
+
{
|
|
453
|
+
return FileHandler.writeFile(this.installLockPath, 'Installation completed on '+new Date().toISOString());
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
async copyAdminDirectory()
|
|
457
|
+
{
|
|
458
|
+
let projectAdminPath = FileHandler.joinPaths(this.projectRoot, 'admin');
|
|
459
|
+
if(FileHandler.exists(projectAdminPath)){
|
|
460
|
+
Logger.info('Admin folder already exists in project root.');
|
|
461
|
+
return true;
|
|
462
|
+
}
|
|
463
|
+
if(!FileHandler.exists(this.moduleAdminPath)){
|
|
464
|
+
Logger.error('Admin folder not found in module path: '+this.moduleAdminPath);
|
|
465
|
+
return false;
|
|
466
|
+
}
|
|
467
|
+
let projectAdminTemplates = FileHandler.joinPaths(projectAdminPath, 'templates');
|
|
468
|
+
FileHandler.copyFolderSync(this.moduleAdminTemplatesPath, projectAdminTemplates);
|
|
469
|
+
FileHandler.copyFolderSync(this.moduleAdminAssetsPath, this.projectPublicAssetsPath);
|
|
470
|
+
FileHandler.copyFile(
|
|
471
|
+
FileHandler.joinPaths(this.moduleAdminPath, 'reldens-admin-client.css'),
|
|
472
|
+
FileHandler.joinPaths(this.projectCssPath, 'reldens-admin-client.css')
|
|
473
|
+
);
|
|
474
|
+
FileHandler.copyFile(
|
|
475
|
+
FileHandler.joinPaths(this.moduleAdminPath, 'reldens-admin-client.js'),
|
|
476
|
+
FileHandler.joinPaths(this.projectJsPath, 'reldens-admin-client.js')
|
|
477
|
+
);
|
|
478
|
+
Logger.info('Admin folder copied to project root.');
|
|
479
|
+
return true;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
async prepareProjectDirectories()
|
|
483
|
+
{
|
|
484
|
+
FileHandler.createFolder(this.projectTemplatesPath);
|
|
485
|
+
FileHandler.createFolder(FileHandler.joinPaths(this.projectTemplatesPath, 'layouts'));
|
|
486
|
+
FileHandler.createFolder(this.projectPublicPath);
|
|
487
|
+
FileHandler.createFolder(this.projectPublicAssetsPath);
|
|
488
|
+
FileHandler.createFolder(this.projectCssPath);
|
|
489
|
+
FileHandler.createFolder(this.projectJsPath);
|
|
490
|
+
let baseFiles = [
|
|
491
|
+
'page.html',
|
|
492
|
+
'404.html',
|
|
493
|
+
'browserconfig.xml',
|
|
494
|
+
'favicon.ico',
|
|
495
|
+
'site.webmanifest'
|
|
496
|
+
];
|
|
497
|
+
for(let fileName of baseFiles){
|
|
498
|
+
FileHandler.copyFile(
|
|
499
|
+
FileHandler.joinPaths(this.defaultTemplatesPath, fileName),
|
|
500
|
+
FileHandler.joinPaths(this.projectTemplatesPath, fileName)
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
FileHandler.copyFile(
|
|
504
|
+
FileHandler.joinPaths(this.defaultTemplatesPath, 'layouts', 'default.html'),
|
|
505
|
+
FileHandler.joinPaths(this.projectTemplatesPath, 'layouts', 'default.html')
|
|
506
|
+
);
|
|
507
|
+
FileHandler.copyFolderSync(FileHandler.joinPaths(this.defaultTemplatesPath, 'css'), this.projectCssPath);
|
|
508
|
+
FileHandler.copyFolderSync(FileHandler.joinPaths(this.defaultTemplatesPath, 'js'), this.projectJsPath);
|
|
509
|
+
FileHandler.copyFolderSync(
|
|
510
|
+
FileHandler.joinPaths(this.defaultTemplatesPath, 'partials'),
|
|
511
|
+
FileHandler.joinPaths(this.projectTemplatesPath, 'partials')
|
|
512
|
+
);
|
|
513
|
+
FileHandler.copyFolderSync(
|
|
514
|
+
FileHandler.joinPaths(this.defaultTemplatesPath, 'domains'),
|
|
515
|
+
FileHandler.joinPaths(this.projectTemplatesPath, 'domains')
|
|
516
|
+
);
|
|
517
|
+
FileHandler.copyFolderSync(
|
|
518
|
+
FileHandler.joinPaths(this.defaultTemplatesPath, 'assets'),
|
|
519
|
+
this.projectPublicAssetsPath
|
|
520
|
+
);
|
|
521
|
+
return true;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
fetchDefaults()
|
|
525
|
+
{
|
|
526
|
+
return {
|
|
527
|
+
'app-host': process.env.RELDENS_APP_HOST || 'http://localhost',
|
|
528
|
+
'app-port': process.env.RELDENS_APP_PORT || '8080',
|
|
529
|
+
'app-public-url': process.env.RELDENS_PUBLIC_URL || '',
|
|
530
|
+
'app-admin-path': process.env.RELDENS_ADMIN_ROUTE_PATH || '/reldens-admin',
|
|
531
|
+
'db-storage-driver': process.env.RELDENS_STORAGE_DRIVER || 'prisma',
|
|
532
|
+
'db-client': process.env.RELDENS_DB_CLIENT || 'mysql',
|
|
533
|
+
'db-host': process.env.RELDENS_DB_HOST || 'localhost',
|
|
534
|
+
'db-port': process.env.RELDENS_DB_PORT || '3306',
|
|
535
|
+
'db-name': process.env.RELDENS_DB_NAME || 'reldens_cms',
|
|
536
|
+
'db-username': process.env.RELDENS_DB_USER || '',
|
|
537
|
+
'db-password': process.env.RELDENS_DB_PASSWORD || ''
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
async checkAndInstallPackages(requiredPackages)
|
|
542
|
+
{
|
|
543
|
+
let missingPackages = [];
|
|
544
|
+
for(let packageName of requiredPackages){
|
|
545
|
+
let packagePath = FileHandler.joinPaths(this.projectRoot, 'node_modules', packageName);
|
|
546
|
+
if(!FileHandler.exists(packagePath)){
|
|
547
|
+
missingPackages.push(packageName);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
if(0 === missingPackages.length){
|
|
551
|
+
return true;
|
|
552
|
+
}
|
|
553
|
+
Logger.info('Missing required packages: ' + missingPackages.join(', '));
|
|
554
|
+
Logger.info('These packages are required for the CMS to function properly.');
|
|
555
|
+
Logger.info('Would you like to install them automatically? (This may take a few minutes)');
|
|
556
|
+
Logger.info('Installing packages: npm install ' + missingPackages.join(' '));
|
|
557
|
+
try {
|
|
558
|
+
let installCommand = 'npm install ' + missingPackages.join(' ');
|
|
559
|
+
execSync(installCommand, {stdio: 'inherit', cwd: this.projectRoot});
|
|
560
|
+
Logger.info('Dependencies installed successfully.');
|
|
561
|
+
return true;
|
|
562
|
+
} catch (error) {
|
|
563
|
+
Logger.error('Failed to install dependencies: ' + error.message);
|
|
564
|
+
Logger.error('Please run manually: npm install ' + missingPackages.join(' '));
|
|
565
|
+
return false;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
async runSubprocessInstallation(dbConfig, templateVariables)
|
|
570
|
+
{
|
|
571
|
+
Logger.info('Subprocess Prisma installation - Starting...');
|
|
572
|
+
let workerPath = FileHandler.joinPaths(__dirname, 'prisma-subprocess-worker.js');
|
|
573
|
+
let worker = fork(workerPath, [], {
|
|
574
|
+
cwd: this.projectRoot,
|
|
575
|
+
stdio: ['pipe', 'pipe', 'pipe', 'ipc'],
|
|
576
|
+
env: {...process.env}
|
|
577
|
+
});
|
|
578
|
+
let message = {
|
|
579
|
+
dbConfig: dbConfig,
|
|
580
|
+
templateVariables: templateVariables,
|
|
581
|
+
migrationsPath: this.migrationsPath,
|
|
582
|
+
projectRoot: this.projectRoot
|
|
583
|
+
};
|
|
584
|
+
worker.stdout.on('data', (data) => {
|
|
585
|
+
Logger.info('Subprocess: '+data.toString().trim());
|
|
586
|
+
});
|
|
587
|
+
worker.stderr.on('data', (data) => {
|
|
588
|
+
Logger.error('Subprocess error: '+data.toString().trim());
|
|
589
|
+
});
|
|
590
|
+
worker.send(message);
|
|
591
|
+
let subprocessCompleted = false;
|
|
592
|
+
let subprocessSuccess = false;
|
|
593
|
+
let workerExited = false;
|
|
594
|
+
worker.on('message', (message) => {
|
|
595
|
+
subprocessCompleted = true;
|
|
596
|
+
subprocessSuccess = sc.get(message, 'success', false);
|
|
597
|
+
if(!subprocessSuccess){
|
|
598
|
+
Logger.error('Subprocess failed: '+sc.get(message, 'error', 'Unknown'));
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
worker.on('error', (error) => {
|
|
602
|
+
subprocessCompleted = true;
|
|
603
|
+
subprocessSuccess = false;
|
|
604
|
+
Logger.error('Subprocess error: '+error.message);
|
|
605
|
+
});
|
|
606
|
+
worker.on('exit', (code, signal) => {
|
|
607
|
+
workerExited = true;
|
|
608
|
+
if(!subprocessCompleted){
|
|
609
|
+
subprocessCompleted = true;
|
|
610
|
+
subprocessSuccess = false;
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
let attempts = 0;
|
|
614
|
+
while(!subprocessCompleted && attempts < this.subprocessMaxAttempts){
|
|
615
|
+
attempts++;
|
|
616
|
+
await this.waitMilliseconds(100);
|
|
617
|
+
}
|
|
618
|
+
if(!workerExited){
|
|
619
|
+
worker.kill('SIGTERM');
|
|
620
|
+
await this.waitMilliseconds(1000);
|
|
621
|
+
if(!workerExited){
|
|
622
|
+
worker.kill('SIGKILL');
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
Logger.info('Subprocess Prisma installation - Ended.');
|
|
626
|
+
return subprocessSuccess;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
async waitMilliseconds(ms)
|
|
630
|
+
{
|
|
631
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
module.exports.Installer = Installer;
|