@reldens/cms 0.7.0 → 0.9.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/admin/reldens-admin-client.css +28 -29
- package/install/index.html +23 -2
- package/install/success.html +6 -0
- package/lib/admin-manager.js +40 -3
- package/lib/entities-loader.js +1 -2
- package/lib/frontend.js +153 -29
- package/lib/installer.js +109 -78
- package/lib/manager.js +154 -46
- package/migrations/default-homepage.sql +10 -0
- package/migrations/install.sql +1 -23
- package/migrations/users-authentication.sql +16 -0
- package/package.json +3 -3
- package/templates/.env.dist +11 -11
- package/templates/assets/favicons/android-icon-144x144.png +0 -0
- package/templates/assets/favicons/android-icon-192x192.png +0 -0
- package/templates/assets/favicons/android-icon-512x512.png +0 -0
- package/templates/assets/favicons/apple-touch-icon.png +0 -0
- package/templates/assets/favicons/favicon-16x16.png +0 -0
- package/templates/assets/favicons/favicon-32x32.png +0 -0
- package/templates/assets/favicons/mstile-150x150.png +0 -0
- package/templates/assets/favicons/safari-pinned-tab.svg +121 -0
- package/templates/browserconfig.xml +9 -0
- package/templates/favicon.ico +0 -0
- package/templates/site.webmanifest +27 -0
|
@@ -86,7 +86,6 @@
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
& .button {
|
|
89
|
-
|
|
90
89
|
padding: 0.5rem 1rem;
|
|
91
90
|
border: none;
|
|
92
91
|
border-radius: 4px;
|
|
@@ -94,44 +93,44 @@
|
|
|
94
93
|
cursor: pointer;
|
|
95
94
|
text-decoration: none;
|
|
96
95
|
|
|
97
|
-
|
|
98
|
-
color: var(--
|
|
99
|
-
background-color: var(--lightBlue);
|
|
100
|
-
|
|
101
|
-
&:hover {
|
|
102
|
-
background-color: var(--lightBlue);
|
|
103
|
-
}
|
|
96
|
+
&:disabled {
|
|
97
|
+
background-color: var(--grey) !important;
|
|
104
98
|
}
|
|
99
|
+
}
|
|
105
100
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
101
|
+
& .button-primary {
|
|
102
|
+
color: var(--white);
|
|
103
|
+
background-color: var(--lightBlue);
|
|
109
104
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
105
|
+
&:hover {
|
|
106
|
+
background-color: var(--lightBlue);
|
|
113
107
|
}
|
|
108
|
+
}
|
|
114
109
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
110
|
+
& .button-secondary {
|
|
111
|
+
color: var(--white);
|
|
112
|
+
background-color: var(--grey);
|
|
118
113
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
114
|
+
&:hover {
|
|
115
|
+
background-color: var(--grey);
|
|
122
116
|
}
|
|
117
|
+
}
|
|
123
118
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
119
|
+
& .button-warning {
|
|
120
|
+
color: var(--white);
|
|
121
|
+
background-color: var(--orange);
|
|
127
122
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
123
|
+
&:hover {
|
|
124
|
+
background-color: var(--orange);
|
|
131
125
|
}
|
|
126
|
+
}
|
|
132
127
|
|
|
133
|
-
|
|
134
|
-
|
|
128
|
+
& .button-danger {
|
|
129
|
+
color: var(--white);
|
|
130
|
+
background-color: var(--red);
|
|
131
|
+
|
|
132
|
+
&:hover {
|
|
133
|
+
background-color: var(--red);
|
|
135
134
|
}
|
|
136
135
|
}
|
|
137
136
|
|
|
@@ -556,7 +555,7 @@
|
|
|
556
555
|
padding: 0.5rem;
|
|
557
556
|
border: 1px solid #ccc;
|
|
558
557
|
border-radius: 4px;
|
|
559
|
-
min-width: 150px;
|
|
558
|
+
min-width: 150px;
|
|
560
559
|
}
|
|
561
560
|
}
|
|
562
561
|
|
package/install/index.html
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
<div class="forms-container">
|
|
18
18
|
<div class="row">
|
|
19
19
|
<form name="install-form" id="install-form" class="install-form" action="/install" method="post">
|
|
20
|
-
<h3 class="form-title"
|
|
20
|
+
<h3 class="form-title">Server Configuration</h3>
|
|
21
21
|
<div class="input-box app-host">
|
|
22
22
|
<label for="app-host">Host</label>
|
|
23
23
|
<input type="text" name="app-host" id="app-host" value="{{app-host}}" class="required" required/>
|
|
@@ -30,10 +30,14 @@
|
|
|
30
30
|
<label for="app-admin-path">Admin Panel Path</label>
|
|
31
31
|
<input type="text" name="app-admin-path" id="app-admin-path" value="{{app-admin-path}}"/>
|
|
32
32
|
</div>
|
|
33
|
+
<div class="input-box app-admin-secret">
|
|
34
|
+
<label for="app-admin-secret">Admin Panel Secret</label>
|
|
35
|
+
<input type="text" name="app-admin-secret" id="app-admin-secret" value="{{app-admin-secret}}"/>
|
|
36
|
+
</div>
|
|
33
37
|
<div class="input-box app-error">
|
|
34
38
|
<p class="error installation-process-failed">There was an error during the installation process.</p>
|
|
35
39
|
</div>
|
|
36
|
-
<h3 class="form-title"
|
|
40
|
+
<h3 class="form-title">Database Configuration</h3>
|
|
37
41
|
<div class="input-box db-storage-driver">
|
|
38
42
|
<label for="db-storage-driver">Storage Driver</label>
|
|
39
43
|
<select name="db-storage-driver" id="db-storage-driver" class="required" required>
|
|
@@ -72,6 +76,23 @@
|
|
|
72
76
|
<p class="error sql-file-not-found">SQL installation file not found.</p>
|
|
73
77
|
<p class="error db-installation-process-failed">There was an error during the installation process.</p>
|
|
74
78
|
</div>
|
|
79
|
+
<h3 class="form-title">Installation Options</h3>
|
|
80
|
+
<div class="input-box install-cms-tables">
|
|
81
|
+
<input type="checkbox" name="install-cms-tables" id="install-cms-tables" checked/>
|
|
82
|
+
<label for="install-cms-tables">Install default CMS pages tables</label>
|
|
83
|
+
</div>
|
|
84
|
+
<div class="input-box install-user-auth">
|
|
85
|
+
<input type="checkbox" name="install-user-auth" id="install-user-auth" checked/>
|
|
86
|
+
<label for="install-user-auth">Install users authentication</label>
|
|
87
|
+
</div>
|
|
88
|
+
<div class="input-box install-default-user">
|
|
89
|
+
<input type="checkbox" name="install-default-user" id="install-default-user" checked/>
|
|
90
|
+
<label for="install-default-user">Install default admin user (root@cms-admin.com / root)</label>
|
|
91
|
+
</div>
|
|
92
|
+
<div class="input-box install-default-homepage">
|
|
93
|
+
<input type="checkbox" name="install-default-homepage" id="install-default-homepage" checked/>
|
|
94
|
+
<label for="install-default-homepage">Install default homepage</label>
|
|
95
|
+
</div>
|
|
75
96
|
<div class="input-box submit-container">
|
|
76
97
|
<img class="install-loading hidden" src="/install-assets/img/loading.gif"/>
|
|
77
98
|
<input id="install-submit-button" type="submit" value="Install"/>
|
package/install/success.html
CHANGED
package/lib/admin-manager.js
CHANGED
|
@@ -380,6 +380,9 @@ class AdminManager
|
|
|
380
380
|
this.adminRouter = this.applicationFramework.Router();
|
|
381
381
|
// apply session middleware only to /admin routes:
|
|
382
382
|
if(this.session){
|
|
383
|
+
if(!this.secret){
|
|
384
|
+
Logger.warning('Admin Manager "secret" key was not provided.');
|
|
385
|
+
}
|
|
383
386
|
this.adminRouter.use(this.session({secret: this.secret, resave: false, saveUninitialized: true}));
|
|
384
387
|
}
|
|
385
388
|
this.adminRouter.use(this.bodyParser.json());
|
|
@@ -636,6 +639,26 @@ class AdminManager
|
|
|
636
639
|
return fileNames.join(property.isArray);
|
|
637
640
|
}
|
|
638
641
|
|
|
642
|
+
formatDateTimeForEdit(dateValue, isDisabled)
|
|
643
|
+
{
|
|
644
|
+
if(!dateValue || '' === dateValue){
|
|
645
|
+
return '';
|
|
646
|
+
}
|
|
647
|
+
let date = new Date(dateValue);
|
|
648
|
+
if(isNaN(date.getTime())){
|
|
649
|
+
return '';
|
|
650
|
+
}
|
|
651
|
+
if(isDisabled){
|
|
652
|
+
return sc.formatDate(date);
|
|
653
|
+
}
|
|
654
|
+
let year = date.getFullYear();
|
|
655
|
+
let month = String(date.getMonth() + 1).padStart(2, '0');
|
|
656
|
+
let day = String(date.getDate()).padStart(2, '0');
|
|
657
|
+
let hours = String(date.getHours()).padStart(2, '0');
|
|
658
|
+
let minutes = String(date.getMinutes()).padStart(2, '0');
|
|
659
|
+
return year+'-'+month+'-'+day+'T'+hours+':'+minutes;
|
|
660
|
+
}
|
|
661
|
+
|
|
639
662
|
async generateEditRouteContent(req, driverResource, entityPath)
|
|
640
663
|
{
|
|
641
664
|
let idProperty = this.fetchEntityIdPropertyKey(driverResource);
|
|
@@ -660,6 +683,7 @@ class AdminManager
|
|
|
660
683
|
if(resourceProperty.isUpload && loadedEntity){
|
|
661
684
|
isRequired = '';
|
|
662
685
|
}
|
|
686
|
+
let inputType = this.getInputType(resourceProperty, fieldDisabled);
|
|
663
687
|
renderedEditProperties[propertyKey] = await this.render(
|
|
664
688
|
this.adminFilesContents.fields.edit[this.propertyType(resourceProperty, 'edit')],
|
|
665
689
|
{
|
|
@@ -667,11 +691,13 @@ class AdminManager
|
|
|
667
691
|
fieldValue: await this.generatePropertyEditRenderedValue(
|
|
668
692
|
loadedEntity,
|
|
669
693
|
propertyKey,
|
|
670
|
-
resourceProperty
|
|
694
|
+
resourceProperty,
|
|
695
|
+
fieldDisabled
|
|
671
696
|
),
|
|
672
697
|
fieldDisabled: fieldDisabled ? ' disabled="disabled"' : '',
|
|
673
698
|
required: isRequired,
|
|
674
|
-
multiple: resourceProperty.isArray ? ' multiple="multiple"' : ''
|
|
699
|
+
multiple: resourceProperty.isArray ? ' multiple="multiple"' : '',
|
|
700
|
+
inputType
|
|
675
701
|
}
|
|
676
702
|
);
|
|
677
703
|
}
|
|
@@ -679,6 +705,14 @@ class AdminManager
|
|
|
679
705
|
return await this.renderRoute(renderedView, this.adminContents.sideBar);
|
|
680
706
|
}
|
|
681
707
|
|
|
708
|
+
getInputType(resourceProperty, fieldDisabled)
|
|
709
|
+
{
|
|
710
|
+
if('datetime' === resourceProperty.type && !fieldDisabled){
|
|
711
|
+
return 'datetime-local';
|
|
712
|
+
}
|
|
713
|
+
return 'text';
|
|
714
|
+
}
|
|
715
|
+
|
|
682
716
|
async loadEntityById(driverResource, id)
|
|
683
717
|
{
|
|
684
718
|
let entityRepository = this.dataServer.getEntity(driverResource.entityKey);
|
|
@@ -936,13 +970,16 @@ class AdminManager
|
|
|
936
970
|
return {fieldValue, fieldName};
|
|
937
971
|
}
|
|
938
972
|
|
|
939
|
-
async generatePropertyEditRenderedValue(entity, propertyKey, resourceProperty)
|
|
973
|
+
async generatePropertyEditRenderedValue(entity, propertyKey, resourceProperty, isFieldDisabled)
|
|
940
974
|
{
|
|
941
975
|
let entityPropertyValue = sc.get(entity, propertyKey, null);
|
|
942
976
|
let fieldValue = (0 === entityPropertyValue ? '0' : entityPropertyValue || '').toString();
|
|
943
977
|
if('boolean' === resourceProperty.type){
|
|
944
978
|
fieldValue = '1' === fieldValue || 'true' === fieldValue ? ' checked="checked"' : '';
|
|
945
979
|
}
|
|
980
|
+
if('datetime' === resourceProperty.type){
|
|
981
|
+
fieldValue = this.formatDateTimeForEdit(entityPropertyValue, isFieldDisabled);
|
|
982
|
+
}
|
|
946
983
|
if('reference' === resourceProperty.type){
|
|
947
984
|
let relationDriverResource = this.resourcesByReference[resourceProperty.reference];
|
|
948
985
|
let relation = this.relations[resourceProperty.reference];
|
package/lib/entities-loader.js
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const { LoadedEntitiesProcessor } = require('./loaded-entities-processor');
|
|
8
7
|
const { FileHandler } = require('@reldens/server-utils');
|
|
9
8
|
const { Logger, sc } = require('@reldens/utils');
|
|
10
9
|
|
|
@@ -30,7 +29,7 @@ class EntitiesLoader
|
|
|
30
29
|
}
|
|
31
30
|
try {
|
|
32
31
|
let {rawRegisteredEntities, entitiesConfig, entitiesTranslations} = require(entitiesPath);
|
|
33
|
-
return
|
|
32
|
+
return {rawRegisteredEntities, entitiesTranslations, entitiesConfig};
|
|
34
33
|
} catch(error){
|
|
35
34
|
Logger.error('Failed to load generated entities: ' + error.message);
|
|
36
35
|
}
|
package/lib/frontend.js
CHANGED
|
@@ -19,6 +19,10 @@ class Frontend
|
|
|
19
19
|
this.projectRoot = sc.get(props, 'projectRoot', './');
|
|
20
20
|
this.templatesPath = FileHandler.joinPaths(this.projectRoot, 'templates');
|
|
21
21
|
this.publicPath = FileHandler.joinPaths(this.projectRoot, 'public');
|
|
22
|
+
this.partialsExtensions = sc.get(props, 'partialsExtensions', ['.html', '.mustache', '.template']);
|
|
23
|
+
this.partialsCache = {};
|
|
24
|
+
this.domainPartialsCache = new Map();
|
|
25
|
+
this.domainTemplatesMap = new Map();
|
|
22
26
|
this.error = false;
|
|
23
27
|
}
|
|
24
28
|
|
|
@@ -36,6 +40,8 @@ class Frontend
|
|
|
36
40
|
Logger.error('Public folder not found: '+this.publicPath);
|
|
37
41
|
return false;
|
|
38
42
|
}
|
|
43
|
+
await this.loadPartials();
|
|
44
|
+
await this.setupDomainTemplates();
|
|
39
45
|
this.setupStaticAssets();
|
|
40
46
|
this.app.get('*', async (req, res) => {
|
|
41
47
|
return await this.handleRequest(req, res);
|
|
@@ -43,6 +49,101 @@ class Frontend
|
|
|
43
49
|
return true;
|
|
44
50
|
}
|
|
45
51
|
|
|
52
|
+
async loadPartials()
|
|
53
|
+
{
|
|
54
|
+
let partialsPath = FileHandler.joinPaths(this.templatesPath, 'partials');
|
|
55
|
+
FileHandler.createFolder(partialsPath);
|
|
56
|
+
let partialFiles = FileHandler.getFilesInFolder(partialsPath, this.partialsExtensions);
|
|
57
|
+
for(let file of partialFiles){
|
|
58
|
+
let partialName = '';
|
|
59
|
+
for(let extension of this.partialsExtensions){
|
|
60
|
+
if(file.endsWith(extension)){
|
|
61
|
+
partialName = file.replace(extension, '');
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
let partialPath = FileHandler.joinPaths(partialsPath, file);
|
|
66
|
+
let partialContent = FileHandler.readFile(partialPath);
|
|
67
|
+
this.partialsCache[partialName] = partialContent.toString();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async loadDomainPartials(domain, domainPath)
|
|
72
|
+
{
|
|
73
|
+
let domainPartialsPath = FileHandler.joinPaths(domainPath, 'partials');
|
|
74
|
+
if(!FileHandler.exists(domainPartialsPath)){
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
let domainPartials = {};
|
|
78
|
+
let partialFiles = FileHandler.getFilesInFolder(domainPartialsPath, this.partialsExtensions);
|
|
79
|
+
for(let file of partialFiles){
|
|
80
|
+
let partialName = '';
|
|
81
|
+
for(let extension of this.partialsExtensions){
|
|
82
|
+
if(file.endsWith(extension)){
|
|
83
|
+
partialName = file.replace(extension, '');
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
let partialPath = FileHandler.joinPaths(domainPartialsPath, file);
|
|
88
|
+
let partialContent = FileHandler.readFile(partialPath);
|
|
89
|
+
domainPartials[partialName] = partialContent.toString();
|
|
90
|
+
}
|
|
91
|
+
this.domainPartialsCache.set(domain, domainPartials);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async setupDomainTemplates()
|
|
95
|
+
{
|
|
96
|
+
let domainsPath = FileHandler.joinPaths(this.templatesPath, 'domains');
|
|
97
|
+
if(!FileHandler.exists(domainsPath)){
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
let domainFolders = FileHandler.fetchSubFoldersList(domainsPath);
|
|
101
|
+
for(let domain of domainFolders){
|
|
102
|
+
let domainPath = FileHandler.joinPaths(domainsPath, domain);
|
|
103
|
+
this.domainTemplatesMap.set(domain, domainPath);
|
|
104
|
+
await this.loadDomainPartials(domain, domainPath);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
getDomainFromRequest(req)
|
|
109
|
+
{
|
|
110
|
+
let host = req.get('host');
|
|
111
|
+
if(!host){
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
return host.split(':')[0];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
getPartialsForDomain(domain)
|
|
118
|
+
{
|
|
119
|
+
let domainPartials = this.domainPartialsCache.get(domain);
|
|
120
|
+
if(!domainPartials){
|
|
121
|
+
return this.partialsCache;
|
|
122
|
+
}
|
|
123
|
+
return Object.assign({}, this.partialsCache, domainPartials);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
findTemplatePathForDomain(templateName, domain)
|
|
127
|
+
{
|
|
128
|
+
if(!domain){
|
|
129
|
+
return this.getDefaultTemplatePath(templateName);
|
|
130
|
+
}
|
|
131
|
+
let domainPath = this.domainTemplatesMap.get(domain);
|
|
132
|
+
if(!domainPath){
|
|
133
|
+
return this.getDefaultTemplatePath(templateName);
|
|
134
|
+
}
|
|
135
|
+
let domainTemplatePath = FileHandler.joinPaths(domainPath, templateName + '.html');
|
|
136
|
+
if(!FileHandler.exists(domainTemplatePath)){
|
|
137
|
+
return this.getDefaultTemplatePath(templateName);
|
|
138
|
+
}
|
|
139
|
+
return domainTemplatePath;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
getDefaultTemplatePath(templateName)
|
|
143
|
+
{
|
|
144
|
+
return FileHandler.joinPaths(this.templatesPath, templateName + '.html');
|
|
145
|
+
}
|
|
146
|
+
|
|
46
147
|
setupStaticAssets()
|
|
47
148
|
{
|
|
48
149
|
if(!this.app || !this.appServerFactory || !this.publicPath){
|
|
@@ -62,26 +163,32 @@ class Frontend
|
|
|
62
163
|
return res.status(404).send('');
|
|
63
164
|
}
|
|
64
165
|
try {
|
|
166
|
+
let domain = this.getDomainFromRequest(req);
|
|
65
167
|
let route = await this.findRouteByPath(path);
|
|
66
168
|
if(route){
|
|
67
|
-
|
|
169
|
+
Logger.debug('Found route for path: '+path, route);
|
|
170
|
+
return await this.renderContentFromRoute(res, route, domain);
|
|
68
171
|
}
|
|
69
172
|
let pathSegments = path.split('/').filter(segment => segment !== '');
|
|
70
173
|
if(0 < pathSegments.length){
|
|
71
174
|
let entityResult = await this.findEntityByPath(pathSegments);
|
|
72
175
|
if(entityResult){
|
|
176
|
+
Logger.debug('Found entity for path segments: '+pathSegments.join('/'));
|
|
73
177
|
return await this.renderContentFromEntity(
|
|
74
178
|
res,
|
|
75
179
|
entityResult.entity,
|
|
76
|
-
entityResult.entityName
|
|
180
|
+
entityResult.entityName,
|
|
181
|
+
domain
|
|
77
182
|
);
|
|
78
183
|
}
|
|
79
184
|
}
|
|
80
|
-
let templatePath = this.findTemplateByPath(path);
|
|
185
|
+
let templatePath = this.findTemplateByPath(path, domain);
|
|
81
186
|
if(templatePath){
|
|
82
|
-
|
|
187
|
+
Logger.debug('Found template for path: '+path+' at: '+templatePath);
|
|
188
|
+
return await this.renderTemplateOnly(res, templatePath, domain);
|
|
83
189
|
}
|
|
84
|
-
|
|
190
|
+
Logger.debug('No template found for path: '+path+', rendering 404');
|
|
191
|
+
return await this.renderNotFoundPage(res, domain);
|
|
85
192
|
} catch (error) {
|
|
86
193
|
Logger.error('Request handling error: '+error.message);
|
|
87
194
|
return res.status(500).send('Internal server error');
|
|
@@ -90,32 +197,41 @@ class Frontend
|
|
|
90
197
|
|
|
91
198
|
async findRouteByPath(path)
|
|
92
199
|
{
|
|
93
|
-
if('/' === path){
|
|
94
|
-
path = '/home';
|
|
95
|
-
}
|
|
96
200
|
let routesEntity = this.dataServer.getEntity('routes');
|
|
97
201
|
if(!routesEntity){
|
|
202
|
+
Logger.error('Routes entity not found in dataServer');
|
|
98
203
|
return false;
|
|
99
204
|
}
|
|
100
|
-
|
|
205
|
+
let route = await routesEntity.loadOneBy('path', path);
|
|
206
|
+
if(route){
|
|
207
|
+
return route;
|
|
208
|
+
}
|
|
209
|
+
if('/' === path){
|
|
210
|
+
return await routesEntity.loadOneBy('path', '/home');
|
|
211
|
+
}
|
|
212
|
+
return false;
|
|
101
213
|
}
|
|
102
214
|
|
|
103
215
|
async findEntityByPath(pathSegments)
|
|
104
216
|
{
|
|
105
217
|
if(1 > pathSegments.length){
|
|
218
|
+
Logger.debug('No path segments provided');
|
|
106
219
|
return false;
|
|
107
220
|
}
|
|
108
221
|
let entityName = pathSegments[0];
|
|
109
222
|
let entityId = 2 > pathSegments.length ? false : pathSegments[1];
|
|
110
223
|
if(!entityId){
|
|
224
|
+
Logger.debug('No entity ID in path segments');
|
|
111
225
|
return false;
|
|
112
226
|
}
|
|
113
227
|
let entity = this.dataServer.getEntity(entityName);
|
|
114
228
|
if(!entity){
|
|
229
|
+
Logger.debug('Entity not found: '+entityName);
|
|
115
230
|
return false;
|
|
116
231
|
}
|
|
117
232
|
let loadedEntity = await entity.loadById(entityId);
|
|
118
233
|
if(!loadedEntity){
|
|
234
|
+
Logger.debug('Entity not loaded by ID: '+entityId);
|
|
119
235
|
return false;
|
|
120
236
|
}
|
|
121
237
|
return {
|
|
@@ -124,7 +240,7 @@ class Frontend
|
|
|
124
240
|
};
|
|
125
241
|
}
|
|
126
242
|
|
|
127
|
-
findTemplateByPath(path)
|
|
243
|
+
findTemplateByPath(path, domain)
|
|
128
244
|
{
|
|
129
245
|
if('/' === path){
|
|
130
246
|
path = '/index';
|
|
@@ -135,32 +251,36 @@ class Frontend
|
|
|
135
251
|
templatePath = templatePath.startsWith('/')
|
|
136
252
|
? templatePath.substring(1)
|
|
137
253
|
: templatePath;
|
|
138
|
-
let fullPath =
|
|
254
|
+
let fullPath = this.findTemplatePathForDomain(templatePath, domain);
|
|
139
255
|
if(FileHandler.exists(fullPath)){
|
|
140
256
|
return fullPath;
|
|
141
257
|
}
|
|
142
258
|
return false;
|
|
143
259
|
}
|
|
144
260
|
|
|
145
|
-
async renderContentFromRoute(res, route)
|
|
261
|
+
async renderContentFromRoute(res, route, domain)
|
|
146
262
|
{
|
|
147
263
|
if(!route.router || !route.content_id){
|
|
148
|
-
|
|
264
|
+
Logger.debug('Route missing router or content_id');
|
|
265
|
+
return await this.renderNotFoundPage(res, domain);
|
|
149
266
|
}
|
|
150
267
|
let entity = this.dataServer.getEntity(route.router);
|
|
151
268
|
if(!entity){
|
|
152
|
-
|
|
269
|
+
Logger.debug('Entity not found: '+route.router);
|
|
270
|
+
return await this.renderNotFoundPage(res, domain);
|
|
153
271
|
}
|
|
154
272
|
let content = await entity.loadById(route.content_id);
|
|
155
273
|
if(!content){
|
|
156
|
-
|
|
274
|
+
Logger.debug('Content not found for ID: '+route.content_id+' in entity: '+route.router);
|
|
275
|
+
return await this.renderNotFoundPage(res, domain);
|
|
157
276
|
}
|
|
158
277
|
let templateName = content.template || route.router;
|
|
159
|
-
let templatePath =
|
|
278
|
+
let templatePath = this.findTemplatePathForDomain(templateName, domain);
|
|
160
279
|
if(!FileHandler.exists(templatePath)){
|
|
161
|
-
templatePath =
|
|
280
|
+
templatePath = this.findTemplatePathForDomain('page', domain);
|
|
162
281
|
if(!FileHandler.exists(templatePath)){
|
|
163
|
-
|
|
282
|
+
Logger.debug('Neither template found: '+templateName+'.html nor page.html');
|
|
283
|
+
return await this.renderNotFoundPage(res, domain);
|
|
164
284
|
}
|
|
165
285
|
}
|
|
166
286
|
let template = FileHandler.readFile(templatePath).toString();
|
|
@@ -169,17 +289,18 @@ class Frontend
|
|
|
169
289
|
...content,
|
|
170
290
|
current_year: new Date().getFullYear()
|
|
171
291
|
};
|
|
172
|
-
let
|
|
292
|
+
let partials = this.getPartialsForDomain(domain);
|
|
293
|
+
let rendered = mustache.render(template, data, partials);
|
|
173
294
|
return res.send(rendered);
|
|
174
295
|
}
|
|
175
296
|
|
|
176
|
-
async renderContentFromEntity(res, entity, entityName)
|
|
297
|
+
async renderContentFromEntity(res, entity, entityName, domain)
|
|
177
298
|
{
|
|
178
|
-
let templatePath =
|
|
299
|
+
let templatePath = this.findTemplatePathForDomain(entityName, domain);
|
|
179
300
|
if(!FileHandler.exists(templatePath)){
|
|
180
|
-
templatePath =
|
|
301
|
+
templatePath = this.findTemplatePathForDomain('page', domain);
|
|
181
302
|
if(!FileHandler.exists(templatePath)){
|
|
182
|
-
return await this.renderNotFoundPage(res);
|
|
303
|
+
return await this.renderNotFoundPage(res, domain);
|
|
183
304
|
}
|
|
184
305
|
}
|
|
185
306
|
let template = FileHandler.readFile(templatePath).toString();
|
|
@@ -188,24 +309,26 @@ class Frontend
|
|
|
188
309
|
title: entity.title || entity.name || entityName,
|
|
189
310
|
current_year: new Date().getFullYear()
|
|
190
311
|
};
|
|
191
|
-
let
|
|
312
|
+
let partials = this.getPartialsForDomain(domain);
|
|
313
|
+
let rendered = mustache.render(template, data, partials);
|
|
192
314
|
return res.send(rendered);
|
|
193
315
|
}
|
|
194
316
|
|
|
195
|
-
async renderTemplateOnly(res, templatePath)
|
|
317
|
+
async renderTemplateOnly(res, templatePath, domain)
|
|
196
318
|
{
|
|
197
319
|
let template = FileHandler.readFile(templatePath).toString();
|
|
198
320
|
let data = {
|
|
199
321
|
title: 'Page Title',
|
|
200
322
|
current_year: new Date().getFullYear()
|
|
201
323
|
};
|
|
202
|
-
let
|
|
324
|
+
let partials = this.getPartialsForDomain(domain);
|
|
325
|
+
let rendered = mustache.render(template, data, partials);
|
|
203
326
|
return res.send(rendered);
|
|
204
327
|
}
|
|
205
328
|
|
|
206
|
-
async renderNotFoundPage(res)
|
|
329
|
+
async renderNotFoundPage(res, domain)
|
|
207
330
|
{
|
|
208
|
-
let templatePath =
|
|
331
|
+
let templatePath = this.findTemplatePathForDomain('404', domain);
|
|
209
332
|
if(!FileHandler.exists(templatePath)){
|
|
210
333
|
return res.status(404).send('Page not found');
|
|
211
334
|
}
|
|
@@ -214,7 +337,8 @@ class Frontend
|
|
|
214
337
|
title: '404 - Page Not Found',
|
|
215
338
|
current_year: new Date().getFullYear()
|
|
216
339
|
};
|
|
217
|
-
let
|
|
340
|
+
let partials = this.getPartialsForDomain(domain);
|
|
341
|
+
let rendered = mustache.render(template, data, partials);
|
|
218
342
|
return res.status(404).send(rendered);
|
|
219
343
|
}
|
|
220
344
|
|