@reldens/cms 0.16.0 → 0.19.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 +92 -9
- package/admin/reldens-admin-client.css +55 -0
- package/admin/reldens-admin-client.js +24 -0
- package/admin/templates/cache-clean-button.html +4 -0
- package/admin/templates/clear-all-cache-button.html +18 -0
- package/admin/templates/fields/view/textarea.html +1 -1
- package/bin/reldens-cms-generate-entities.js +85 -18
- package/bin/reldens-cms.js +6 -6
- package/lib/admin-manager/contents-builder.js +257 -0
- package/lib/admin-manager/router-contents.js +618 -0
- package/lib/admin-manager/router.js +208 -0
- package/lib/admin-manager-validator.js +2 -1
- package/lib/admin-manager.js +116 -990
- package/lib/admin-translations.js +9 -1
- package/lib/cache/add-cache-button-subscriber.js +149 -0
- package/lib/cache/cache-manager.js +168 -0
- package/lib/cache/cache-routes-handler.js +99 -0
- package/lib/cms-pages-route-manager.js +45 -21
- package/lib/frontend.js +288 -71
- package/lib/installer.js +5 -2
- package/lib/json-fields-parser.js +74 -0
- package/lib/manager.js +49 -4
- package/lib/pagination-handler.js +243 -0
- package/lib/search-renderer.js +116 -0
- package/lib/search.js +344 -0
- package/lib/template-engine/collections-single-transformer.js +53 -0
- package/lib/template-engine/collections-transformer-base.js +84 -0
- package/lib/template-engine/collections-transformer.js +353 -0
- package/lib/template-engine/entities-transformer.js +65 -0
- package/lib/template-engine/partials-transformer.js +171 -0
- package/lib/template-engine.js +53 -387
- package/lib/templates-list.js +2 -0
- package/migrations/default-homepage.sql +6 -6
- package/migrations/install.sql +21 -20
- package/package.json +4 -4
- package/templates/page.html +19 -2
- package/templates/partials/entriesListView.html +14 -0
- package/templates/partials/pagedCollection.html +33 -0
package/lib/frontend.js
CHANGED
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
const { TemplateEngine } = require('./template-engine');
|
|
8
|
+
const { Search } = require('./search');
|
|
9
|
+
const { SearchRenderer } = require('./search-renderer');
|
|
7
10
|
const { FileHandler } = require('@reldens/server-utils');
|
|
8
11
|
const { Logger, sc } = require('@reldens/utils');
|
|
9
|
-
const { TemplateEngine } = require('./template-engine');
|
|
10
12
|
|
|
11
13
|
class Frontend
|
|
12
14
|
{
|
|
@@ -28,7 +30,27 @@ class Frontend
|
|
|
28
30
|
this.domainPartialsCache = new Map();
|
|
29
31
|
this.domainTemplatesMap = new Map();
|
|
30
32
|
this.entityAccessCache = new Map();
|
|
33
|
+
this.entitiesConfig = sc.get(props, 'entitiesConfig', {});
|
|
31
34
|
this.templateEngine = false;
|
|
35
|
+
this.cacheManager = sc.get(props, 'cacheManager', false);
|
|
36
|
+
this.searchPath = sc.get(props, 'searchPath', '/search');
|
|
37
|
+
this.searchSets = sc.get(props, 'searchSets', false);
|
|
38
|
+
this.searchConfig = {dataServer: this.dataServer};
|
|
39
|
+
if(this.searchSets){
|
|
40
|
+
this.searchConfig.searchSets = this.searchSets;
|
|
41
|
+
}
|
|
42
|
+
this.search = new Search(this.searchConfig);
|
|
43
|
+
this.searchRenderer = new SearchRenderer({
|
|
44
|
+
renderEngine: this.renderEngine,
|
|
45
|
+
getPartials: this.getPartialsForDomain.bind(this)
|
|
46
|
+
});
|
|
47
|
+
this.metaDefaults = sc.get(props, 'metaDefaults', {
|
|
48
|
+
locale: 'en',
|
|
49
|
+
viewport: 'width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes, viewport-fit=cover',
|
|
50
|
+
meta_robots: 'index,follow',
|
|
51
|
+
meta_theme_color: '#000000',
|
|
52
|
+
meta_twitter_card_type: 'summary'
|
|
53
|
+
});
|
|
32
54
|
}
|
|
33
55
|
|
|
34
56
|
async initialize()
|
|
@@ -56,12 +78,17 @@ class Frontend
|
|
|
56
78
|
this.templateEngine = new TemplateEngine({
|
|
57
79
|
renderEngine: this.renderEngine,
|
|
58
80
|
dataServer: this.dataServer,
|
|
59
|
-
getPartials: this.getPartialsForDomain.bind(this)
|
|
81
|
+
getPartials: this.getPartialsForDomain.bind(this),
|
|
82
|
+
entitiesConfig: this.entitiesConfig
|
|
60
83
|
});
|
|
84
|
+
this.searchConfig.jsonFieldsParser = this.templateEngine.jsonFieldsParser;
|
|
61
85
|
await this.loadPartials();
|
|
62
86
|
await this.setupDomainTemplates();
|
|
63
87
|
await this.loadEntityAccessRules();
|
|
64
88
|
this.setupStaticAssets();
|
|
89
|
+
this.app.get(this.searchPath, async (req, res) => {
|
|
90
|
+
return await this.handleSearchRequest(req, res);
|
|
91
|
+
});
|
|
65
92
|
this.app.get('*', async (req, res) => {
|
|
66
93
|
return await this.handleRequest(req, res);
|
|
67
94
|
});
|
|
@@ -246,26 +273,114 @@ class Frontend
|
|
|
246
273
|
return false;
|
|
247
274
|
}
|
|
248
275
|
|
|
276
|
+
fetchMetaFields(data)
|
|
277
|
+
{
|
|
278
|
+
if(!sc.isObject(data) || 0 === Object.keys(data).length){
|
|
279
|
+
return this.metaDefaults;
|
|
280
|
+
}
|
|
281
|
+
let result = Object.assign({}, this.metaDefaults);
|
|
282
|
+
for(let key of Object.keys(data)){
|
|
283
|
+
let value = data[key];
|
|
284
|
+
if(null !== value && '' !== value && 'undefined' !== typeof value){
|
|
285
|
+
result[key] = value;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
let titleValue = sc.get(result, 'title', '');
|
|
289
|
+
let metaTitleValue = sc.get(result, 'meta_title', titleValue);
|
|
290
|
+
if(metaTitleValue && '' !== metaTitleValue){
|
|
291
|
+
result.meta_title = metaTitleValue;
|
|
292
|
+
}
|
|
293
|
+
let metaOgTitleValue = sc.get(result, 'meta_og_title', metaTitleValue);
|
|
294
|
+
if(metaOgTitleValue && '' !== metaOgTitleValue){
|
|
295
|
+
result.meta_og_title = metaOgTitleValue;
|
|
296
|
+
}
|
|
297
|
+
let metaDescValue = sc.get(result, 'meta_description', '');
|
|
298
|
+
let metaOgDescValue = sc.get(result, 'meta_og_description', metaDescValue);
|
|
299
|
+
if(metaOgDescValue && '' !== metaOgDescValue){
|
|
300
|
+
result.meta_og_description = metaOgDescValue;
|
|
301
|
+
}
|
|
302
|
+
let jsonData = sc.get(result, 'json_data', null);
|
|
303
|
+
if(sc.isString(jsonData)){
|
|
304
|
+
jsonData = sc.toJson(jsonData, {});
|
|
305
|
+
}
|
|
306
|
+
if(!sc.isObject(jsonData)){
|
|
307
|
+
jsonData = {};
|
|
308
|
+
}
|
|
309
|
+
let viewportValue = sc.get(jsonData, 'viewport', this.metaDefaults.viewport);
|
|
310
|
+
if(viewportValue && '' !== viewportValue){
|
|
311
|
+
jsonData.viewport = viewportValue;
|
|
312
|
+
}
|
|
313
|
+
result.json_data = jsonData;
|
|
314
|
+
return result;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
async handleSearchRequest(req, res)
|
|
318
|
+
{
|
|
319
|
+
try {
|
|
320
|
+
let domain = this.getDomainFromRequest(req);
|
|
321
|
+
let config = this.search.parseSearchParameters(req.query);
|
|
322
|
+
if(!config){
|
|
323
|
+
return res.redirect('/?error-message=searchInvalidParameters');
|
|
324
|
+
}
|
|
325
|
+
let cacheKey = this.buildCacheKey(req.path, req);
|
|
326
|
+
if(this.cacheManager && this.cacheManager.isEnabled()){
|
|
327
|
+
let cachedContent = await this.cacheManager.get(domain, cacheKey);
|
|
328
|
+
if(cachedContent){
|
|
329
|
+
return res.send(cachedContent);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
let searchResults = await this.search.executeSearch(config);
|
|
333
|
+
if(false === searchResults){
|
|
334
|
+
return res.redirect('/?error-message=searchExecutionFailed');
|
|
335
|
+
}
|
|
336
|
+
let content = await this.renderWithTemplateContent(
|
|
337
|
+
{
|
|
338
|
+
template: config.render.page,
|
|
339
|
+
layout: config.render.layout,
|
|
340
|
+
content: await this.searchRenderer.renderSearchResults(searchResults, config, domain, req)
|
|
341
|
+
},
|
|
342
|
+
Object.assign({}, {
|
|
343
|
+
search_query: sc.get(req.query, 'search', ''),
|
|
344
|
+
searchConfig: config,
|
|
345
|
+
query: req.query
|
|
346
|
+
}),
|
|
347
|
+
domain,
|
|
348
|
+
req
|
|
349
|
+
);
|
|
350
|
+
if(this.cacheManager && this.cacheManager.isEnabled()){
|
|
351
|
+
await this.cacheManager.set(domain, cacheKey, content);
|
|
352
|
+
}
|
|
353
|
+
return res.send(content);
|
|
354
|
+
} catch (error) {
|
|
355
|
+
Logger.error('Search request handling error: ' + error.message);
|
|
356
|
+
return res.redirect('/?error-message=searchError');
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
249
360
|
async handleRequest(req, res)
|
|
250
361
|
{
|
|
251
362
|
try {
|
|
252
363
|
let path = req.path;
|
|
253
364
|
let domain = this.getDomainFromRequest(req);
|
|
254
|
-
|
|
255
|
-
|
|
365
|
+
if(this.cacheManager && this.cacheManager.isEnabled()){
|
|
366
|
+
let cachedContent = await this.cacheManager.get(domain, path);
|
|
367
|
+
if(cachedContent){
|
|
368
|
+
return res.send(cachedContent);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
256
371
|
let route = await this.findRouteByPath(path, domain);
|
|
257
372
|
if(route){
|
|
258
|
-
return await this.
|
|
373
|
+
return await this.renderRouteWithCache(route, domain, res, path, req);
|
|
259
374
|
}
|
|
260
375
|
let entityResult = await this.findEntityByPath(path);
|
|
261
376
|
if(entityResult){
|
|
262
|
-
return await this.
|
|
377
|
+
return await this.renderEntityWithCache(entityResult, domain, res, path, req);
|
|
263
378
|
}
|
|
264
379
|
let templatePath = this.findTemplateByPath(path, domain);
|
|
265
380
|
if(templatePath){
|
|
266
|
-
return await this.
|
|
381
|
+
return await this.renderTemplateWithCache(templatePath, domain, res, path, req);
|
|
267
382
|
}
|
|
268
|
-
return await this.renderNotFound(domain, res);
|
|
383
|
+
return await this.renderNotFound(domain, res, req);
|
|
269
384
|
} catch (error) {
|
|
270
385
|
Logger.error('Request handling error: '+error.message);
|
|
271
386
|
return res.status(500).send('Internal server error');
|
|
@@ -276,23 +391,27 @@ class Frontend
|
|
|
276
391
|
{
|
|
277
392
|
let routesEntity = this.dataServer.getEntity('routes');
|
|
278
393
|
if(!routesEntity){
|
|
279
|
-
Logger.error('Routes entity not found in dataServer');
|
|
394
|
+
Logger.error('Routes entity not found in dataServer.');
|
|
280
395
|
return false;
|
|
281
396
|
}
|
|
282
397
|
let domainFilter = domain || null;
|
|
283
398
|
let routeFilters = {path, enabled: 1};
|
|
284
399
|
let routes = await routesEntity.load(routeFilters);
|
|
285
400
|
let matchingRoute = false;
|
|
401
|
+
let nullDomain = false;
|
|
286
402
|
for(let route of routes){
|
|
287
|
-
if(
|
|
403
|
+
if(route.domain === domainFilter){
|
|
288
404
|
matchingRoute = route;
|
|
289
405
|
break;
|
|
290
406
|
}
|
|
407
|
+
if(!route.domain){
|
|
408
|
+
nullDomain = route;
|
|
409
|
+
}
|
|
291
410
|
}
|
|
292
411
|
if(matchingRoute){
|
|
293
412
|
return matchingRoute;
|
|
294
413
|
}
|
|
295
|
-
return
|
|
414
|
+
return nullDomain;
|
|
296
415
|
}
|
|
297
416
|
|
|
298
417
|
async isEntityAccessible(entityName)
|
|
@@ -338,107 +457,205 @@ class Frontend
|
|
|
338
457
|
return this.findTemplatePath(templatePath, domain);
|
|
339
458
|
}
|
|
340
459
|
|
|
341
|
-
async
|
|
460
|
+
async renderRouteWithCache(route, domain, res, path, req)
|
|
461
|
+
{
|
|
462
|
+
let renderedContent = await this.generateRouteContent(route, domain, req);
|
|
463
|
+
if(!renderedContent){
|
|
464
|
+
return await this.renderNotFound(domain, res, req);
|
|
465
|
+
}
|
|
466
|
+
if(this.cacheManager && this.cacheManager.isEnabled()){
|
|
467
|
+
let cacheKey = this.buildCacheKey(path, req);
|
|
468
|
+
await this.cacheManager.set(domain, cacheKey, renderedContent);
|
|
469
|
+
}
|
|
470
|
+
return res.send(renderedContent);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
async renderEntityWithCache(entityResult, domain, res, path, req)
|
|
474
|
+
{
|
|
475
|
+
let renderedContent = await this.generateEntityContent(entityResult, domain, req);
|
|
476
|
+
if(!renderedContent){
|
|
477
|
+
return await this.renderNotFound(domain, res, req);
|
|
478
|
+
}
|
|
479
|
+
if(this.cacheManager && this.cacheManager.isEnabled()){
|
|
480
|
+
let cacheKey = this.buildCacheKey(path, req);
|
|
481
|
+
await this.cacheManager.set(domain, cacheKey, renderedContent);
|
|
482
|
+
}
|
|
483
|
+
return res.send(renderedContent);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
async renderTemplateWithCache(templatePath, domain, res, path, req)
|
|
487
|
+
{
|
|
488
|
+
let renderedContent = await this.generateTemplateContent(templatePath, domain, req);
|
|
489
|
+
if(!renderedContent){
|
|
490
|
+
return res.status(500).send('Template error: '+templatePath);
|
|
491
|
+
}
|
|
492
|
+
if(this.cacheManager && this.cacheManager.isEnabled()){
|
|
493
|
+
let cacheKey = this.buildCacheKey(path, req);
|
|
494
|
+
await this.cacheManager.set(domain, cacheKey, renderedContent);
|
|
495
|
+
}
|
|
496
|
+
return await this.renderWithLayout({content: renderedContent}, {}, 'default', domain, res, req);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
async generateRouteContent(route, domain, req)
|
|
342
500
|
{
|
|
343
|
-
if(!route.router
|
|
344
|
-
return
|
|
501
|
+
if(!route.router){
|
|
502
|
+
return false;
|
|
345
503
|
}
|
|
346
504
|
let entity = this.dataServer.getEntity(route.router);
|
|
347
505
|
if(!entity){
|
|
348
|
-
return
|
|
506
|
+
return false;
|
|
349
507
|
}
|
|
350
|
-
let content = await entity.
|
|
508
|
+
let content = await entity.loadOne({route_id: route.id});
|
|
351
509
|
if(!content){
|
|
352
|
-
return
|
|
510
|
+
return false;
|
|
353
511
|
}
|
|
354
|
-
return await this.
|
|
355
|
-
content,
|
|
356
|
-
Object.assign({}, route, content),
|
|
357
|
-
sc.get(content, 'layout', 'default'),
|
|
358
|
-
domain,
|
|
359
|
-
res
|
|
360
|
-
);
|
|
512
|
+
return await this.renderWithTemplateContent(content, Object.assign({}, route, content), domain, req);
|
|
361
513
|
}
|
|
362
514
|
|
|
363
|
-
async
|
|
515
|
+
async generateEntityContent(entityResult, domain, req)
|
|
364
516
|
{
|
|
365
|
-
return await this.
|
|
366
|
-
entityResult.entity,
|
|
367
|
-
Object.assign({}, entityResult.entity),
|
|
368
|
-
sc.get(entityResult.entity, 'layout', 'default'),
|
|
369
|
-
domain,
|
|
370
|
-
res
|
|
371
|
-
);
|
|
517
|
+
return await this.renderWithTemplateContent(entityResult.entity, entityResult.entity, domain, req);
|
|
372
518
|
}
|
|
373
519
|
|
|
374
|
-
async
|
|
520
|
+
async generateTemplateContent(templatePath, domain, req)
|
|
375
521
|
{
|
|
376
|
-
let
|
|
522
|
+
let template = FileHandler.readFile(templatePath);
|
|
523
|
+
if(!template){
|
|
524
|
+
Logger.error('Failed to read template: ' + templatePath);
|
|
525
|
+
return false;
|
|
526
|
+
}
|
|
527
|
+
return await this.templateEngine.render(template, {}, this.getPartialsForDomain(domain), domain, req);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
async renderRoute(route, domain, res, req)
|
|
531
|
+
{
|
|
532
|
+
if(!route.router){
|
|
533
|
+
return await this.renderNotFound(domain, res, req);
|
|
534
|
+
}
|
|
535
|
+
let entity = this.dataServer.getEntity(route.router);
|
|
536
|
+
if(!entity){
|
|
537
|
+
return await this.renderNotFound(domain, res, req);
|
|
538
|
+
}
|
|
539
|
+
let content = await entity.loadOne({route_id: route.id});
|
|
377
540
|
if(!content){
|
|
378
|
-
return
|
|
541
|
+
return await this.renderNotFound(domain, res, req);
|
|
379
542
|
}
|
|
380
|
-
return await this.
|
|
543
|
+
return await this.renderWithTemplate(content, Object.assign({}, route, content), domain, res, req);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
async renderWithTemplate(content, data, domain, res, req)
|
|
547
|
+
{
|
|
548
|
+
return res.send(await this.renderWithTemplateContent(content, data, domain, req));
|
|
381
549
|
}
|
|
382
550
|
|
|
383
|
-
async
|
|
551
|
+
async renderWithTemplateContent(content, data, domain, req)
|
|
552
|
+
{
|
|
553
|
+
let templateName = sc.get(content, 'template', 'page');
|
|
554
|
+
if(!templateName){
|
|
555
|
+
templateName = 'page';
|
|
556
|
+
}
|
|
557
|
+
let layoutName = sc.get(content, 'layout', '');
|
|
558
|
+
if(!layoutName){
|
|
559
|
+
layoutName = 'default';
|
|
560
|
+
}
|
|
561
|
+
let layoutContent = await this.processContentWithLayout(content, data, layoutName, domain, req);
|
|
562
|
+
let templatePath = this.findTemplatePath(templateName, domain);
|
|
563
|
+
if(!templatePath){
|
|
564
|
+
return layoutContent;
|
|
565
|
+
}
|
|
566
|
+
let pageTemplate = FileHandler.readFile(templatePath);
|
|
567
|
+
if(!pageTemplate){
|
|
568
|
+
return layoutContent;
|
|
569
|
+
}
|
|
570
|
+
return await this.templateEngine.render(
|
|
571
|
+
pageTemplate,
|
|
572
|
+
Object.assign({}, this.fetchMetaFields(data), {content: layoutContent, siteHandle: this.resolveDomainToSiteKey(domain)}),
|
|
573
|
+
this.getPartialsForDomain(domain),
|
|
574
|
+
domain,
|
|
575
|
+
req
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
async renderContentWithTemplate(templatePath, data, domain, req)
|
|
384
580
|
{
|
|
385
581
|
let template = FileHandler.readFile(templatePath);
|
|
386
582
|
if(!template){
|
|
387
583
|
Logger.error('Failed to read template: ' + templatePath);
|
|
388
584
|
return false;
|
|
389
585
|
}
|
|
390
|
-
return await this.templateEngine.render(template, data, this.getPartialsForDomain(domain));
|
|
586
|
+
return await this.templateEngine.render(template, data, this.getPartialsForDomain(domain), domain, req);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
async renderWithLayout(content, data, layoutName, domain, res, req)
|
|
590
|
+
{
|
|
591
|
+
return res.send(await this.renderWithTemplateContent(content, data, domain, req));
|
|
391
592
|
}
|
|
392
593
|
|
|
393
|
-
async
|
|
594
|
+
async processContentWithLayout(content, data, layoutName, domain, req)
|
|
394
595
|
{
|
|
596
|
+
let processedContent = await this.processContent(content, data, domain, req);
|
|
395
597
|
let layoutPath = this.findLayoutPath(layoutName, domain);
|
|
396
|
-
|
|
397
|
-
|
|
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
|
-
}
|
|
598
|
+
if(!layoutPath){
|
|
599
|
+
return processedContent;
|
|
414
600
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
601
|
+
let layoutTemplate = FileHandler.readFile(layoutPath);
|
|
602
|
+
if(!layoutTemplate){
|
|
603
|
+
return processedContent;
|
|
604
|
+
}
|
|
605
|
+
return await this.templateEngine.render(
|
|
606
|
+
layoutTemplate,
|
|
607
|
+
Object.assign({}, data, {content: processedContent}),
|
|
608
|
+
this.getPartialsForDomain(domain),
|
|
609
|
+
domain,
|
|
610
|
+
req
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
async processContent(content, data, domain, req)
|
|
615
|
+
{
|
|
616
|
+
let contentText = sc.get(content, 'content', '');
|
|
617
|
+
if(!contentText){
|
|
618
|
+
return '';
|
|
424
619
|
}
|
|
425
|
-
|
|
426
|
-
return res.send(layoutContent);
|
|
620
|
+
return await this.templateEngine.render(contentText, data, this.getPartialsForDomain(domain), domain, req);
|
|
427
621
|
}
|
|
428
622
|
|
|
429
|
-
async renderNotFound(domain, res)
|
|
623
|
+
async renderNotFound(domain, res, req)
|
|
430
624
|
{
|
|
431
625
|
let notFoundPath = this.findTemplatePath('404', domain);
|
|
432
626
|
if(notFoundPath){
|
|
433
|
-
let content = await this.renderContentWithTemplate(notFoundPath, {}, domain);
|
|
627
|
+
let content = await this.renderContentWithTemplate(notFoundPath, {}, domain, req);
|
|
434
628
|
if(content){
|
|
435
629
|
res.status(404);
|
|
436
|
-
return await this.renderWithLayout({content}, {}, 'default', domain, res);
|
|
630
|
+
return await this.renderWithLayout({content}, {}, 'default', domain, res, req);
|
|
437
631
|
}
|
|
438
632
|
}
|
|
439
633
|
return res.status(404).send('Page not found');
|
|
440
634
|
}
|
|
441
635
|
|
|
636
|
+
buildCacheKey(path, req)
|
|
637
|
+
{
|
|
638
|
+
if(!req || !req.query){
|
|
639
|
+
return path;
|
|
640
|
+
}
|
|
641
|
+
for(let key of Object.keys(req.query)){
|
|
642
|
+
if(key.endsWith('-key')){
|
|
643
|
+
let queryString = '';
|
|
644
|
+
for(let qKey of Object.keys(req.query)){
|
|
645
|
+
queryString += (queryString ? '&' : '') + qKey + '=' + req.query[qKey];
|
|
646
|
+
}
|
|
647
|
+
let hash = 0;
|
|
648
|
+
for(let i = 0; i < queryString.length; i++){
|
|
649
|
+
let char = queryString.charCodeAt(i);
|
|
650
|
+
hash = ((hash << 5) - hash) + char;
|
|
651
|
+
hash = hash & hash;
|
|
652
|
+
}
|
|
653
|
+
return path + '_' + Math.abs(hash);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
return path;
|
|
657
|
+
}
|
|
658
|
+
|
|
442
659
|
}
|
|
443
660
|
|
|
444
661
|
module.exports.Frontend = Frontend;
|
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;
|