@reldens/cms 0.16.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/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()
@@ -56,7 +58,8 @@ class Frontend
56
58
  this.templateEngine = new TemplateEngine({
57
59
  renderEngine: this.renderEngine,
58
60
  dataServer: this.dataServer,
59
- getPartials: this.getPartialsForDomain.bind(this)
61
+ getPartials: this.getPartialsForDomain.bind(this),
62
+ entitiesConfig: this.entitiesConfig
60
63
  });
61
64
  await this.loadPartials();
62
65
  await this.setupDomainTemplates();
@@ -251,19 +254,24 @@ class Frontend
251
254
  try {
252
255
  let path = req.path;
253
256
  let domain = this.getDomainFromRequest(req);
254
- console.log('---------------> ', {path, domain});
255
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
+ }
256
264
  let route = await this.findRouteByPath(path, domain);
257
265
  if(route){
258
- return await this.renderRoute(route, domain, res);
266
+ return await this.renderRouteWithCache(route, domain, res, path);
259
267
  }
260
268
  let entityResult = await this.findEntityByPath(path);
261
269
  if(entityResult){
262
- return await this.renderEntity(entityResult, domain, res);
270
+ return await this.renderEntityWithCache(entityResult, domain, res, path);
263
271
  }
264
272
  let templatePath = this.findTemplateByPath(path, domain);
265
273
  if(templatePath){
266
- return await this.renderTemplate(templatePath, domain, res);
274
+ return await this.renderTemplateWithCache(templatePath, domain, res, path);
267
275
  }
268
276
  return await this.renderNotFound(domain, res);
