@reldens/cms 0.7.0 → 0.8.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 +21 -4
- 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 +2 -2
- 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
|
@@ -64,12 +64,14 @@ class Frontend
|
|
|
64
64
|
try {
|
|
65
65
|
let route = await this.findRouteByPath(path);
|
|
66
66
|
if(route){
|
|
67
|
+
Logger.debug('Found route for path: '+path, route);
|
|
67
68
|
return await this.renderContentFromRoute(res, route);
|
|
68
69
|
}
|
|
69
70
|
let pathSegments = path.split('/').filter(segment => segment !== '');
|
|
70
71
|
if(0 < pathSegments.length){
|
|
71
72
|
let entityResult = await this.findEntityByPath(pathSegments);
|
|
72
73
|
if(entityResult){
|
|
74
|
+
Logger.debug('Found entity for path segments: '+pathSegments.join('/'));
|
|
73
75
|
return await this.renderContentFromEntity(
|
|
74
76
|
res,
|
|
75
77
|
entityResult.entity,
|
|
@@ -79,8 +81,10 @@ class Frontend
|
|
|
79
81
|
}
|
|
80
82
|
let templatePath = this.findTemplateByPath(path);
|
|
81
83
|
if(templatePath){
|
|
84
|
+
Logger.debug('Found template for path: '+path+' at: '+templatePath);
|
|
82
85
|
return await this.renderTemplateOnly(res, templatePath);
|
|
83
86
|
}
|
|
87
|
+
Logger.debug('No template found for path: '+path+', rendering 404');
|
|
84
88
|
return await this.renderNotFoundPage(res);
|
|
85
89
|
} catch (error) {
|
|
86
90
|
Logger.error('Request handling error: '+error.message);
|
|
@@ -90,32 +94,41 @@ class Frontend
|
|
|
90
94
|
|
|
91
95
|
async findRouteByPath(path)
|
|
92
96
|
{
|
|
93
|
-
if('/' === path){
|
|
94
|
-
path = '/home';
|
|
95
|
-
}
|
|
96
97
|
let routesEntity = this.dataServer.getEntity('routes');
|
|
97
98
|
if(!routesEntity){
|
|
99
|
+
Logger.error('Routes entity not found in dataServer');
|
|
98
100
|
return false;
|
|
99
101
|
}
|
|
100
|
-
|
|
102
|
+
let route = await routesEntity.loadOneBy('path', path);
|
|
103
|
+
if(route){
|
|
104
|
+
return route;
|
|
105
|
+
}
|
|
106
|
+
if('/' === path){
|
|
107
|
+
return await routesEntity.loadOneBy('path', '/home');
|
|
108
|
+
}
|
|
109
|
+
return false;
|
|
101
110
|
}
|
|
102
111
|
|
|
103
112
|
async findEntityByPath(pathSegments)
|
|
104
113
|
{
|
|
105
114
|
if(1 > pathSegments.length){
|
|
115
|
+
Logger.debug('No path segments provided');
|
|
106
116
|
return false;
|
|
107
117
|
}
|
|
108
118
|
let entityName = pathSegments[0];
|
|
109
119
|
let entityId = 2 > pathSegments.length ? false : pathSegments[1];
|
|
110
120
|
if(!entityId){
|
|
121
|
+
Logger.debug('No entity ID in path segments');
|
|
111
122
|
return false;
|
|
112
123
|
}
|
|
113
124
|
let entity = this.dataServer.getEntity(entityName);
|
|
114
125
|
if(!entity){
|
|
126
|
+
Logger.debug('Entity not found: '+entityName);
|
|
115
127
|
return false;
|
|
116
128
|
}
|
|
117
129
|
let loadedEntity = await entity.loadById(entityId);
|
|
118
130
|
if(!loadedEntity){
|
|
131
|
+
Logger.debug('Entity not loaded by ID: '+entityId);
|
|
119
132
|
return false;
|
|
120
133
|
}
|
|
121
134
|
return {
|
|
@@ -145,14 +158,17 @@ class Frontend
|
|
|
145
158
|
async renderContentFromRoute(res, route)
|
|
146
159
|
{
|
|
147
160
|
if(!route.router || !route.content_id){
|
|
161
|
+
Logger.debug('Route missing router or content_id');
|
|
148
162
|
return await this.renderNotFoundPage(res);
|
|
149
163
|
}
|
|
150
164
|
let entity = this.dataServer.getEntity(route.router);
|
|
151
165
|
if(!entity){
|
|
166
|
+
Logger.debug('Entity not found: '+route.router);
|
|
152
167
|
return await this.renderNotFoundPage(res);
|
|
153
168
|
}
|
|
154
169
|
let content = await entity.loadById(route.content_id);
|
|
155
170
|
if(!content){
|
|
171
|
+
Logger.debug('Content not found for ID: '+route.content_id+' in entity: '+route.router);
|
|
156
172
|
return await this.renderNotFoundPage(res);
|
|
157
173
|
}
|
|
158
174
|
let templateName = content.template || route.router;
|
|
@@ -160,6 +176,7 @@ class Frontend
|
|
|
160
176
|
if(!FileHandler.exists(templatePath)){
|
|
161
177
|
templatePath = FileHandler.joinPaths(this.templatesPath, 'page.html');
|
|
162
178
|
if(!FileHandler.exists(templatePath)){
|
|
179
|
+
Logger.debug('Neither template found: '+templateName+'.html nor page.html');
|
|
163
180
|
return await this.renderNotFoundPage(res);
|
|
164
181
|
}
|
|
165
182
|
}
|
package/lib/installer.js
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
const { FileHandler, Encryptor } = require('@reldens/server-utils');
|
|
8
8
|
const { DriversMap, EntitiesGenerator, PrismaSchemaGenerator } = require('@reldens/storage');
|
|
9
9
|
const { EntitiesLoader } = require('./entities-loader');
|
|
10
|
-
const { AdminEntitiesGenerator } = require('./admin-entities-generator');
|
|
11
10
|
const { Logger, sc } = require('@reldens/utils');
|
|
12
11
|
const mustache = require('mustache');
|
|
13
12
|
|
|
@@ -104,10 +103,11 @@ class Installer
|
|
|
104
103
|
'connection-failed': 'Database connection failed. Please check your credentials.',
|
|
105
104
|
'raw-query-not-found': 'Query method not found in driver.',
|
|
106
105
|
'sql-file-not-found': 'SQL installation file not found.',
|
|
107
|
-
'sql-tables-creation-failed': 'Failed to create
|
|
106
|
+
'sql-cms-tables-creation-failed': 'Failed to create CMS tables.',
|
|
107
|
+
'sql-user-auth-creation-failed': 'Failed to create user authentication tables.',
|
|
108
108
|
'sql-default-user-error': 'Failed to create default user.',
|
|
109
|
+
'sql-default-homepage-error': 'Failed to create default homepage.',
|
|
109
110
|
'installation-entities-generation-failed': 'Failed to generate entities.',
|
|
110
|
-
'installation-process-failed': 'Installation process failed.',
|
|
111
111
|
'installation-entities-callback-failed': 'Failed to process entities for callback.',
|
|
112
112
|
'configuration-error': 'Configuration error while completing installation.',
|
|
113
113
|
'already-installed': 'The application is already installed.'
|
|
@@ -157,39 +157,32 @@ class Installer
|
|
|
157
157
|
Logger.error('Method "rawQuery" not found.');
|
|
158
158
|
return res.redirect('/?error=raw-query-not-found');
|
|
159
159
|
}
|
|
160
|
-
let
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if(
|
|
174
|
-
|
|
175
|
-
if(!queryUserResult){
|
|
176
|
-
Logger.error('Default user creation failed.', queryUserResult);
|
|
177
|
-
return res.redirect('/?error=sql-default-user-error');
|
|
178
|
-
}
|
|
179
|
-
Logger.info('Created default user.');
|
|
180
|
-
}
|
|
181
|
-
let entitiesGenerationResult = await this.generateEntities(dbDriver);
|
|
182
|
-
if(!entitiesGenerationResult){
|
|
183
|
-
Logger.error('Entities generation error.');
|
|
184
|
-
return res.redirect('/?error=installation-entities-generation-failed');
|
|
160
|
+
let executeFiles = {
|
|
161
|
+
'install-cms-tables': 'install.sql',
|
|
162
|
+
'install-user-auth': 'users-authentication.sql',
|
|
163
|
+
'install-default-user': 'default-user.sql',
|
|
164
|
+
'install-default-homepage': 'default-homepage.sql'
|
|
165
|
+
};
|
|
166
|
+
for(let checkboxName of Object.keys(executeFiles)){
|
|
167
|
+
let fileName = executeFiles[checkboxName];
|
|
168
|
+
let redirectError = await this.executeQueryFile(
|
|
169
|
+
sc.get(templateVariables, checkboxName, 'off'),
|
|
170
|
+
fileName,
|
|
171
|
+
dbDriver
|
|
172
|
+
);
|
|
173
|
+
if('' !== redirectError){
|
|
174
|
+
return res.redirect(redirectError);
|
|
185
175
|
}
|
|
186
|
-
Logger.info('Generated entities.');
|
|
187
|
-
} catch (error) {
|
|
188
|
-
Logger.error('Installation error: '+error.message);
|
|
189
|
-
return res.redirect('/?error=installation-process-failed');
|
|
190
176
|
}
|
|
177
|
+
let entitiesGenerationResult = await this.generateEntities(dbDriver);
|
|
178
|
+
if(!entitiesGenerationResult){
|
|
179
|
+
Logger.error('Entities generation error.');
|
|
180
|
+
return res.redirect('/?error=installation-entities-generation-failed');
|
|
181
|
+
}
|
|
182
|
+
Logger.info('Generated entities.');
|
|
191
183
|
try {
|
|
192
|
-
|
|
184
|
+
let mappedVariablesForConfig = this.mapVariablesForConfig(templateVariables);
|
|
185
|
+
await this.createEnvFile(this.mapVariablesForTemplate(mappedVariablesForConfig));
|
|
193
186
|
await this.prepareProjectDirectories();
|
|
194
187
|
await this.copyAdminDirectory();
|
|
195
188
|
await this.createIndexJsFile(templateVariables);
|
|
@@ -198,7 +191,10 @@ class Installer
|
|
|
198
191
|
await this.appServer.close();
|
|
199
192
|
}
|
|
200
193
|
Logger.debug('Running postInstallCallback.');
|
|
201
|
-
await this.postInstallCallback(
|
|
194
|
+
await this.postInstallCallback({
|
|
195
|
+
loadedEntities: this.entitiesLoader.loadEntities(selectedDriver),
|
|
196
|
+
mappedVariablesForConfig
|
|
197
|
+
});
|
|
202
198
|
}
|
|
203
199
|
await this.createLockFile();
|
|
204
200
|
Logger.info('Installation successful!');
|
|
@@ -214,13 +210,23 @@ class Installer
|
|
|
214
210
|
}
|
|
215
211
|
}
|
|
216
212
|
|
|
217
|
-
async executeQueryFile(
|
|
213
|
+
async executeQueryFile(isMarked, fileName, dbDriver)
|
|
218
214
|
{
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
throw new Error('Could not read SQL file: '+filePath);
|
|
215
|
+
if('on' !== isMarked){
|
|
216
|
+
return '';
|
|
222
217
|
}
|
|
223
|
-
|
|
218
|
+
let sqlFileContent = FileHandler.readFile(FileHandler.joinPaths(this.migrationsPath, fileName));
|
|
219
|
+
if(!sqlFileContent){
|
|
220
|
+
Logger.error('SQL file "'+fileName+'" not found.');
|
|
221
|
+
return '/?error=sql-file-not-found&file-name='+fileName;
|
|
222
|
+
}
|
|
223
|
+
let queryResult = await dbDriver.rawQuery(sqlFileContent.toString());
|
|
224
|
+
if(!queryResult){
|
|
225
|
+
Logger.error('SQL file "'+fileName+'" raw execution failed.');
|
|
226
|
+
return '/?error=sql-file-execution-error&file-name='+fileName;
|
|
227
|
+
}
|
|
228
|
+
Logger.info('SQL file "'+fileName+'" raw execution successfully.');
|
|
229
|
+
return '';
|
|
224
230
|
}
|
|
225
231
|
|
|
226
232
|
async generateEntities(server)
|
|
@@ -252,25 +258,48 @@ class Installer
|
|
|
252
258
|
async createEnvFile(templateVariables)
|
|
253
259
|
{
|
|
254
260
|
let envTemplatePath = FileHandler.joinPaths(this.defaultTemplatesPath, '.env.dist');
|
|
255
|
-
|
|
256
|
-
|
|
261
|
+
let envTemplateContent = FileHandler.readFile(envTemplatePath);
|
|
262
|
+
if(!envTemplateContent){
|
|
263
|
+
Logger.error('Template ".env.dist" not found: '+envTemplatePath);
|
|
257
264
|
return false;
|
|
258
265
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
266
|
+
return FileHandler.writeFile(this.envFilePath, mustache.render(envTemplateContent, templateVariables));
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
mapVariablesForTemplate(configVariables)
|
|
270
|
+
{
|
|
271
|
+
return {
|
|
272
|
+
host: configVariables.host,
|
|
273
|
+
port: configVariables.port,
|
|
274
|
+
adminPath: configVariables.adminPath,
|
|
275
|
+
adminSecret: configVariables.adminSecret,
|
|
276
|
+
dbClient: configVariables.database.client,
|
|
277
|
+
dbHost: configVariables.database.host,
|
|
278
|
+
dbPort: configVariables.database.port,
|
|
279
|
+
dbName: configVariables.database.name,
|
|
280
|
+
dbUser: configVariables.database.user,
|
|
281
|
+
dbPassword: configVariables.database.password,
|
|
282
|
+
dbDriver: configVariables.database.driver
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
mapVariablesForConfig(templateVariables)
|
|
287
|
+
{
|
|
288
|
+
return {
|
|
289
|
+
host: sc.get(templateVariables, 'app-host', 'http://localhost'),
|
|
290
|
+
port: Number(sc.get(templateVariables, 'app-port', 8080)),
|
|
291
|
+
adminPath: sc.get(templateVariables, 'app-admin-path', '/reldens-admin'),
|
|
292
|
+
adminSecret: sc.get(templateVariables, 'app-admin-secret', Encryptor.generateSecretKey()),
|
|
293
|
+
database: {
|
|
294
|
+
client: sc.get(templateVariables, 'db-client', 'mysql'),
|
|
295
|
+
host: sc.get(templateVariables, 'db-host', 'localhost'),
|
|
296
|
+
port: Number(sc.get(templateVariables, 'db-port', 3306)),
|
|
297
|
+
name: sc.get(templateVariables, 'db-name', 'reldens_cms'),
|
|
298
|
+
user: sc.get(templateVariables, 'db-username', ''),
|
|
299
|
+
password: sc.get(templateVariables, 'db-password', ''),
|
|
300
|
+
driver: sc.get(templateVariables, 'db-storage-driver', 'prisma')
|
|
301
|
+
}
|
|
302
|
+
};
|
|
274
303
|
}
|
|
275
304
|
|
|
276
305
|
async createIndexJsFile(templateVariables)
|
|
@@ -328,18 +357,20 @@ class Installer
|
|
|
328
357
|
FileHandler.createFolder(this.projectPublicAssetsPath);
|
|
329
358
|
FileHandler.createFolder(this.projectCssPath);
|
|
330
359
|
FileHandler.createFolder(this.projectJsPath);
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
FileHandler.
|
|
341
|
-
|
|
342
|
-
|
|
360
|
+
let baseFiles = [
|
|
361
|
+
'page.html',
|
|
362
|
+
'404.html',
|
|
363
|
+
'layout.html',
|
|
364
|
+
'browserconfig.xml',
|
|
365
|
+
'favicon.ico',
|
|
366
|
+
'site.webmanifest'
|
|
367
|
+
];
|
|
368
|
+
for(let fileName of baseFiles){
|
|
369
|
+
FileHandler.copyFile(
|
|
370
|
+
FileHandler.joinPaths(this.defaultTemplatesPath, fileName),
|
|
371
|
+
FileHandler.joinPaths(this.projectTemplatesPath, fileName)
|
|
372
|
+
);
|
|
373
|
+
}
|
|
343
374
|
FileHandler.copyFile(
|
|
344
375
|
FileHandler.joinPaths(this.defaultTemplatesPath, 'css', 'styles.css'),
|
|
345
376
|
FileHandler.joinPaths(this.projectCssPath, 'styles.css')
|
|
@@ -354,16 +385,16 @@ class Installer
|
|
|
354
385
|
fetchDefaults()
|
|
355
386
|
{
|
|
356
387
|
return {
|
|
357
|
-
'app-host': process.env.
|
|
358
|
-
'app-port': process.env.
|
|
359
|
-
'app-admin-path': process.env.
|
|
360
|
-
'db-storage-driver': 'prisma',
|
|
361
|
-
'db-client': process.env.
|
|
362
|
-
'db-host': process.env.
|
|
363
|
-
'db-port': process.env.
|
|
364
|
-
'db-name': process.env.
|
|
365
|
-
'db-username': process.env.
|
|
366
|
-
'db-password': process.env.
|
|
388
|
+
'app-host': process.env.RELDENS_APP_HOST || 'http://localhost',
|
|
389
|
+
'app-port': process.env.RELDENS_APP_PORT || '8080',
|
|
390
|
+
'app-admin-path': process.env.RELDENS_ADMIN_ROUTE_PATH || '/reldens-admin',
|
|
391
|
+
'db-storage-driver': process.env.RELDENS_STORAGE_DRIVER || 'prisma',
|
|
392
|
+
'db-client': process.env.RELDENS_DB_CLIENT || 'mysql',
|
|
393
|
+
'db-host': process.env.RELDENS_DB_HOST || 'localhost',
|
|
394
|
+
'db-port': process.env.RELDENS_DB_PORT || '3306',
|
|
395
|
+
'db-name': process.env.RELDENS_DB_NAME || 'reldens_cms',
|
|
396
|
+
'db-username': process.env.RELDENS_DB_USER || '',
|
|
397
|
+
'db-password': process.env.RELDENS_DB_PASSWORD || ''
|
|
367
398
|
};
|
|
368
399
|
}
|
|
369
400
|
}
|
package/lib/manager.js
CHANGED
|
@@ -51,35 +51,98 @@ class Manager
|
|
|
51
51
|
this.companyName = sc.get(props, 'companyName', 'Reldens - CMS');
|
|
52
52
|
this.logo = sc.get(props, 'logo', '/assets/web/reldens-your-logo-mage.png');
|
|
53
53
|
this.favicon = sc.get(props, 'favicon', '/assets/web/favicon.ico');
|
|
54
|
+
this.app = sc.get(props, 'app', false);
|
|
55
|
+
this.appServer = sc.get(props, 'appServer', false);
|
|
56
|
+
this.dataServer = sc.get(props, 'dataServer', false);
|
|
57
|
+
this.adminManager = sc.get(props, 'adminManager', false);
|
|
58
|
+
this.frontend = sc.get(props, 'frontend', false);
|
|
54
59
|
this.appServerFactory = new AppServerFactory();
|
|
55
60
|
this.adminEntitiesGenerator = new AdminEntitiesGenerator();
|
|
56
61
|
this.installer = new Installer({
|
|
57
62
|
projectRoot: this.projectRoot,
|
|
58
63
|
postInstallCallback: this.initializeCmsAfterInstall.bind(this)
|
|
59
64
|
});
|
|
60
|
-
this.
|
|
61
|
-
this.
|
|
62
|
-
this.
|
|
63
|
-
this.
|
|
64
|
-
|
|
65
|
+
this.useProvidedServer = this.validateProvidedServer();
|
|
66
|
+
this.useProvidedDataServer = this.validateProvidedDataServer();
|
|
67
|
+
this.useProvidedAdminManager = this.validateProvidedAdminManager();
|
|
68
|
+
this.useProvidedFrontend = this.validateProvidedFrontend();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
validateProvidedServer()
|
|
72
|
+
{
|
|
73
|
+
if(!this.app){
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
if(!this.appServer){
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
if('function' !== typeof this.app.use){
|
|
80
|
+
Logger.critical('Invalid app instance provided - missing use method.');
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
if('function' !== typeof this.appServer.listen){
|
|
84
|
+
Logger.critical('Invalid appServer instance provided - missing listen method.');
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
validateProvidedDataServer()
|
|
91
|
+
{
|
|
92
|
+
if(!this.dataServer){
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
if('function' !== typeof this.dataServer.connect){
|
|
96
|
+
Logger.critical('Invalid dataServer instance provided - missing connect method.');
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
if('function' !== typeof this.dataServer.generateEntities){
|
|
100
|
+
Logger.critical('Invalid dataServer instance provided - missing generateEntities method.');
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
validateProvidedAdminManager()
|
|
107
|
+
{
|
|
108
|
+
if(!this.adminManager){
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
if('function' !== typeof this.adminManager.setupAdmin){
|
|
112
|
+
Logger.critical('Invalid adminManager instance provided - missing setupAdmin method.');
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
validateProvidedFrontend()
|
|
119
|
+
{
|
|
120
|
+
if(!this.frontend){
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
if('function' !== typeof this.frontend.initialize){
|
|
124
|
+
Logger.critical('Invalid frontend instance provided - missing initialize method.');
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
return true;
|
|
65
128
|
}
|
|
66
129
|
|
|
67
130
|
loadConfigFromEnv()
|
|
68
131
|
{
|
|
69
132
|
let envVars = process.env;
|
|
70
133
|
return {
|
|
71
|
-
host: sc.get(envVars, '
|
|
72
|
-
port: Number(sc.get(envVars, '
|
|
73
|
-
adminPath: sc.get(envVars, '
|
|
74
|
-
adminSecret: sc.get(envVars, '
|
|
134
|
+
host: sc.get(envVars, 'RELDENS_APP_HOST', 'http://localhost'),
|
|
135
|
+
port: Number(sc.get(envVars, 'RELDENS_APP_PORT', 8080)),
|
|
136
|
+
adminPath: sc.get(envVars, 'RELDENS_ADMIN_ROUTE_PATH', '/reldens-admin'),
|
|
137
|
+
adminSecret: sc.get(envVars, 'RELDENS_ADMIN_SECRET', ''),
|
|
75
138
|
database: {
|
|
76
|
-
client: sc.get(envVars, '
|
|
77
|
-
host: sc.get(envVars, '
|
|
78
|
-
port: Number(sc.get(envVars, '
|
|
79
|
-
name: sc.get(envVars, '
|
|
80
|
-
user: sc.get(envVars, '
|
|
81
|
-
password: sc.get(envVars, '
|
|
82
|
-
driver: sc.get(envVars, '
|
|
139
|
+
client: sc.get(envVars, 'RELDENS_DB_CLIENT', 'mysql'),
|
|
140
|
+
host: sc.get(envVars, 'RELDENS_DB_HOST', 'localhost'),
|
|
141
|
+
port: Number(sc.get(envVars, 'RELDENS_DB_PORT', 3306)),
|
|
142
|
+
name: sc.get(envVars, 'RELDENS_DB_NAME', 'reldens_cms'),
|
|
143
|
+
user: sc.get(envVars, 'RELDENS_DB_USER', ''),
|
|
144
|
+
password: sc.get(envVars, 'RELDENS_DB_PASSWORD', ''),
|
|
145
|
+
driver: sc.get(envVars, 'RELDENS_STORAGE_DRIVER', 'prisma')
|
|
83
146
|
}
|
|
84
147
|
};
|
|
85
148
|
}
|
|
@@ -91,17 +154,21 @@ class Manager
|
|
|
91
154
|
|
|
92
155
|
async start()
|
|
93
156
|
{
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
157
|
+
if(!this.useProvidedServer){
|
|
158
|
+
let createdAppServer = this.appServerFactory.createAppServer();
|
|
159
|
+
if(this.appServerFactory.error.message){
|
|
160
|
+
Logger.error('App server error: '+this.appServerFactory.error.message);
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
this.app = createdAppServer.app;
|
|
164
|
+
this.appServer = createdAppServer.appServer;
|
|
98
165
|
}
|
|
99
|
-
this.app = createdAppServer.app;
|
|
100
|
-
this.appServer = createdAppServer.appServer;
|
|
101
166
|
if(!this.isInstalled()){
|
|
102
167
|
Logger.info('CMS not installed, preparing setup');
|
|
103
168
|
await this.installer.prepareSetup(this.app, this.appServer, this.appServerFactory);
|
|
104
|
-
|
|
169
|
+
if(!this.useProvidedServer){
|
|
170
|
+
await this.appServer.listen(this.config.port);
|
|
171
|
+
}
|
|
105
172
|
Logger.info('Installer running on '+this.config.host+':'+this.config.port);
|
|
106
173
|
return true;
|
|
107
174
|
}
|
|
@@ -115,14 +182,14 @@ class Manager
|
|
|
115
182
|
}
|
|
116
183
|
}
|
|
117
184
|
|
|
118
|
-
async initializeCmsAfterInstall(
|
|
185
|
+
async initializeCmsAfterInstall(props)
|
|
119
186
|
{
|
|
120
187
|
try {
|
|
121
|
-
|
|
122
|
-
this.
|
|
123
|
-
this.
|
|
124
|
-
this.
|
|
125
|
-
this.config =
|
|
188
|
+
//Logger.debug('Loaded entities post install.', props.loadedEntities);
|
|
189
|
+
this.rawRegisteredEntities = props.loadedEntities.rawRegisteredEntities;
|
|
190
|
+
this.entitiesTranslations = props.loadedEntities.entitiesTranslations;
|
|
191
|
+
this.entitiesConfig = props.loadedEntities.entitiesConfig;
|
|
192
|
+
this.config = props.mappedVariablesForConfig;
|
|
126
193
|
await this.initializeServices();
|
|
127
194
|
Logger.info('CMS initialized after installation on '+this.config.host+':'+this.config.port);
|
|
128
195
|
return true;
|
|
@@ -134,28 +201,68 @@ class Manager
|
|
|
134
201
|
|
|
135
202
|
async initializeServices()
|
|
136
203
|
{
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
204
|
+
if(!this.useProvidedDataServer){
|
|
205
|
+
if(!await this.initializeDataServer()){
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if(!this.loadProcessedEntities()){
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
if(!await this.generateAdminEntities()){
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
if(!this.useProvidedAdminManager){
|
|
216
|
+
if(!await this.initializeAdminManager()){
|
|
217
|
+
return false;
|
|
145
218
|
}
|
|
146
|
-
|
|
147
|
-
|
|
219
|
+
}
|
|
220
|
+
if(!this.useProvidedFrontend){
|
|
221
|
+
if(!await this.initializeFrontend()){
|
|
148
222
|
return false;
|
|
149
223
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
224
|
+
}
|
|
225
|
+
if(!this.useProvidedServer){
|
|
226
|
+
await this.appServer.listen(this.config.port);
|
|
227
|
+
}
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
loadProcessedEntities()
|
|
232
|
+
{
|
|
233
|
+
if(0 === Object.keys(this.processedEntities).length){
|
|
234
|
+
this.processedEntities = LoadedEntitiesProcessor.process(
|
|
235
|
+
this.rawRegisteredEntities,
|
|
236
|
+
this.entitiesTranslations,
|
|
237
|
+
this.entitiesConfig
|
|
154
238
|
);
|
|
155
239
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
240
|
+
if(!this.processedEntities?.entities){
|
|
241
|
+
Logger.critical('Processed entities undefined.');
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async generateAdminEntities()
|
|
248
|
+
{
|
|
249
|
+
if(0 < Object.keys(this.adminEntities).length){
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
if(!this.dataServer.rawEntities && this.rawRegisteredEntities){
|
|
253
|
+
this.dataServer.rawEntities = this.rawRegisteredEntities;
|
|
254
|
+
}
|
|
255
|
+
Logger.debug('Generate entities count: '+Object.keys(this.rawRegisteredEntities).length);
|
|
256
|
+
await this.dataServer.generateEntities();
|
|
257
|
+
this.adminEntities = this.adminEntitiesGenerator.generate(
|
|
258
|
+
this.processedEntities.entities,
|
|
259
|
+
this.dataServer.entityManager.entities
|
|
260
|
+
);
|
|
261
|
+
if(0 === Object.keys(this.adminEntities).length){
|
|
262
|
+
Logger.warning('Admin entities not found.');
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
return true;
|
|
159
266
|
}
|
|
160
267
|
|
|
161
268
|
async initializeDataServer()
|
|
@@ -181,6 +288,7 @@ class Manager
|
|
|
181
288
|
Logger.critical('Failed to connect to database.');
|
|
182
289
|
return false;
|
|
183
290
|
}
|
|
291
|
+
Logger.debug('Entities count: '+Object.keys(this.rawRegisteredEntities).length);
|
|
184
292
|
await this.dataServer.generateEntities();
|
|
185
293
|
return true;
|
|
186
294
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
|
|
2
|
+
-- Default homepage:
|
|
3
|
+
|
|
4
|
+
-- Create a default homepage route if not exists
|
|
5
|
+
REPLACE INTO `cms_pages` (`id`, `title`, `content`, `template`, `created_at`) VALUES
|
|
6
|
+
(1, 'Home', '<h1>Welcome to Reldens CMS</h1><p>This is your homepage. Edit this content in the admin panel.</p>', 'page', NOW());
|
|
7
|
+
|
|
8
|
+
-- Create a default route to the homepage
|
|
9
|
+
REPLACE INTO `routes` (`id`, `path`, `router`, `content_id`, `title`, `meta_description`, `status`, `created_at`) VALUES
|
|
10
|
+
(1, '/home', 'cmsPages', 1, 'Home', 'Welcome to Reldens CMS', 'published', NOW());
|
package/migrations/install.sql
CHANGED
|
@@ -1,19 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
-- Install
|
|
3
|
-
|
|
4
|
-
CREATE TABLE IF NOT EXISTS `users` (
|
|
5
|
-
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
6
|
-
`email` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
7
|
-
`username` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
8
|
-
`password` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
9
|
-
`role_id` INT(10) UNSIGNED NOT NULL,
|
|
10
|
-
`status` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
11
|
-
`created_at` TIMESTAMP NOT NULL DEFAULT (NOW()),
|
|
12
|
-
`updated_at` TIMESTAMP NOT NULL DEFAULT (NOW()) ON UPDATE CURRENT_TIMESTAMP,
|
|
13
|
-
PRIMARY KEY (`id`) USING BTREE,
|
|
14
|
-
UNIQUE KEY `email` (`email`) USING BTREE,
|
|
15
|
-
UNIQUE KEY `username` (`username`) USING BTREE
|
|
16
|
-
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
2
|
+
-- Install Reldens CMS
|
|
17
3
|
|
|
18
4
|
CREATE TABLE IF NOT EXISTS `routes` (
|
|
19
5
|
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
@@ -74,11 +60,3 @@ CREATE TABLE IF NOT EXISTS `entities_meta` (
|
|
|
74
60
|
PRIMARY KEY (`id`) USING BTREE,
|
|
75
61
|
UNIQUE KEY `entity_meta` (`entity_name`, `entity_id`, `meta_key`) USING BTREE
|
|
76
62
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
77
|
-
|
|
78
|
-
-- Create a default homepage route if not exists
|
|
79
|
-
INSERT IGNORE INTO `cms_pages` (`id`, `title`, `content`, `template`, `created_at`) VALUES
|
|
80
|
-
(1, 'Home', '<h1>Welcome to Reldens CMS</h1><p>This is your homepage. Edit this content in the admin panel.</p>', 'page', NOW());
|
|
81
|
-
|
|
82
|
-
-- Create a default route to the homepage
|
|
83
|
-
INSERT IGNORE INTO `routes` (`path`, `router`, `content_id`, `title`, `meta_description`, `status`, `created_at`) VALUES
|
|
84
|
-
('/home', 'cms_pages', 1, 'Home', 'Welcome to Reldens CMS', 'published', NOW());
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
|
|
2
|
+
-- Default db-users authentication:
|
|
3
|
+
|
|
4
|
+
CREATE TABLE IF NOT EXISTS `users` (
|
|
5
|
+
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
6
|
+
`email` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
7
|
+
`username` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
8
|
+
`password` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
9
|
+
`role_id` INT(10) UNSIGNED NOT NULL,
|
|
10
|
+
`status` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
|
|
11
|
+
`created_at` TIMESTAMP NOT NULL DEFAULT (NOW()),
|
|
12
|
+
`updated_at` TIMESTAMP NOT NULL DEFAULT (NOW()) ON UPDATE CURRENT_TIMESTAMP,
|
|
13
|
+
PRIMARY KEY (`id`) USING BTREE,
|
|
14
|
+
UNIQUE KEY `email` (`email`) USING BTREE,
|
|
15
|
+
UNIQUE KEY `username` (`username`) USING BTREE
|
|
16
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reldens/cms",
|
|
3
3
|
"scope": "@reldens",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.8.0",
|
|
5
5
|
"description": "Reldens - CMS",
|
|
6
6
|
"author": "Damian A. Pastorini",
|
|
7
7
|
"license": "MIT",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@reldens/server-utils": "^0.16.0",
|
|
36
|
-
"@reldens/storage": "^0.
|
|
36
|
+
"@reldens/storage": "^0.45.0",
|
|
37
37
|
"@reldens/utils": "^0.47.0",
|
|
38
38
|
"dotenv": "^16.5.0",
|
|
39
39
|
"mustache": "^4.2.0"
|
package/templates/.env.dist
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# Database Configuration
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
RELDENS_DB_CLIENT={{&dbClient}}
|
|
3
|
+
RELDENS_DB_HOST={{&dbHost}}
|
|
4
|
+
RELDENS_DB_PORT={{&dbPort}}
|
|
5
|
+
RELDENS_DB_NAME={{&dbName}}
|
|
6
|
+
RELDENS_DB_USER={{&dbUser}}
|
|
7
|
+
RELDENS_DB_PASSWORD={{&dbPassword}}
|
|
8
|
+
RELDENS_DB_DRIVER={{&dbDriver}}
|
|
9
9
|
|
|
10
10
|
# Admin Panel Configuration
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
RELDENS_ADMIN_ROUTE_PATH={{&adminPath}}
|
|
12
|
+
RELDENS_ADMIN_SECRET={{&adminSecret}}
|
|
13
13
|
|
|
14
14
|
# Server Configuration
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
RELDENS_APP_HOST={{&host}}
|
|
16
|
+
RELDENS_APP_PORT={{&port}}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
<?xml version="1.0" standalone="no"?>
|
|
2
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
|
3
|
+
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
|
4
|
+
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
|
|
6
|
+
preserveAspectRatio="xMidYMid meet">
|
|
7
|
+
<metadata>
|
|
8
|
+
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
|
9
|
+
</metadata>
|
|
10
|
+
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
|
|
11
|
+
fill="#000000" stroke="none">
|
|
12
|
+
<path d="M0 3883 l0 -1238 34 61 c49 92 94 149 203 255 121 119 204 185 372
|
|
13
|
+
294 72 46 181 130 243 186 62 55 205 173 318 262 232 183 317 262 348 328 53
|
|
14
|
+
111 4 231 -120 295 -90 46 -167 58 -363 58 -197 0 -282 -9 -545 -56 -198 -35
|
|
15
|
+
-232 -33 -285 13 -91 80 -34 207 172 379 181 151 379 270 593 357 l105 42
|
|
16
|
+
-537 1 -538 0 0 -1237z"/>
|
|
17
|
+
<path d="M2208 5100 c141 -51 465 -230 620 -344 85 -63 344 -321 447 -446 122
|
|
18
|
+
-147 247 -318 380 -520 68 -102 136 -197 151 -212 40 -37 83 -53 221 -83 236
|
|
19
|
+
-51 557 -156 713 -233 87 -42 210 -133 264 -193 21 -22 55 -68 77 -101 l39
|
|
20
|
+
-61 0 1106 0 1107 -1482 -1 c-1462 0 -1482 -1 -1430 -19z"/>
|
|
21
|
+
<path d="M1466 5070 c-357 -41 -721 -207 -1034 -470 -64 -54 -173 -180 -155
|
|
22
|
+
-180 4 0 76 13 158 29 292 58 365 66 600 65 195 0 229 -3 299 -22 164 -45 261
|
|
23
|
+
-113 313 -220 39 -80 45 -207 12 -277 -35 -76 -196 -230 -424 -405 -110 -85
|
|
24
|
+
-245 -198 -300 -252 -64 -63 -131 -117 -185 -149 -197 -119 -409 -284 -497
|
|
25
|
+
-387 -221 -261 -230 -536 -24 -742 74 -73 150 -117 261 -151 61 -19 99 -23
|
|
26
|
+
202 -24 81 0 127 -4 125 -10 -9 -26 -95 -76 -182 -105 -112 -37 -178 -79 -220
|
|
27
|
+
-138 -27 -39 -30 -53 -36 -145 -3 -56 -8 -109 -12 -117 -4 -8 -64 -69 -133
|
|
28
|
+
-134 -69 -65 -150 -149 -180 -186 l-54 -67 0 -492 0 -491 2229 0 2230 0 98 62
|
|
29
|
+
c54 34 136 85 183 114 47 29 151 96 233 149 l147 96 0 50 0 51 -117 -63 c-65
|
|
30
|
+
-34 -250 -138 -412 -231 -162 -92 -298 -168 -302 -168 -4 0 -10 36 -14 81 -4
|
|
31
|
+
44 -13 103 -20 130 -13 48 -13 49 8 49 19 1 94 27 322 114 54 20 204 96 452
|
|
32
|
+
227 l83 44 0 108 c0 59 -2 107 -4 107 -2 0 -53 -31 -113 -68 -145 -92 -310
|
|
33
|
+
-182 -442 -242 -162 -73 -144 -74 -237 22 -208 217 -289 440 -301 828 l-6 185
|
|
34
|
+
162 92 c280 160 523 331 616 435 176 195 265 406 238 564 -30 173 -138 301
|
|
35
|
+
-343 403 -188 95 -540 203 -829 256 -74 14 -135 26 -136 28 -1 1 -23 40 -50
|
|
36
|
+
87 -70 124 -239 377 -358 536 -252 336 -503 586 -694 689 -32 18 -101 58 -153
|
|
37
|
+
90 -182 112 -404 198 -590 229 -119 21 -310 28 -414 16z m-87 -435 c293 -51
|
|
38
|
+
484 -158 526 -293 6 -20 -2 -17 -57 23 -194 144 -417 206 -898 251 -68 6 -72
|
|
39
|
+
8 -42 16 112 30 311 31 471 3z m511 -677 c-15 -80 -40 -133 -87 -193 -42 -52
|
|
40
|
+
-126 -109 -178 -120 l-30 -7 35 28 c126 103 181 176 233 313 35 95 48 85 27
|
|
41
|
+
-21z m-285 -426 c-113 -110 -134 -122 -227 -138 -46 -8 -96 -19 -113 -24 l-30
|
|
42
|
+
-9 24 17 c35 25 394 221 406 222 5 0 -22 -31 -60 -68z m-80 -169 c-22 -14 -78
|
|
43
|
+
-57 -123 -94 -56 -46 -96 -71 -120 -75 -33 -6 -223 -61 -277 -80 -69 -24 276
|
|
44
|
+
178 390 228 102 46 104 46 140 47 l30 1 -40 -27z m2033 -515 c-60 -74 -62 -81
|
|
45
|
+
-33 -135 37 -70 32 -114 -26 -250 -33 -78 -33 -39 1 48 37 97 40 155 11 203
|
|
46
|
+
-12 18 -21 42 -21 54 0 20 89 136 98 127 3 -2 -11 -24 -30 -47z m-811 -42 c89
|
|
47
|
+
0 164 -4 167 -7 16 -16 -98 -250 -183 -374 -34 -50 -64 -106 -67 -125 -6 -34
|
|
48
|
+
-10 -37 -92 -70 -48 -19 -89 -36 -92 -38 -4 -1 -17 17 -30 40 -13 24 -48 66
|
|
49
|
+
-77 95 -40 39 -54 61 -59 91 l-6 39 52 7 c51 7 150 44 150 56 0 3 -27 12 -61
|
|
50
|
+
19 -43 8 -71 21 -95 44 l-35 32 71 116 72 116 61 -20 c51 -16 92 -20 224 -21z
|
|
51
|
+
m563 -16 c35 -12 36 -14 54 -95 10 -46 15 -92 11 -105 -4 -13 -25 -42 -47 -66
|
|
52
|
+
l-40 -44 26 -10 c21 -8 25 -14 21 -37 -2 -15 -6 -53 -9 -84 -3 -31 -12 -71
|
|
53
|
+
-20 -90 -51 -121 -55 -129 -71 -123 -8 3 -15 12 -15 19 0 7 -13 24 -30 38 -26
|
|
54
|
+
22 -27 25 -10 30 10 3 28 20 39 38 29 47 27 145 -3 306 -14 71 -28 151 -31
|
|
55
|
+
180 l-7 52 39 4 c41 4 41 4 93 -13z m-206 -47 c4 -32 18 -121 32 -198 28 -150
|
|
56
|
+
33 -220 17 -236 -12 -12 -73 0 -73 14 0 5 -12 35 -27 66 -27 54 -28 60 -27
|
|
57
|
+
222 1 92 5 172 8 178 4 6 20 11 35 11 27 0 29 -3 35 -57z m506 -85 c16 -63 7
|
|
58
|
+
-217 -15 -256 l-14 -27 5 30 c21 125 21 217 -2 294 -5 16 -4 22 3 18 6 -4 17
|
|
59
|
+
-30 23 -59z m-1720 -141 c0 -8 27 -52 59 -98 74 -106 88 -150 72 -222 -15 -69
|
|
60
|
+
-51 -129 -154 -261 -105 -134 -140 -200 -145 -276 -5 -70 8 -113 55 -181 44
|
|
61
|
+
-64 37 -74 -12 -17 -61 71 -70 94 -70 183 0 71 4 88 36 155 20 41 73 125 119
|
|
62
|
+
185 99 132 140 212 140 275 0 48 -14 79 -71 162 -38 55 -57 108 -39 108 5 0
|
|
63
|
+
10 -6 10 -13z m-469 -285 c-11 -22 -61 -76 -110 -121 -95 -87 -111 -115 -111
|
|
64
|
+
-189 0 -38 -2 -43 -15 -32 -24 20 -28 72 -10 116 15 38 256 287 263 272 2 -3
|
|
65
|
+
-6 -24 -17 -46z m-270 -109 c-10 -21 -35 -54 -55 -72 l-36 -35 0 26 c0 18 17
|
|
66
|
+
45 52 83 56 61 72 60 39 -2z m1963 -153 c43 0 168 -36 224 -64 51 -26 88 -57
|
|
67
|
+
113 -96 l17 -25 7 38 c5 23 3 49 -5 67 -17 41 -11 44 30 12 18 -14 39 -27 46
|
|
68
|
+
-28 29 -4 78 -52 97 -94 12 -27 22 -77 25 -123 l5 -78 22 21 23 21 6 -24 c3
|
|
69
|
+
-14 42 -64 86 -112 101 -111 125 -159 124 -245 -1 -53 -8 -83 -42 -162 -67
|
|
70
|
+
-154 -51 -238 51 -283 l29 -12 -47 -41 c-58 -51 -130 -76 -215 -75 -72 0 -142
|
|
71
|
+
25 -174 62 -22 24 -12 27 24 6 33 -19 114 -19 129 -1 10 12 7 18 -18 31 -88
|
|
72
|
+
45 -78 147 29 305 79 117 91 164 58 230 -18 34 -43 58 -108 102 -93 63 -124
|
|
73
|
+
95 -141 146 -18 54 -61 124 -70 115 -5 -5 -12 -30 -15 -56 -13 -90 65 -201
|
|
74
|
+
194 -281 66 -40 86 -70 76 -112 -4 -14 -35 -68 -70 -120 -35 -52 -69 -114 -75
|
|
75
|
+
-137 -7 -23 -16 -46 -21 -52 -16 -18 -8 34 23 152 37 143 40 203 10 261 -28
|
|
76
|
+
56 -82 93 -189 132 -175 63 -187 81 -179 254 l5 119 -41 -63 c-38 -58 -41 -67
|
|
77
|
+
-42 -134 0 -113 47 -179 158 -220 23 -9 69 -25 102 -37 76 -28 120 -71 130
|
|
78
|
+
-126 7 -44 -21 -181 -73 -351 -20 -68 -24 -97 -20 -165 5 -87 18 -123 71 -191
|
|
79
|
+
l31 -39 -39 7 c-154 27 -310 115 -362 203 -22 37 -24 50 -18 95 11 79 24 103
|
|
80
|
+
24 47 1 -59 51 -160 96 -195 37 -28 90 -40 131 -30 26 7 26 7 -20 36 -90 58
|
|
81
|
+
-102 108 -64 283 43 205 25 286 -84 386 -156 141 -213 220 -226 313 -3 28 -8
|
|
82
|
+
36 -16 28 -16 -16 -11 -186 7 -245 l16 -50 -25 30 c-13 17 -39 59 -57 95 -29
|
|
83
|
+
59 -32 73 -32 156 0 103 -8 108 -45 23 -43 -101 -35 -241 19 -348 31 -61 110
|
|
84
|
+
-139 181 -180 36 -20 71 -49 79 -63 14 -24 13 -32 -2 -82 -10 -31 -21 -56 -26
|
|
85
|
+
-56 -4 0 -12 15 -19 34 -20 54 -69 115 -119 146 -26 17 -48 28 -51 26 -2 -3
|
|
86
|
+
10 -22 27 -43 50 -63 85 -150 89 -225 2 -38 1 -68 -2 -68 -3 0 -20 15 -36 33
|
|
87
|
+
l-29 32 -1 -28 c0 -38 -21 -117 -30 -117 -14 0 -27 25 -40 78 -7 32 -20 58
|
|
88
|
+
-33 67 -21 15 -21 15 9 15 33 0 84 29 52 30 -21 0 -68 21 -68 30 0 4 12 13 28
|
|
89
|
+
20 l27 13 -30 10 c-42 13 -82 54 -96 94 -7 23 -9 73 -4 152 9 148 -1 190 -61
|
|
90
|
+
250 -24 24 -44 47 -44 52 0 4 -21 25 -46 47 -55 47 -55 62 3 138 l38 49 6 -42
|
|
91
|
+
c7 -57 15 -62 29 -19 15 47 63 94 203 199 65 49 125 100 134 114 l17 26 39
|
|
92
|
+
-47 c31 -37 45 -46 71 -46z m-2619 12 c87 -25 284 -19 423 15 17 4 22 1 22
|
|
93
|
+
-15 0 -28 -19 -33 -157 -39 -161 -8 -327 19 -368 60 -7 7 -3 7 10 2 11 -4 43
|
|
94
|
+
-14 70 -23z m955 -71 c0 -41 39 -93 93 -121 47 -25 62 -43 51 -62 -4 -5 -13
|
|
95
|
+
-4 -22 5 -10 8 -45 29 -78 46 -73 39 -89 65 -74 122 13 45 30 52 30 10z m-385
|
|
96
|
+
-156 c7 -96 2 -145 -15 -145 -6 0 -10 12 -10 26 0 15 -5 55 -12 89 -9 50 -8
|
|
97
|
+
70 2 94 7 17 16 31 20 31 5 0 11 -43 15 -95z m946 -210 c53 -55 96 -131 83
|
|
98
|
+
-144 -5 -6 -142 142 -170 183 -26 38 -37 81 -28 109 6 18 10 14 30 -31 13 -30
|
|
99
|
+
50 -81 85 -117z m-641 70 c0 -8 23 -58 50 -110 28 -52 50 -108 50 -123 l-1
|
|
100
|
+
-27 -16 30 c-8 17 -30 46 -49 65 -18 19 -38 48 -43 63 -14 36 -14 117 -1 117
|
|
101
|
+
6 0 10 -7 10 -15z m435 -296 c4 -11 38 -52 77 -91 38 -38 67 -72 64 -75 -9 -9
|
|
102
|
+
-114 57 -140 88 -17 20 -26 43 -26 65 0 38 15 46 25 13z m263 -836 c-10 -2
|
|
103
|
+
-26 -2 -35 0 -10 3 -2 5 17 5 19 0 27 -2 18 -5z"/>
|
|
104
|
+
<path d="M5097 2386 c-34 -82 -138 -227 -231 -321 -105 -107 -232 -203 -431
|
|
105
|
+
-326 -284 -177 -281 -174 -279 -319 3 -219 60 -416 166 -576 82 -122 145 -136
|
|
106
|
+
288 -64 117 59 274 150 403 233 l107 70 0 674 c0 370 -1 673 -2 673 -2 0 -11
|
|
107
|
+
-20 -21 -44z"/>
|
|
108
|
+
<path d="M1 1675 l-1 -490 109 111 c118 119 111 108 154 279 8 33 27 88 41
|
|
109
|
+
123 36 85 27 113 -58 181 -87 69 -191 182 -220 238 l-25 48 0 -490z"/>
|
|
110
|
+
<path d="M5100 600 c-9 -6 -10 -10 -3 -10 6 0 15 5 18 10 8 12 4 12 -15 0z"/>
|
|
111
|
+
<path d="M5050 580 c-8 -5 -10 -10 -5 -10 6 0 17 5 25 10 8 5 11 10 5 10 -5 0
|
|
112
|
+
-17 -5 -25 -10z"/>
|
|
113
|
+
<path d="M4680 385 c-14 -8 -20 -14 -15 -14 6 0 21 6 35 14 14 8 21 14 15 14
|
|
114
|
+
-5 0 -21 -6 -35 -14z"/>
|
|
115
|
+
<path d="M5043 272 c-39 -26 -105 -67 -145 -91 -40 -24 -118 -74 -172 -110
|
|
116
|
+
l-100 -66 173 -3 174 -2 73 45 74 46 0 115 c0 63 -1 114 -2 114 -2 -1 -35 -22
|
|
117
|
+
-75 -48z"/>
|
|
118
|
+
<path d="M4420 270 c-19 -11 -30 -19 -25 -19 6 0 26 8 45 19 19 11 31 19 25
|
|
119
|
+
19 -5 0 -26 -8 -45 -19z"/>
|
|
120
|
+
</g>
|
|
121
|
+
</svg>
|
|
Binary file
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Reldens",
|
|
3
|
+
"short_name": "Reldens",
|
|
4
|
+
"start_url": "/",
|
|
5
|
+
"display": "standalone",
|
|
6
|
+
"theme_color": "#000000",
|
|
7
|
+
"background_color": "#000000",
|
|
8
|
+
"icons": [
|
|
9
|
+
{
|
|
10
|
+
"src": "assets/favicons/android-icon-144x144.png",
|
|
11
|
+
"sizes": "144x144",
|
|
12
|
+
"type": "image/png",
|
|
13
|
+
"purpose": "any"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"src": "assets/favicons/android-icon-192x192.png",
|
|
17
|
+
"sizes": "192x192",
|
|
18
|
+
"type": "image/png"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"src": "assets/favicons/android-icon-512x512.png",
|
|
22
|
+
"sizes": "512x512",
|
|
23
|
+
"type": "image/png",
|
|
24
|
+
"purpose": "maskable"
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|