@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/lib/frontend.js CHANGED
@@ -1,268 +1,274 @@
1
- /**
2
- *
3
- * Reldens - CMS - Frontend
4
- *
5
- */
6
-
7
- const { TemplateEngine } = require('./template-engine');
8
- const { Search } = require('./search');
9
- const { SearchRenderer } = require('./search-renderer');
10
- const { SearchRequestHandler } = require('./search-request-handler');
11
- const { DynamicForm } = require('./dynamic-form');
12
- const { DynamicFormRenderer } = require('./dynamic-form-renderer');
13
- const { DynamicFormRequestHandler } = require('./dynamic-form-request-handler');
14
- const { TemplateResolver } = require('./frontend/template-resolver');
15
- const { TemplateCache } = require('./frontend/template-cache');
16
- const { RequestProcessor } = require('./frontend/request-processor');
17
- const { EntityAccessManager } = require('./frontend/entity-access-manager');
18
- const { ContentRenderer } = require('./frontend/content-renderer');
19
- const { ResponseManager } = require('./frontend/response-manager');
20
- const { FileHandler } = require('@reldens/server-utils');
21
- const { Logger, sc } = require('@reldens/utils');
22
-
23
- class Frontend
24
- {
25
-
26
- constructor(props)
27
- {
28
- this.app = sc.get(props, 'app', false);
29
- this.appServerFactory = sc.get(props, 'appServerFactory', false);
30
- this.dataServer = sc.get(props, 'dataServer', false);
31
- this.renderEngine = sc.get(props, 'renderEngine', false);
32
- this.events = sc.get(props, 'events', false);
33
- this.projectRoot = sc.get(props, 'projectRoot', './');
34
- this.templatesPath = FileHandler.joinPaths(this.projectRoot, 'templates');
35
- this.publicPath = FileHandler.joinPaths(this.projectRoot, 'public');
36
- this.templateExtensions = sc.get(props, 'templateExtensions', ['.html', '.mustache', '.template']);
37
- this.defaultDomain = sc.get(props, 'defaultDomain', 'default');
38
- this.domainMapping = sc.get(props, 'domainMapping', {});
39
- this.siteKeyMapping = sc.get(props, 'siteKeyMapping', {});
40
- this.entitiesConfig = sc.get(props, 'entitiesConfig', {});
41
- this.templateEngine = false;
42
- this.cacheManager = sc.get(props, 'cacheManager', false);
43
- this.handleFrontendTemplateReload = sc.get(props, 'handleFrontendTemplateReload', false);
44
- this.searchPath = sc.get(props, 'searchPath', '/search');
45
- this.dynamicFormPath = sc.get(props, 'dynamicFormPath', '/dynamic-form');
46
- this.dynamicFormDisplayPath = sc.get(props, 'dynamicFormDisplayPath', '/form');
47
- this.searchSets = sc.get(props, 'searchSets', false);
48
- this.searchConfig = {dataServer: this.dataServer};
49
- if(this.searchSets){
50
- this.searchConfig.searchSets = this.searchSets;
51
- }
52
- this.search = new Search(this.searchConfig);
53
- this.dynamicFormConfig = {
54
- dataServer: this.dataServer,
55
- allowedOrigins: sc.get(props, 'allowedOrigins', []),
56
- honeypotFieldName: sc.get(props, 'honeypotFieldName', 'website_url'),
57
- rateLimitWindow: sc.get(props, 'rateLimitWindow', 300000),
58
- rateLimitMax: sc.get(props, 'rateLimitMax', 5),
59
- events: this.events
60
- };
61
- this.dynamicForm = new DynamicForm(this.dynamicFormConfig);
62
- this.metaDefaults = sc.get(props, 'metaDefaults', {
63
- locale: 'en',
64
- viewport: 'width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes, viewport-fit=cover',
65
- meta_robots: 'index,follow',
66
- meta_theme_color: '#000000',
67
- meta_twitter_card_type: 'summary'
68
- });
69
- this.templateResolver = new TemplateResolver(this);
70
- this.templateCache = new TemplateCache(this);
71
- this.requestProcessor = new RequestProcessor(this);
72
- this.entityAccessManager = new EntityAccessManager(this);
73
- this.contentRenderer = new ContentRenderer(this);
74
- this.responseManager = new ResponseManager(this);
75
- this.searchRenderer = new SearchRenderer({
76
- renderEngine: this.renderEngine,
77
- getPartials: this.templateCache.getPartialsForDomain.bind(this.templateCache)
78
- });
79
- this.dynamicFormRenderer = new DynamicFormRenderer({
80
- renderEngine: this.renderEngine,
81
- getPartials: this.templateCache.getPartialsForDomain.bind(this.templateCache),
82
- projectRoot: this.projectRoot,
83
- defaultDomain: this.defaultDomain,
84
- events: this.events
85
- });
86
- this.searchRequestHandler = new SearchRequestHandler(this);
87
- this.dynamicFormRequestHandler = new DynamicFormRequestHandler({
88
- dynamicForm: this.dynamicForm,
89
- contentRenderer: this.contentRenderer,
90
- requestProcessor: this.requestProcessor,
91
- cacheManager: this.cacheManager,
92
- enableJsonResponse: sc.get(props, 'enableJsonResponse', false),
93
- events: this.events
94
- });
95
- }
96
-
97
- async initialize()
98
- {
99
- if(!this.app || !this.dataServer){
100
- Logger.error('Missing app or dataServer');
101
- return false;
102
- }
103
- if(!this.renderEngine){
104
- Logger.error('Please, provide a renderEngine, it must contain a "render" method.');
105
- return false;
106
- }
107
- if(!sc.isFunction(this.renderEngine.render)){
108
- Logger.error('The provided renderEngine does not contain a "render" method.');
109
- return false;
110
- }
111
- if(!FileHandler.exists(this.templatesPath)){
112
- Logger.error('Templates folder not found: '+this.templatesPath);
113
- return false;
114
- }
115
- if(!FileHandler.exists(this.publicPath)){
116
- Logger.error('Public folder not found: '+this.publicPath);
117
- return false;
118
- }
119
- this.templateResolver.domainTemplatesMap = this.templateCache.getDomainTemplatesMap();
120
- this.templateEngine = new TemplateEngine({
121
- renderEngine: this.renderEngine,
122
- dataServer: this.dataServer,
123
- getPartials: this.templateCache.getPartialsForDomain.bind(this.templateCache),
124
- entitiesConfig: this.entitiesConfig,
125
- events: this.events,
126
- defaultDomain: this.defaultDomain,
127
- projectRoot: this.projectRoot,
128
- publicPath: this.publicPath,
129
- dynamicForm: this.dynamicForm,
130
- dynamicFormRenderer: this.dynamicFormRenderer
131
- });
132
- this.contentRenderer.templateEngine = this.templateEngine;
133
- this.searchConfig.jsonFieldsParser = this.templateEngine.jsonFieldsParser;
134
- await this.templateCache.loadPartials();
135
- await this.templateCache.setupDomainTemplates();
136
- await this.entityAccessManager.loadEntityAccessRules();
137
- this.setupStaticAssets();
138
- this.app.get(this.searchPath, async (req, res) => {
139
- return await this.searchRequestHandler.handleSearchRequest(req, res);
140
- });
141
- this.app.post(this.dynamicFormPath, async (req, res) => {
142
- return await this.dynamicFormRequestHandler.handleFormSubmission(req, res);
143
- });
144
- this.app.get('*', async (req, res) => {
145
- return await this.handleRequest(req, res);
146
- });
147
- return true;
148
- }
149
-
150
- async handleRequest(req, res)
151
- {
152
- try {
153
- if(this.handleFrontendTemplateReload){
154
- await this.handleFrontendTemplateReload(this.templateCache, this.templateResolver);
155
- }
156
- let originalPath = req.path;
157
- let domain = this.requestProcessor.getDomainFromRequest(req);
158
- if(this.cacheManager && this.cacheManager.isEnabled()){
159
- let cachedContent = await this.cacheManager.get(domain, originalPath);
160
- if(cachedContent){
161
- return res.send(cachedContent);
162
- }
163
- }
164
- let route = await this.requestProcessor.findRouteByPath(originalPath, domain);
165
- if(route){
166
- let redirectResult = await this.requestProcessor.handleRouteRedirect(route, res);
167
- if(redirectResult){
168
- return redirectResult;
169
- }
170
- let normalizedPath = this.requestProcessor.normalizePathForRouteSearch(originalPath);
171
- let routeFoundWithSlash = originalPath !== normalizedPath && route.path === normalizedPath + '/';
172
- if(!originalPath.endsWith('/') && routeFoundWithSlash){
173
- return res.redirect(301, originalPath + '/');
174
- }
175
- return await this.responseManager.renderWithCacheHandler(
176
- async () => await this.contentRenderer.generateRouteContent(route, domain, req),
177
- async () => await this.responseManager.renderNotFound(domain, res, req),
178
- async (content) => res.send(content),
179
- domain,
180
- res,
181
- originalPath,
182
- req
183
- );
184
- }
185
- if(!originalPath.endsWith('/') && '/' !== originalPath){
186
- let routeWithSlash = await this.requestProcessor.findRouteByPath(originalPath + '/', domain);
187
- if(routeWithSlash){
188
- let redirectResult = await this.requestProcessor.handleRouteRedirect(routeWithSlash, res);
189
- if(redirectResult){
190
- return redirectResult;
191
- }
192
- return res.redirect(301, originalPath + '/');
193
- }
194
- }
195
- let entityResult = await this.entityAccessManager.findEntityByPath(originalPath);
196
- if(entityResult){
197
- return await this.responseManager.renderWithCacheHandler(
198
- async () => await this.contentRenderer.renderWithTemplateContent(
199
- entityResult.entity,
200
- {},
201
- domain,
202
- req,
203
- null
204
- ),
205
- async () => await this.responseManager.renderNotFound(domain, res, req),
206
- async (content) => res.send(content),
207
- domain,
208
- res,
209
- originalPath,
210
- req
211
- );
212
- }
213
- let templatePath = this.templateResolver.findTemplateByPath(originalPath, domain);
214
- if(templatePath){
215
- return await this.responseManager.renderWithCacheHandler(
216
- async () => await this.contentRenderer.generateTemplateContent(templatePath, domain, req),
217
- async () => res.status(500).send('Template error: '+templatePath),
218
- async (content) => res.send(await this.contentRenderer.renderWithTemplateContent({content}, {}, domain, req, null)),
219
- domain,
220
- res,
221
- originalPath,
222
- req
223
- );
224
- }
225
- return await this.responseManager.renderNotFound(domain, res, req);
226
- } catch (error) {
227
- Logger.error('Request handling error: '+error.message);
228
- return res.status(500).send('Internal server error');
229
- }
230
- }
231
-
232
- async renderRoute(route, domain, res, req)
233
- {
234
- if(!route.router){
235
- return await this.responseManager.renderNotFound(domain, res, req);
236
- }
237
- let entity = this.dataServer.getEntity(route.router);
238
- if(!entity){
239
- return await this.responseManager.renderNotFound(domain, res, req);
240
- }
241
- let content = await entity.loadOne({route_id: route.id});
242
- if(!content){
243
- return await this.responseManager.renderNotFound(domain, res, req);
244
- }
245
- return res.send(await this.contentRenderer.renderWithTemplateContent(
246
- content,
247
- Object.assign({}, route, content),
248
- domain,
249
- req,
250
- null
251
- ));
252
- }
253
-
254
- setupStaticAssets()
255
- {
256
- if(!this.app || !this.appServerFactory || !this.publicPath){
257
- return false;
258
- }
259
- if(this.appServerFactory && this.appServerFactory.applicationFramework){
260
- this.app.use(this.appServerFactory.applicationFramework.static(this.publicPath));
261
- return true;
262
- }
263
- return false;
264
- }
265
-
266
- }
267
-
268
- module.exports.Frontend = Frontend;
1
+ /**
2
+ *
3
+ * Reldens - CMS - Frontend
4
+ *
5
+ */
6
+
7
+ const { TemplateEngine } = require('./template-engine');
8
+ const { Search } = require('./search');
9
+ const { SearchRenderer } = require('./search-renderer');
10
+ const { SearchRequestHandler } = require('./search-request-handler');
11
+ const { DynamicForm } = require('./dynamic-form');
12
+ const { DynamicFormRenderer } = require('./dynamic-form-renderer');
13
+ const { DynamicFormRequestHandler } = require('./dynamic-form-request-handler');
14
+ const { TemplateResolver } = require('./frontend/template-resolver');
15
+ const { TemplateCache } = require('./frontend/template-cache');
16
+ const { RequestProcessor } = require('./frontend/request-processor');
17
+ const { EntityAccessManager } = require('./frontend/entity-access-manager');
18
+ const { ContentRenderer } = require('./frontend/content-renderer');
19
+ const { ResponseManager } = require('./frontend/response-manager');
20
+ const { FileHandler } = require('@reldens/server-utils');
21
+ const { Logger, sc } = require('@reldens/utils');
22
+
23
+ class Frontend
24
+ {
25
+
26
+ constructor(props)
27
+ {
28
+ this.app = sc.get(props, 'app', false);
29
+ this.appServerFactory = sc.get(props, 'appServerFactory', false);
30
+ this.dataServer = sc.get(props, 'dataServer', false);
31
+ this.renderEngine = sc.get(props, 'renderEngine', false);
32
+ this.events = sc.get(props, 'events', false);
33
+ this.projectRoot = sc.get(props, 'projectRoot', './');
34
+ this.templatesPath = FileHandler.joinPaths(this.projectRoot, 'templates');
35
+ this.publicPath = FileHandler.joinPaths(this.projectRoot, 'public');
36
+ this.templateExtensions = sc.get(props, 'templateExtensions', ['.html', '.mustache', '.template']);
37
+ this.defaultDomain = sc.get(props, 'defaultDomain', 'default');
38
+ this.domainMapping = sc.get(props, 'domainMapping', {});
39
+ this.siteKeyMapping = sc.get(props, 'siteKeyMapping', {});
40
+ this.domainPublicUrlMapping = sc.get(props, 'domainPublicUrlMapping', {});
41
+ this.defaultPublicUrl = sc.get(props, 'defaultPublicUrl', '');
42
+ this.entitiesConfig = sc.get(props, 'entitiesConfig', {});
43
+ this.templateEngine = false;
44
+ this.cacheManager = sc.get(props, 'cacheManager', false);
45
+ this.handleFrontendTemplateReload = sc.get(props, 'handleFrontendTemplateReload', false);
46
+ this.searchPath = sc.get(props, 'searchPath', '/search');
47
+ this.dynamicFormPath = sc.get(props, 'dynamicFormPath', '/dynamic-form');
48
+ this.dynamicFormDisplayPath = sc.get(props, 'dynamicFormDisplayPath', '/form');
49
+ this.searchSets = sc.get(props, 'searchSets', false);
50
+ this.searchConfig = {dataServer: this.dataServer};
51
+ if(this.searchSets){
52
+ this.searchConfig.searchSets = this.searchSets;
53
+ }
54
+ this.search = new Search(this.searchConfig);
55
+ this.dynamicFormConfig = {
56
+ dataServer: this.dataServer,
57
+ allowedOrigins: sc.get(props, 'allowedOrigins', []),
58
+ honeypotFieldName: sc.get(props, 'honeypotFieldName', 'website_url'),
59
+ rateLimitWindow: sc.get(props, 'rateLimitWindow', 300000),
60
+ rateLimitMax: sc.get(props, 'rateLimitMax', 5),
61
+ events: this.events
62
+ };
63
+ this.dynamicForm = new DynamicForm(this.dynamicFormConfig);
64
+ this.metaDefaults = sc.get(props, 'metaDefaults', {
65
+ locale: 'en',
66
+ viewport: 'width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes, viewport-fit=cover',
67
+ meta_robots: 'index,follow',
68
+ meta_theme_color: '#000000',
69
+ meta_twitter_card_type: 'summary'
70
+ });
71
+ this.templateResolver = new TemplateResolver(this);
72
+ this.templateCache = new TemplateCache(this);
73
+ this.requestProcessor = new RequestProcessor(this);
74
+ this.entityAccessManager = new EntityAccessManager(this);
75
+ this.contentRenderer = new ContentRenderer(this);
76
+ this.responseManager = new ResponseManager(this);
77
+ this.searchRenderer = new SearchRenderer({
78
+ renderEngine: this.renderEngine,
79
+ getPartials: this.templateCache.getPartialsForDomain.bind(this.templateCache)
80
+ });
81
+ this.dynamicFormRenderer = new DynamicFormRenderer({
82
+ renderEngine: this.renderEngine,
83
+ getPartials: this.templateCache.getPartialsForDomain.bind(this.templateCache),
84
+ projectRoot: this.projectRoot,
85
+ defaultDomain: this.defaultDomain,
86
+ events: this.events
87
+ });
88
+ this.searchRequestHandler = new SearchRequestHandler(this);
89
+ this.dynamicFormRequestHandler = new DynamicFormRequestHandler({
90
+ dynamicForm: this.dynamicForm,
91
+ contentRenderer: this.contentRenderer,
92
+ requestProcessor: this.requestProcessor,
93
+ cacheManager: this.cacheManager,
94
+ enableJsonResponse: sc.get(props, 'enableJsonResponse', false),
95
+ events: this.events
96
+ });
97
+ }
98
+
99
+ async initialize()
100
+ {
101
+ if(!this.app || !this.dataServer){
102
+ Logger.error('Missing app or dataServer');
103
+ return false;
104
+ }
105
+ if(!this.renderEngine){
106
+ Logger.error('Please, provide a renderEngine, it must contain a "render" method.');
107
+ return false;
108
+ }
109
+ if(!sc.isFunction(this.renderEngine.render)){
110
+ Logger.error('The provided renderEngine does not contain a "render" method.');
111
+ return false;
112
+ }
113
+ if(!FileHandler.exists(this.templatesPath)){
114
+ Logger.error('Templates folder not found: '+this.templatesPath);
115
+ return false;
116
+ }
117
+ if(!FileHandler.exists(this.publicPath)){
118
+ Logger.error('Public folder not found: '+this.publicPath);
119
+ return false;
120
+ }
121
+ this.templateResolver.domainTemplatesMap = this.templateCache.getDomainTemplatesMap();
122
+ this.templateEngine = new TemplateEngine({
123
+ renderEngine: this.renderEngine,
124
+ dataServer: this.dataServer,
125
+ getPartials: this.templateCache.getPartialsForDomain.bind(this.templateCache),
126
+ entitiesConfig: this.entitiesConfig,
127
+ events: this.events,
128
+ defaultDomain: this.defaultDomain,
129
+ projectRoot: this.projectRoot,
130
+ publicPath: this.publicPath,
131
+ domainPublicUrlMapping: this.domainPublicUrlMapping,
132
+ defaultPublicUrl: this.defaultPublicUrl,
133
+ dynamicForm: this.dynamicForm,
134
+ dynamicFormRenderer: this.dynamicFormRenderer
135
+ });
136
+ this.contentRenderer.templateEngine = this.templateEngine;
137
+ this.searchConfig.jsonFieldsParser = this.templateEngine.jsonFieldsParser;
138
+ await this.templateCache.loadPartials();
139
+ await this.templateCache.setupDomainTemplates();
140
+ await this.entityAccessManager.loadEntityAccessRules();
141
+ this.setupStaticAssets();
142
+ this.app.get(this.searchPath, async (req, res) => {
143
+ return await this.searchRequestHandler.handleSearchRequest(req, res);
144
+ });
145
+ this.app.post(this.dynamicFormPath, async (req, res) => {
146
+ return await this.dynamicFormRequestHandler.handleFormSubmission(req, res);
147
+ });
148
+ this.app.get('*', async (req, res) => {
149
+ return await this.handleRequest(req, res);
150
+ });
151
+ return true;
152
+ }
153
+
154
+ async handleRequest(req, res)
155
+ {
156
+ try {
157
+ if(this.handleFrontendTemplateReload){
158
+ await this.handleFrontendTemplateReload(this.templateCache, this.templateResolver);
159
+ }
160
+ let originalPath = req.path;
161
+ let domain = this.requestProcessor.getDomainFromRequest(req);
162
+ if(this.cacheManager && this.cacheManager.isEnabled()){
163
+ let cachedContent = await this.cacheManager.get(domain, originalPath);
164
+ if(cachedContent){
165
+ return res.send(cachedContent);
166
+ }
167
+ }
168
+ let route = await this.requestProcessor.findRouteByPath(originalPath, domain);
169
+ if(route){
170
+ let redirectResult = await this.requestProcessor.handleRouteRedirect(route, res);
171
+ if(redirectResult){
172
+ return redirectResult;
173
+ }
174
+ let normalizedPath = this.requestProcessor.normalizePathForRouteSearch(originalPath);
175
+ let routeFoundWithSlash = originalPath !== normalizedPath && route.path === normalizedPath + '/';
176
+ if(!originalPath.endsWith('/') && routeFoundWithSlash){
177
+ return res.redirect(301, originalPath + '/');
178
+ }
179
+ return await this.responseManager.renderWithCacheHandler(
180
+ async () => await this.contentRenderer.generateRouteContent(route, domain, req),
181
+ async () => await this.responseManager.renderNotFound(domain, res, req),
182
+ async (content) => res.send(content),
183
+ domain,
184
+ res,
185
+ originalPath,
186
+ req
187
+ );
188
+ }
189
+ if(!originalPath.endsWith('/') && '/' !== originalPath){
190
+ let routeWithSlash = await this.requestProcessor.findRouteByPath(originalPath + '/', domain);
191
+ if(routeWithSlash){
192
+ let redirectResult = await this.requestProcessor.handleRouteRedirect(routeWithSlash, res);
193
+ if(redirectResult){
194
+ return redirectResult;
195
+ }
196
+ return res.redirect(301, originalPath + '/');
197
+ }
198
+ }
199
+ let entityResult = await this.entityAccessManager.findEntityByPath(originalPath);
200
+ if(entityResult){
201
+ return await this.responseManager.renderWithCacheHandler(
202
+ async () => await this.contentRenderer.renderWithTemplateContent(
203
+ entityResult.entity,
204
+ {},
205
+ domain,
206
+ req,
207
+ null
208
+ ),
209
+ async () => await this.responseManager.renderNotFound(domain, res, req),
210
+ async (content) => res.send(content),
211
+ domain,
212
+ res,
213
+ originalPath,
214
+ req
215
+ );
216
+ }
217
+ let templatePath = this.templateResolver.findTemplateByPath(originalPath, domain);
218
+ if(templatePath){
219
+ return await this.responseManager.renderWithCacheHandler(
220
+ async () => await this.contentRenderer.generateTemplateContent(templatePath, domain, req),
221
+ async () => res.status(500).send('Template error: '+templatePath),
222
+ async (content) => res.send(
223
+ await this.contentRenderer.renderWithTemplateContent({content}, {}, domain, req, null)
224
+ ),
225
+ domain,
226
+ res,
227
+ originalPath,
228
+ req
229
+ );
230
+ }
231
+ return await this.responseManager.renderNotFound(domain, res, req);
232
+ } catch (error) {
233
+ Logger.error('Request handling error: '+error.message);
234
+ return res.status(500).send('Internal server error');
235
+ }
236
+ }
237
+
238
+ async renderRoute(route, domain, res, req)
239
+ {
240
+ if(!route.router){
241
+ return await this.responseManager.renderNotFound(domain, res, req);
242
+ }
243
+ let entity = this.dataServer.getEntity(route.router);
244
+ if(!entity){
245
+ return await this.responseManager.renderNotFound(domain, res, req);
246
+ }
247
+ let content = await entity.loadOne({route_id: route.id});
248
+ if(!content){
249
+ return await this.responseManager.renderNotFound(domain, res, req);
250
+ }
251
+ return res.send(await this.contentRenderer.renderWithTemplateContent(
252
+ content,
253
+ Object.assign({}, route, content),
254
+ domain,
255
+ req,
256
+ null
257
+ ));
258
+ }
259
+
260
+ setupStaticAssets()
261
+ {
262
+ if(!this.app || !this.appServerFactory || !this.publicPath){
263
+ return false;
264
+ }
265
+ if(this.appServerFactory && this.appServerFactory.applicationFramework){
266
+ this.app.use(this.appServerFactory.applicationFramework.static(this.publicPath));
267
+ return true;
268
+ }
269
+ return false;
270
+ }
271
+
272
+ }
273
+
274
+ module.exports.Frontend = Frontend;