269
277
  } catch (error) {
@@ -276,23 +284,27 @@ class Frontend
276
284
  {
277
285
  let routesEntity = this.dataServer.getEntity('routes');
278
286
  if(!routesEntity){
279
- Logger.error('Routes entity not found in dataServer');
287
+ Logger.error('Routes entity not found in dataServer.');
280
288
  return false;
281
289
  }
282
290
  let domainFilter = domain || null;
283
291
  let routeFilters = {path, enabled: 1};
284
292
  let routes = await routesEntity.load(routeFilters);
285
293
  let matchingRoute = false;
294
+ let nullDomain = false;
286
295
  for(let route of routes){
287
- if(!route.domain || route.domain === domainFilter){
296
+ if(route.domain === domainFilter){
288
297
  matchingRoute = route;
289
298
  break;
290
299
  }
300
+ if(!route.domain){
301
+ nullDomain = route;
302
+ }
291
303
  }
292
304
  if(matchingRoute){
293
305
  return matchingRoute;
294
306
  }
295
- return false;
307
+ return nullDomain;
296
308
  }
297
309
 
298
310
  async isEntityAccessible(entityName)
@@ -338,36 +350,153 @@ class Frontend
338
350
  return this.findTemplatePath(templatePath, domain);
339
351
  }
340
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
+
341
434
  async renderRoute(route, domain, res)
342
435
  {
343
- if(!route.router || !route.cms_page_id){
436
+ if(!route.router){
344
437
  return await this.renderNotFound(domain, res);
345
438
  }
346
439
  let entity = this.dataServer.getEntity(route.router);
347
440
  if(!entity){
348
441
  return await this.renderNotFound(domain, res);
349
442
  }
350
- let content = await entity.loadById(route.cms_page_id);
443
+ let content = await entity.loadOne({route_id: route.id});
351
444
  if(!content){
352
445
  return await this.renderNotFound(domain, res);
353
446
  }
354
- return await this.renderWithLayout(
355
- content,
356
- Object.assign({}, route, content),
357
- sc.get(content, 'layout', 'default'),
358
- domain,
359
- res
360
- );
447
+ return await this.renderWithTemplate(content, Object.assign({}, route, content), domain, res);
361
448
  }
362
449
 
363
450
  async renderEntity(entityResult, domain, res)
364
451
  {
365
- return await this.renderWithLayout(
366
- entityResult.entity,
367
- Object.assign({}, entityResult.entity),
368
- sc.get(entityResult.entity, 'layout', 'default'),
369
- domain,
370
- res
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)
371
500
  );
372
501
  }
373
502
 
@@ -392,38 +521,49 @@ class Frontend
392
521
 
393
522
  async renderWithLayout(content, data, layoutName, domain, res)
394
523
  {
524
+ let layoutContent = await this.processContentWithLayout(content, data, layoutName, domain);
525
+ let pagePath = this.findTemplatePath('page', domain);
526
+ if(!pagePath){
527
+ return res.send(layoutContent);
528
+ }
529
+ let pageTemplate = FileHandler.readFile(pagePath);
530
+ if(!pageTemplate){
531
+ return res.send(layoutContent);
532
+ }
533
+ return res.send(
534
+ await this.templateEngine.render(
535
+ pageTemplate,
536
+ Object.assign({}, data, {content: layoutContent, siteHandle: this.resolveDomainToSiteKey(domain)}),
537
+ this.getPartialsForDomain(domain)
538
+ )
539
+ );
540
+ }
541
+
542
+ async processContentWithLayout(content, data, layoutName, domain)
543
+ {
544
+ let processedContent = await this.processContent(content, data, domain);
395
545
  let layoutPath = this.findLayoutPath(layoutName, domain);
396
- let layoutContent = '';
397
- if(layoutPath){
398
- let layoutTemplate = FileHandler.readFile(layoutPath);
399
- if(layoutTemplate){
400
- let processedContent = sc.get(content, 'content', '');
401
- if(processedContent){
402
- processedContent = await this.templateEngine.render(
403
- processedContent,
404
- data,
405
- this.getPartialsForDomain(domain)
406
- );
407
- }
408
- layoutContent = await this.templateEngine.render(
409
- layoutTemplate,
410
- Object.assign({}, data, {content: processedContent}),
411
- this.getPartialsForDomain(domain)
412
- );
413
- }
546
+ if(!layoutPath){
547
+ return processedContent;
414
548
  }
415
- if('' === layoutContent){
416
- let processedContent = sc.get(content, 'content', '');
417
- if(processedContent){
418
- layoutContent = await this.templateEngine.render(
419
- processedContent,
420
- data,
421
- this.getPartialsForDomain(domain)
422
- );
423
- }
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 '';
424
565
  }
425
- res.setHeader('Content-Type', 'text/html; charset=utf-8');
426
- return res.send(layoutContent);
566
+ return await this.templateEngine.render(contentText, data, this.getPartialsForDomain(domain));
427
567
  }
428
568
 
429
569
  async renderNotFound(domain, res)
package/lib/installer.js CHANGED
@@ -243,11 +243,11 @@ class Installer
243
243
  return '';
244
244
  }
245
245
 
246
- async generateEntities(server, isOverride = false, isInstallationMode = false)
246
+ async generateEntities(server, isOverride = false, isInstallationMode = false, isDryPrisma = false)
247
247
  {
248
248
  let driverType = sc.get(DriversClassMap, server.constructor.name, '');
249
249
  Logger.debug('Driver type detected: '+driverType+', Server constructor: '+server.constructor.name);
250
- if('prisma' === driverType && !isInstallationMode){
250
+ if('prisma' === driverType && !isInstallationMode && !isDryPrisma){
251
251
  Logger.info('Running prisma introspect "npx prisma db pull"...');
252
252
  let dbConfig = this.extractDbConfigFromServer(server);
253
253
  Logger.debug('Extracted DB config:', dbConfig);
@@ -260,6 +260,9 @@ class Installer
260
260
  Logger.info('Generated Prisma schema for entities generation.');
261
261
  }
262
262
  }
263
+ if('prisma' === driverType && isDryPrisma){
264
+ Logger.info('Skipping Prisma schema generation due to --dry-prisma flag.');
265
+ }
263
266
  let generatorConfig = {
264
267
  server,
265
268
  projectPath: this.projectRoot,
@@ -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;
package/lib/manager.js CHANGED
@@ -17,6 +17,7 @@ const { AdminManager } = require('./admin-manager');
17
17
  const { CmsPagesRouteManager } = require('./cms-pages-route-manager');
18
18
  const { Installer } = require('./installer');
19
19
  const { Frontend } = require('./frontend');
20
+ const { CacheManager } = require('./cache/cache-manager');
20
21
  const { EventsManagerSingleton, Logger, sc } = require('@reldens/utils');
21
22
  const { DriversMap } = require('@reldens/storage');
22
23
  const { AppServerFactory, FileHandler, Encryptor } = require('@reldens/server-utils');
@@ -58,6 +59,7 @@ class Manager
58
59
  this.domainMapping = sc.get(props, 'domainMapping', sc.toJson(process.env.RELDENS_DOMAIN_MAPPING));
59
60
  this.siteKeyMapping = sc.get(props, 'siteKeyMapping', sc.toJson(process.env.RELDENS_SITE_KEY_MAPPING));
60
61
  this.templateExtensions = sc.get(props, 'templateExtensions', ['.html', '.template']);
62
+ this.cache = sc.get(props, 'cache', false);
61
63
  this.app = sc.get(props, 'app', false);
62
64
  this.appServer = sc.get(props, 'appServer', false);
63
65
  this.dataServer = sc.get(props, 'dataServer', false);
@@ -67,6 +69,7 @@ class Manager
67
69
  this.prismaClient = sc.get(props, 'prismaClient', false);
68
70
  this.appServerFactory = new AppServerFactory();
69
71
  this.adminEntitiesGenerator = new AdminEntitiesGenerator();
72
+ this.cacheManager = new CacheManager({projectRoot: this.projectRoot, enabled: this.cache});
70
73
  this.installer = new Installer({
71
74
  projectRoot: this.projectRoot,
72
75
  prismaClient: this.prismaClient,
@@ -395,6 +398,7 @@ class Manager
395
398
  adminRoleId: this.adminRoleId,
396
399
  stylesFilePath: this.stylesFilePath,
397
400
  scriptsFilePath: this.scriptsFilePath,
401
+ cacheManager: this.cacheManager,
398
402
  branding: {
399
403
  companyName: this.companyName,
400
404
  logo: this.logo,
@@ -439,7 +443,9 @@ class Manager
439
443
  defaultDomain: this.defaultDomain,
440
444
  domainMapping: this.domainMapping,
441
445
  siteKeyMapping: this.siteKeyMapping,
442
- templateExtensions: this.templateExtensions
446
+ templateExtensions: this.templateExtensions,
447
+ entitiesConfig: this.entitiesConfig,
448
+ cacheManager: this.cacheManager
443
449
  });
444
450
  return await this.frontend.initialize();
445
451
  }