@reldens/cms 0.15.0 → 0.18.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 +150 -34
- package/admin/reldens-admin-client.css +39 -23
- package/admin/reldens-admin-client.js +7 -0
- package/admin/templates/cache-clean-button.html +4 -0
- package/admin/templates/edit.html +3 -1
- package/admin/templates/fields/view/textarea.html +1 -0
- package/admin/templates/sections/editForm/cms-pages.html +15 -0
- package/admin/templates/sections/viewForm/cms-pages.html +15 -0
- package/admin/templates/view.html +1 -0
- package/bin/reldens-cms-generate-entities.js +116 -5
- package/bin/reldens-cms.js +26 -8
- package/install/js/installer.js +5 -0
- package/install/success.html +1 -1
- package/lib/admin-manager/contents-builder.js +256 -0
- package/lib/admin-manager/router-contents.js +576 -0
- package/lib/admin-manager/router.js +208 -0
- package/lib/admin-manager.js +114 -944
- package/lib/cache/add-cache-button-subscriber.js +101 -0
- package/lib/cache/cache-manager.js +129 -0
- package/lib/cache/cache-routes-handler.js +76 -0
- package/lib/cms-pages-route-manager.js +117 -0
- package/lib/frontend.js +207 -64
- package/lib/installer.js +44 -20
- package/lib/json-fields-parser.js +74 -0
- package/lib/manager.js +55 -10
- package/lib/template-engine.js +361 -41
- package/lib/templates-list.js +10 -0
- package/migrations/default-blocks.sql +1 -1
- package/migrations/default-entity-access.sql +2 -2
- package/migrations/default-homepage.sql +27 -7
- package/migrations/install.sql +33 -36
- package/package.json +3 -3
- package/templates/index.js.dist +3 -3
- package/templates/js/scripts.js +5 -0
- package/templates/layouts/default.html +4 -4
- package/templates/page.html +14 -10
- package/templates/partials/footer.html +2 -23
- package/templates/partials/header.html +2 -35
package/lib/frontend.js
CHANGED
|
@@ -28,7 +28,9 @@ class Frontend
|
|
|
28
28
|
this.domainPartialsCache = new Map();
|
|
29
29
|
this.domainTemplatesMap = new Map();
|
|
30
30
|
this.entityAccessCache = new Map();
|
|
31
|
+
this.entitiesConfig = sc.get(props, 'entitiesConfig', {});
|
|
31
32
|
this.templateEngine = false;
|
|
33
|
+
this.cacheManager = sc.get(props, 'cacheManager', false);
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
async initialize()
|
|
@@ -55,7 +57,9 @@ class Frontend
|
|
|
55
57
|
}
|
|
56
58
|
this.templateEngine = new TemplateEngine({
|
|
57
59
|
renderEngine: this.renderEngine,
|
|
58
|
-
dataServer: this.dataServer
|
|
60
|
+
dataServer: this.dataServer,
|
|
61
|
+
getPartials: this.getPartialsForDomain.bind(this),
|
|
62
|
+
entitiesConfig: this.entitiesConfig
|
|
59
63
|
});
|
|
60
64
|
await this.loadPartials();
|
|
61
65
|
await this.setupDomainTemplates();
|
|
@@ -69,9 +73,9 @@ class Frontend
|
|
|
69
73
|
|
|
70
74
|
async loadEntityAccessRules()
|
|
71
75
|
{
|
|
72
|
-
let accessEntity = this.dataServer.getEntity('
|
|
76
|
+
let accessEntity = this.dataServer.getEntity('entitiesAccess');
|
|
73
77
|
if(!accessEntity){
|
|
74
|
-
Logger.warning('
|
|
78
|
+
Logger.warning('Entities Access not found.');
|
|
75
79
|
return;
|
|
76
80
|
}
|
|
77
81
|
let accessRules = await accessEntity.loadAll();
|
|
@@ -250,17 +254,24 @@ class Frontend
|
|
|
250
254
|
try {
|
|
251
255
|
let path = req.path;
|
|
252
256
|
let domain = this.getDomainFromRequest(req);
|
|
253
|
-
|
|
257
|
+
this.templateEngine.setCurrentDomain(domain);
|
|
258
|
+
if(this.cacheManager && this.cacheManager.isEnabled()){
|
|
259
|
+
let cachedContent = await this.cacheManager.get(domain, path);
|
|
260
|
+
if(cachedContent){
|
|
261
|
+
return res.send(cachedContent);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
let route = await this.findRouteByPath(path, domain);
|
|
254
265
|
if(route){
|
|
255
|
-
return await this.
|
|
266
|
+
return await this.renderRouteWithCache(route, domain, res, path);
|
|
256
267
|
}
|
|
257
268
|
let entityResult = await this.findEntityByPath(path);
|
|
258
269
|
if(entityResult){
|
|
259
|
-
return await this.
|
|
270
|
+
return await this.renderEntityWithCache(entityResult, domain, res, path);
|
|
260
271
|
}
|
|
261
272
|
let templatePath = this.findTemplateByPath(path, domain);
|
|
262
273
|
if(templatePath){
|
|
263
|
-
return await this.
|
|
274
|
+
return await this.renderTemplateWithCache(templatePath, domain, res, path);
|
|
264
275
|
}
|
|
265
276
|
return await this.renderNotFound(domain, res);
|
|
266
277
|
} catch (error) {
|
|
@@ -269,21 +280,31 @@ class Frontend
|
|
|
269
280
|
}
|
|
270
281
|
}
|
|
271
282
|
|
|
272
|
-
async findRouteByPath(path)
|
|
283
|
+
async findRouteByPath(path, domain)
|
|
273
284
|
{
|
|
274
285
|
let routesEntity = this.dataServer.getEntity('routes');
|
|
275
286
|
if(!routesEntity){
|
|
276
|
-
Logger.error('Routes entity not found in dataServer');
|
|
287
|
+
Logger.error('Routes entity not found in dataServer.');
|
|
277
288
|
return false;
|
|
278
289
|
}
|
|
279
|
-
let
|
|
280
|
-
|
|
281
|
-
|
|
290
|
+
let domainFilter = domain || null;
|
|
291
|
+
let routeFilters = {path, enabled: 1};
|
|
292
|
+
let routes = await routesEntity.load(routeFilters);
|
|
293
|
+
let matchingRoute = false;
|
|
294
|
+
let nullDomain = false;
|
|
295
|
+
for(let route of routes){
|
|
296
|
+
if(route.domain === domainFilter){
|
|
297
|
+
matchingRoute = route;
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
if(!route.domain){
|
|
301
|
+
nullDomain = route;
|
|
302
|
+
}
|
|
282
303
|
}
|
|
283
|
-
if(
|
|
284
|
-
return
|
|
304
|
+
if(matchingRoute){
|
|
305
|
+
return matchingRoute;
|
|
285
306
|
}
|
|
286
|
-
return
|
|
307
|
+
return nullDomain;
|
|
287
308
|
}
|
|
288
309
|
|
|
289
310
|
async isEntityAccessible(entityName)
|
|
@@ -329,47 +350,163 @@ class Frontend
|
|
|
329
350
|
return this.findTemplatePath(templatePath, domain);
|
|
330
351
|
}
|
|
331
352
|
|
|
353
|
+
async renderRouteWithCache(route, domain, res, path)
|
|
354
|
+
{
|
|
355
|
+
let renderedContent = await this.generateRouteContent(route, domain);
|
|
356
|
+
if(!renderedContent){
|
|
357
|
+
return await this.renderNotFound(domain, res);
|
|
358
|
+
}
|
|
359
|
+
if(this.cacheManager && this.cacheManager.isEnabled()){
|
|
360
|
+
await this.cacheManager.set(domain, path, renderedContent);
|
|
361
|
+
}
|
|
362
|
+
return res.send(renderedContent);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
async renderEntityWithCache(entityResult, domain, res, path)
|
|
366
|
+
{
|
|
367
|
+
let renderedContent = await this.generateEntityContent(entityResult, domain);
|
|
368
|
+
if(!renderedContent){
|
|
369
|
+
return await this.renderNotFound(domain, res);
|
|
370
|
+
}
|
|
371
|
+
if(this.cacheManager && this.cacheManager.isEnabled()){
|
|
372
|
+
await this.cacheManager.set(domain, path, renderedContent);
|
|
373
|
+
}
|
|
374
|
+
return res.send(renderedContent);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
async renderTemplateWithCache(templatePath, domain, res, path)
|
|
378
|
+
{
|
|
379
|
+
let renderedContent = await this.generateTemplateContent(templatePath, domain);
|
|
380
|
+
if(!renderedContent){
|
|
381
|
+
return res.status(500).send('Template error: '+templatePath);
|
|
382
|
+
}
|
|
383
|
+
if(this.cacheManager && this.cacheManager.isEnabled()){
|
|
384
|
+
await this.cacheManager.set(domain, path, renderedContent);
|
|
385
|
+
}
|
|
386
|
+
let layoutContent = await this.processContentWithLayout({content: renderedContent}, {}, 'default', domain);
|
|
387
|
+
let pagePath = this.findTemplatePath('page', domain);
|
|
388
|
+
if(!pagePath){
|
|
389
|
+
return res.send(layoutContent);
|
|
390
|
+
}
|
|
391
|
+
let pageTemplate = FileHandler.readFile(pagePath);
|
|
392
|
+
if(!pageTemplate){
|
|
393
|
+
return res.send(layoutContent);
|
|
394
|
+
}
|
|
395
|
+
let finalContent = await this.templateEngine.render(
|
|
396
|
+
pageTemplate,
|
|
397
|
+
Object.assign({}, {}, {content: layoutContent, siteHandle: this.resolveDomainToSiteKey(domain)}),
|
|
398
|
+
this.getPartialsForDomain(domain)
|
|
399
|
+
);
|
|
400
|
+
return res.send(finalContent);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
async generateRouteContent(route, domain)
|
|
404
|
+
{
|
|
405
|
+
if(!route.router){
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
408
|
+
let entity = this.dataServer.getEntity(route.router);
|
|
409
|
+
if(!entity){
|
|
410
|
+
return false;
|
|
411
|
+
}
|
|
412
|
+
let content = await entity.loadOne({route_id: route.id});
|
|
413
|
+
if(!content){
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
return await this.renderWithTemplateContent(content, Object.assign({}, route, content), domain);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
async generateEntityContent(entityResult, domain)
|
|
420
|
+
{
|
|
421
|
+
return await this.renderWithTemplateContent(entityResult.entity, entityResult.entity, domain);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
async generateTemplateContent(templatePath, domain)
|
|
425
|
+
{
|
|
426
|
+
let template = FileHandler.readFile(templatePath);
|
|
427
|
+
if(!template){
|
|
428
|
+
Logger.error('Failed to read template: ' + templatePath);
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
return await this.templateEngine.render(template, {}, this.getPartialsForDomain(domain));
|
|
432
|
+
}
|
|
433
|
+
|
|
332
434
|
async renderRoute(route, domain, res)
|
|
333
435
|
{
|
|
334
|
-
if(!route.router
|
|
436
|
+
if(!route.router){
|
|
335
437
|
return await this.renderNotFound(domain, res);
|
|
336
438
|
}
|
|
337
439
|
let entity = this.dataServer.getEntity(route.router);
|
|
338
440
|
if(!entity){
|
|
339
441
|
return await this.renderNotFound(domain, res);
|
|
340
442
|
}
|
|
341
|
-
let content = await entity.
|
|
443
|
+
let content = await entity.loadOne({route_id: route.id});
|
|
342
444
|
if(!content){
|
|
343
445
|
return await this.renderNotFound(domain, res);
|
|
344
446
|
}
|
|
345
|
-
return await this.
|
|
346
|
-
content,
|
|
347
|
-
Object.assign({}, route, content, {currentYear: new Date().getFullYear()}),
|
|
348
|
-
sc.get(content, 'layout', 'default'),
|
|
349
|
-
domain,
|
|
350
|
-
res
|
|
351
|
-
);
|
|
447
|
+
return await this.renderWithTemplate(content, Object.assign({}, route, content), domain, res);
|
|
352
448
|
}
|
|
353
449
|
|
|
354
450
|
async renderEntity(entityResult, domain, res)
|
|
355
451
|
{
|
|
356
|
-
return await this.
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
452
|
+
return await this.renderWithTemplate(entityResult.entity, entityResult.entity, domain, res);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
async renderWithTemplate(content, data, domain, res)
|
|
456
|
+
{
|
|
457
|
+
let templateName = sc.get(content, 'template', '');
|
|
458
|
+
let layoutName = sc.get(content, 'layout', 'default');
|
|
459
|
+
let layoutContent = await this.processContentWithLayout(content, data, layoutName, domain);
|
|
460
|
+
if(!templateName){
|
|
461
|
+
templateName = 'page';
|
|
462
|
+
}
|
|
463
|
+
let templatePath = this.findTemplatePath(templateName, domain);
|
|
464
|
+
if(!templatePath){
|
|
465
|
+
return res.send(layoutContent);
|
|
466
|
+
}
|
|
467
|
+
let pageTemplate = FileHandler.readFile(templatePath);
|
|
468
|
+
if(!pageTemplate){
|
|
469
|
+
return res.send(layoutContent);
|
|
470
|
+
}
|
|
471
|
+
return res.send(
|
|
472
|
+
await this.templateEngine.render(
|
|
473
|
+
pageTemplate,
|
|
474
|
+
Object.assign({}, data, {content: layoutContent, siteHandle: this.resolveDomainToSiteKey(domain)}),
|
|
475
|
+
this.getPartialsForDomain(domain)
|
|
476
|
+
)
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
async renderWithTemplateContent(content, data, domain)
|
|
481
|
+
{
|
|
482
|
+
let templateName = sc.get(content, 'template', '');
|
|
483
|
+
let layoutName = sc.get(content, 'layout', 'default');
|
|
484
|
+
let layoutContent = await this.processContentWithLayout(content, data, layoutName, domain);
|
|
485
|
+
if(!templateName){
|
|
486
|
+
templateName = 'page';
|
|
487
|
+
}
|
|
488
|
+
let templatePath = this.findTemplatePath(templateName, domain);
|
|
489
|
+
if(!templatePath){
|
|
490
|
+
return layoutContent;
|
|
491
|
+
}
|
|
492
|
+
let pageTemplate = FileHandler.readFile(templatePath);
|
|
493
|
+
if(!pageTemplate){
|
|
494
|
+
return layoutContent;
|
|
495
|
+
}
|
|
496
|
+
return await this.templateEngine.render(
|
|
497
|
+
pageTemplate,
|
|
498
|
+
Object.assign({}, data, {content: layoutContent, siteHandle: this.resolveDomainToSiteKey(domain)}),
|
|
499
|
+
this.getPartialsForDomain(domain)
|
|
362
500
|
);
|
|
363
501
|
}
|
|
364
502
|
|
|
365
503
|
async renderTemplate(templatePath, domain, res)
|
|
366
504
|
{
|
|
367
|
-
let
|
|
368
|
-
let content = await this.renderContentWithTemplate(templatePath, data, domain);
|
|
505
|
+
let content = await this.renderContentWithTemplate(templatePath, {}, domain);
|
|
369
506
|
if(!content){
|
|
370
507
|
return res.status(500).send('Template error: '+templatePath);
|
|
371
508
|
}
|
|
372
|
-
return await this.renderWithLayout({content},
|
|
509
|
+
return await this.renderWithLayout({content}, {}, 'default', domain, res);
|
|
373
510
|
}
|
|
374
511
|
|
|
375
512
|
async renderContentWithTemplate(templatePath, data, domain)
|
|
@@ -384,23 +521,7 @@ class Frontend
|
|
|
384
521
|
|
|
385
522
|
async renderWithLayout(content, data, layoutName, domain, res)
|
|
386
523
|
{
|
|
387
|
-
let
|
|
388
|
-
let layoutContent = '';
|
|
389
|
-
if(layoutPath){
|
|
390
|
-
let layoutTemplate = FileHandler.readFile(layoutPath);
|
|
391
|
-
if(layoutTemplate){
|
|
392
|
-
layoutContent = await this.templateEngine.render(
|
|
393
|
-
layoutTemplate,
|
|
394
|
-
Object.assign({}, data, {
|
|
395
|
-
content: sc.get(content, 'content', '')
|
|
396
|
-
}),
|
|
397
|
-
this.getPartialsForDomain(domain)
|
|
398
|
-
);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
if('' === layoutContent){
|
|
402
|
-
layoutContent = sc.get(content, 'content', '');
|
|
403
|
-
}
|
|
524
|
+
let layoutContent = await this.processContentWithLayout(content, data, layoutName, domain);
|
|
404
525
|
let pagePath = this.findTemplatePath('page', domain);
|
|
405
526
|
if(!pagePath){
|
|
406
527
|
return res.send(layoutContent);
|
|
@@ -412,28 +533,50 @@ class Frontend
|
|
|
412
533
|
return res.send(
|
|
413
534
|
await this.templateEngine.render(
|
|
414
535
|
pageTemplate,
|
|
415
|
-
Object.assign({}, data, {
|
|
416
|
-
content: layoutContent,
|
|
417
|
-
siteHandle: this.resolveDomainToSiteKey(domain)
|
|
418
|
-
}),
|
|
536
|
+
Object.assign({}, data, {content: layoutContent, siteHandle: this.resolveDomainToSiteKey(domain)}),
|
|
419
537
|
this.getPartialsForDomain(domain)
|
|
420
538
|
)
|
|
421
539
|
);
|
|
422
540
|
}
|
|
423
541
|
|
|
424
|
-
async
|
|
542
|
+
async processContentWithLayout(content, data, layoutName, domain)
|
|
425
543
|
{
|
|
426
|
-
let
|
|
427
|
-
|
|
428
|
-
|
|
544
|
+
let processedContent = await this.processContent(content, data, domain);
|
|
545
|
+
let layoutPath = this.findLayoutPath(layoutName, domain);
|
|
546
|
+
if(!layoutPath){
|
|
547
|
+
return processedContent;
|
|
429
548
|
}
|
|
430
|
-
let
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
549
|
+
let layoutTemplate = FileHandler.readFile(layoutPath);
|
|
550
|
+
if(!layoutTemplate){
|
|
551
|
+
return processedContent;
|
|
552
|
+
}
|
|
553
|
+
return await this.templateEngine.render(
|
|
554
|
+
layoutTemplate,
|
|
555
|
+
Object.assign({}, data, {content: processedContent}),
|
|
556
|
+
this.getPartialsForDomain(domain)
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
async processContent(content, data, domain)
|
|
561
|
+
{
|
|
562
|
+
let contentText = sc.get(content, 'content', '');
|
|
563
|
+
if(!contentText){
|
|
564
|
+
return '';
|
|
565
|
+
}
|
|
566
|
+
return await this.templateEngine.render(contentText, data, this.getPartialsForDomain(domain));
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
async renderNotFound(domain, res)
|
|
570
|
+
{
|
|
571
|
+
let notFoundPath = this.findTemplatePath('404', domain);
|
|
572
|
+
if(notFoundPath){
|
|
573
|
+
let content = await this.renderContentWithTemplate(notFoundPath, {}, domain);
|
|
574
|
+
if(content){
|
|
575
|
+
res.status(404);
|
|
576
|
+
return await this.renderWithLayout({content}, {}, 'default', domain, res);
|
|
577
|
+
}
|
|
434
578
|
}
|
|
435
|
-
res.status(404);
|
|
436
|
-
return await this.renderWithLayout({content}, data, 'default', domain, res);
|
|
579
|
+
return res.status(404).send('Page not found');
|
|
437
580
|
}
|
|
438
581
|
|
|
439
582
|
}
|
package/lib/installer.js
CHANGED
|
@@ -35,6 +35,7 @@ class Installer
|
|
|
35
35
|
this.moduleAdminTemplatesPath = FileHandler.joinPaths(this.moduleAdminPath, 'templates')
|
|
36
36
|
this.indexTemplatePath = FileHandler.joinPaths(this.defaultTemplatesPath, 'index.js.dist');
|
|
37
37
|
this.postInstallCallback = sc.get(props, 'postInstallCallback', false);
|
|
38
|
+
this.prismaClient = sc.get(props, 'prismaClient', false);
|
|
38
39
|
this.entitiesLoader = new EntitiesLoader({projectRoot: this.projectRoot});
|
|
39
40
|
}
|
|
40
41
|
|
|
@@ -49,17 +50,21 @@ class Installer
|
|
|
49
50
|
Logger.error('Missing app on prepareSetup for Installer.');
|
|
50
51
|
return false;
|
|
51
52
|
}
|
|
53
|
+
if(!appServer){
|
|
54
|
+
Logger.error('Missing appServer on prepareSetup for Installer.');
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
52
57
|
if(!appServerFactory){
|
|
53
58
|
Logger.error('Missing appServerFactory on prepareSetup for Installer.');
|
|
54
59
|
return false;
|
|
55
60
|
}
|
|
56
61
|
if(!renderEngine){
|
|
57
|
-
Logger.error('Missing renderEngine for Installer.');
|
|
62
|
+
Logger.error('Missing renderEngine on prepareSetup for Installer.');
|
|
58
63
|
return false;
|
|
59
64
|
}
|
|
60
65
|
this.app = app;
|
|
61
|
-
this.appServerFactory = appServerFactory;
|
|
62
66
|
this.appServer = appServer;
|
|
67
|
+
this.appServerFactory = appServerFactory;
|
|
63
68
|
this.renderEngine = renderEngine;
|
|
64
69
|
app.use('/install-assets', appServerFactory.applicationFramework.static(this.installerPath, {index: false}));
|
|
65
70
|
app.use(appServerFactory.session({
|
|
@@ -117,7 +122,7 @@ class Installer
|
|
|
117
122
|
'configuration-error': 'Configuration error while completing installation.',
|
|
118
123
|
'already-installed': 'The application is already installed.'
|
|
119
124
|
};
|
|
120
|
-
return errorMessages
|
|
125
|
+
return sc.get(errorMessages, errorCode, 'An unknown error occurred during installation.');
|
|
121
126
|
}
|
|
122
127
|
|
|
123
128
|
async executeInstallProcess(req, res)
|
|
@@ -145,13 +150,8 @@ class Installer
|
|
|
145
150
|
},
|
|
146
151
|
debug: false
|
|
147
152
|
};
|
|
148
|
-
if('prisma' === selectedDriver){
|
|
149
|
-
|
|
150
|
-
if(!generatedPrismaSchema){
|
|
151
|
-
Logger.error('Could not generated Prisma schema.');
|
|
152
|
-
return res.redirect('/?error=prisma-schema-generation-error');
|
|
153
|
-
}
|
|
154
|
-
Logger.info('Generated Prisma schema.');
|
|
153
|
+
if('prisma' === selectedDriver && this.prismaClient){
|
|
154
|
+
dbConfig.prismaClient = this.prismaClient;
|
|
155
155
|
}
|
|
156
156
|
let dbDriver = new driverClass(dbConfig);
|
|
157
157
|
if(!await dbDriver.connect()){
|
|
@@ -181,7 +181,7 @@ class Installer
|
|
|
181
181
|
return res.redirect(redirectError);
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
|
-
let entitiesGenerationResult = await this.generateEntities(dbDriver);
|
|
184
|
+
let entitiesGenerationResult = await this.generateEntities(dbDriver, false, true);
|
|
185
185
|
if(!entitiesGenerationResult){
|
|
186
186
|
Logger.error('Entities generation error.');
|
|
187
187
|
return res.redirect('/?error=installation-entities-generation-failed');
|
|
@@ -214,7 +214,7 @@ class Installer
|
|
|
214
214
|
if(successFileContent){
|
|
215
215
|
successContent = this.renderEngine.render(
|
|
216
216
|
successFileContent,
|
|
217
|
-
{adminPath: templateVariables['app-admin-path']
|
|
217
|
+
{adminPath: templateVariables['app-admin-path']},
|
|
218
218
|
);
|
|
219
219
|
}
|
|
220
220
|
return res.send(successContent);
|
|
@@ -234,7 +234,8 @@ class Installer
|
|
|
234
234
|
Logger.error('SQL file "'+fileName+'" not found.');
|
|
235
235
|
return '/?error=sql-file-not-found&file-name='+fileName;
|
|
236
236
|
}
|
|
237
|
-
|
|
237
|
+
let queryExecutionResult = await dbDriver.rawQuery(sqlFileContent);
|
|
238
|
+
if(!queryExecutionResult){
|
|
238
239
|
Logger.error('SQL file "'+fileName+'" raw execution failed.');
|
|
239
240
|
return '/?error=sql-file-execution-error&file-name='+fileName;
|
|
240
241
|
}
|
|
@@ -242,23 +243,35 @@ class Installer
|
|
|
242
243
|
return '';
|
|
243
244
|
}
|
|
244
245
|
|
|
245
|
-
async generateEntities(server, isOverride = false)
|
|
246
|
+
async generateEntities(server, isOverride = false, isInstallationMode = false, isDryPrisma = false)
|
|
246
247
|
{
|
|
247
248
|
let driverType = sc.get(DriversClassMap, server.constructor.name, '');
|
|
248
249
|
Logger.debug('Driver type detected: '+driverType+', Server constructor: '+server.constructor.name);
|
|
249
|
-
if('prisma' === driverType){
|
|
250
|
+
if('prisma' === driverType && !isInstallationMode && !isDryPrisma){
|
|
251
|
+
Logger.info('Running prisma introspect "npx prisma db pull"...');
|
|
250
252
|
let dbConfig = this.extractDbConfigFromServer(server);
|
|
251
253
|
Logger.debug('Extracted DB config:', dbConfig);
|
|
252
254
|
if(dbConfig){
|
|
253
255
|
let generatedPrismaSchema = await this.generatePrismaSchema(dbConfig);
|
|
254
256
|
if(!generatedPrismaSchema){
|
|
255
|
-
Logger.error('
|
|
257
|
+
Logger.error('Prisma schema generation failed.');
|
|
256
258
|
return false;
|
|
257
259
|
}
|
|
258
260
|
Logger.info('Generated Prisma schema for entities generation.');
|
|
259
261
|
}
|
|
260
262
|
}
|
|
261
|
-
|
|
263
|
+
if('prisma' === driverType && isDryPrisma){
|
|
264
|
+
Logger.info('Skipping Prisma schema generation due to --dry-prisma flag.');
|
|
265
|
+
}
|
|
266
|
+
let generatorConfig = {
|
|
267
|
+
server,
|
|
268
|
+
projectPath: this.projectRoot,
|
|
269
|
+
isOverride
|
|
270
|
+
};
|
|
271
|
+
if('prisma' === driverType && this.prismaClient){
|
|
272
|
+
generatorConfig.prismaClient = this.prismaClient;
|
|
273
|
+
}
|
|
274
|
+
let generator = new EntitiesGenerator(generatorConfig);
|
|
262
275
|
let success = await generator.generate();
|
|
263
276
|
if(!success){
|
|
264
277
|
Logger.error('Entities generation failed.');
|
|
@@ -303,7 +316,8 @@ class Installer
|
|
|
303
316
|
let generator = new PrismaSchemaGenerator({
|
|
304
317
|
...connectionData,
|
|
305
318
|
dataProxy: useDataProxy,
|
|
306
|
-
|
|
319
|
+
clientOutputPath: FileHandler.joinPaths(this.projectRoot, 'prisma', 'client'),
|
|
320
|
+
prismaSchemaPath: FileHandler.joinPaths(this.projectRoot, 'prisma')
|
|
307
321
|
});
|
|
308
322
|
let success = await generator.generate();
|
|
309
323
|
if(!success){
|
|
@@ -363,12 +377,22 @@ class Installer
|
|
|
363
377
|
async createIndexJsFile(templateVariables)
|
|
364
378
|
{
|
|
365
379
|
if(!FileHandler.exists(this.indexTemplatePath)){
|
|
366
|
-
Logger.error('Index.js template not found: '+this.indexTemplatePath);
|
|
380
|
+
Logger.error('Index.js template not found: ' + this.indexTemplatePath);
|
|
367
381
|
return false;
|
|
368
382
|
}
|
|
369
383
|
let indexTemplate = FileHandler.readFile(this.indexTemplatePath);
|
|
370
384
|
let driverKey = templateVariables['db-storage-driver'];
|
|
371
|
-
let
|
|
385
|
+
let templateParams = {driverKey};
|
|
386
|
+
if('prisma' === driverKey){
|
|
387
|
+
let prismaClientPath = FileHandler.joinPaths(this.projectRoot, 'prisma', 'client');
|
|
388
|
+
templateParams.prismaClientImports = 'const { PrismaClient } = require(\'' + prismaClientPath + '\');';
|
|
389
|
+
templateParams.prismaClientParam = ',\n prismaClient: new PrismaClient()';
|
|
390
|
+
}
|
|
391
|
+
if('prisma' !== driverKey){
|
|
392
|
+
templateParams.prismaClientImports = '';
|
|
393
|
+
templateParams.prismaClientParam = '';
|
|
394
|
+
}
|
|
395
|
+
let indexContent = this.renderEngine.render(indexTemplate, templateParams);
|
|
372
396
|
let indexFilePath = FileHandler.joinPaths(this.projectRoot, 'index.js');
|
|
373
397
|
if(FileHandler.exists(indexFilePath)){
|
|
374
398
|
Logger.info('Index.js file already exists, the CMS installer will not override the existent one.');
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - CMS - JsonFieldsParser
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { Logger, sc } = require('@reldens/utils');
|
|
8
|
+
|
|
9
|
+
class JsonFieldsParser
|
|
10
|
+
{
|
|
11
|
+
|
|
12
|
+
constructor(props)
|
|
13
|
+
{
|
|
14
|
+
this.entitiesConfig = sc.get(props, 'entitiesConfig', {});
|
|
15
|
+
this.jsonFieldsCache = new Map();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
getJsonFieldsForEntity(entityName)
|
|
19
|
+
{
|
|
20
|
+
if(this.jsonFieldsCache.has(entityName)){
|
|
21
|
+
return this.jsonFieldsCache.get(entityName);
|
|
22
|
+
}
|
|
23
|
+
let jsonFields = [];
|
|
24
|
+
let entityConfig = sc.get(this.entitiesConfig, entityName);
|
|
25
|
+
if(!entityConfig){
|
|
26
|
+
Logger.error('Entity not found in configuration: '+entityName);
|
|
27
|
+
return jsonFields;
|
|
28
|
+
}
|
|
29
|
+
if(!sc.hasOwn(entityConfig, 'properties')){
|
|
30
|
+
Logger.error('Missing properties on entity configuration: '+entityName);
|
|
31
|
+
return jsonFields;
|
|
32
|
+
}
|
|
33
|
+
for(let fieldName of Object.keys(entityConfig.properties)){
|
|
34
|
+
if('json' === sc.get(entityConfig.properties[fieldName], 'dbType', '')){
|
|
35
|
+
jsonFields.push(fieldName);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
this.jsonFieldsCache.set(entityName, jsonFields);
|
|
39
|
+
return jsonFields;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
parseJsonFields(data, jsonFields)
|
|
43
|
+
{
|
|
44
|
+
if(!sc.isArray(jsonFields) || 0 === jsonFields.length){
|
|
45
|
+
return data;
|
|
46
|
+
}
|
|
47
|
+
if(sc.isArray(data)){
|
|
48
|
+
for(let item of data){
|
|
49
|
+
this.parseEntityJsonFields(item, jsonFields);
|
|
50
|
+
}
|
|
51
|
+
return data;
|
|
52
|
+
}
|
|
53
|
+
if(sc.isObject(data)){
|
|
54
|
+
this.parseEntityJsonFields(data, jsonFields);
|
|
55
|
+
}
|
|
56
|
+
return data;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
parseEntityJsonFields(entity, jsonFields)
|
|
60
|
+
{
|
|
61
|
+
for(let fieldName of jsonFields){
|
|
62
|
+
if(!sc.hasOwn(entity, fieldName) || !sc.isString(entity[fieldName])){
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
let parsed = sc.parseJson(entity[fieldName], false);
|
|
66
|
+
if(false !== parsed){
|
|
67
|
+
entity[fieldName] = parsed;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
module.exports.JsonFieldsParser = JsonFieldsParser;
|