@reldens/cms 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/admin/assets/admin/filters.png +0 -0
- package/admin/assets/admin/list.png +0 -0
- package/admin/reldens-admin-client.css +830 -0
- package/admin/reldens-admin-client.js +272 -0
- package/admin/templates/dashboard.html +1 -0
- package/admin/templates/default-copyright.html +5 -0
- package/admin/templates/edit.html +25 -0
- package/admin/templates/fields/edit/button.html +3 -0
- package/admin/templates/fields/edit/checkbox.html +1 -0
- package/admin/templates/fields/edit/file.html +2 -0
- package/admin/templates/fields/edit/radio.html +1 -0
- package/admin/templates/fields/edit/select.html +5 -0
- package/admin/templates/fields/edit/text.html +1 -0
- package/admin/templates/fields/edit/textarea.html +1 -0
- package/admin/templates/fields/view/boolean.html +1 -0
- package/admin/templates/fields/view/image.html +4 -0
- package/admin/templates/fields/view/images.html +7 -0
- package/admin/templates/fields/view/link.html +1 -0
- package/admin/templates/fields/view/links.html +6 -0
- package/admin/templates/fields/view/text.html +1 -0
- package/admin/templates/layout.html +37 -0
- package/admin/templates/list-content.html +70 -0
- package/admin/templates/list.html +35 -0
- package/admin/templates/login.html +19 -0
- package/admin/templates/management.html +22 -0
- package/admin/templates/maps-wizard-maps-selection.html +85 -0
- package/admin/templates/maps-wizard.html +341 -0
- package/admin/templates/objects-import.html +143 -0
- package/admin/templates/pagination-link.html +1 -0
- package/admin/templates/sidebar-header.html +4 -0
- package/admin/templates/sidebar-item.html +3 -0
- package/admin/templates/sidebar.html +11 -0
- package/admin/templates/skills-import.html +201 -0
- package/admin/templates/view.html +23 -0
- package/bin/reldens-cms.js +20 -8
- package/index.js +2 -2
- package/lib/entities-loader.js +45 -0
- package/lib/{storefront.js → frontend.js} +10 -6
- package/lib/installer.js +133 -45
- package/lib/manager.js +65 -26
- package/migrations/default-user.sql +2 -1
- package/package.json +2 -2
- package/templates/.env.dist +11 -11
- package/templates/css/styles.css +1 -1
- package/templates/index.js.dist +32 -0
- package/templates/js/scripts.js +1 -1
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
<h2>Skills Import</h2>
|
|
2
|
+
<div class="sub-content objects-import">
|
|
3
|
+
<div class="sub-content-box">
|
|
4
|
+
<form class="sub-content-form objects-import-form confirmation-required"
|
|
5
|
+
name="objects-import-form"
|
|
6
|
+
id="objects-import-form"
|
|
7
|
+
action="{{&actionPath}}"
|
|
8
|
+
method="post"
|
|
9
|
+
enctype="multipart/form-data">
|
|
10
|
+
<div class="main-action-container">
|
|
11
|
+
<p>What would you like to do?</p>
|
|
12
|
+
<hr/>
|
|
13
|
+
<button type="button" class="button button-primary set-sample-data">
|
|
14
|
+
Set Sample Data in "Generator Data"
|
|
15
|
+
</button>
|
|
16
|
+
<p>NOTE: if you already uploaded all your files and never manually removed them from the "generate-data" folder, you don't need to upload the same files again.</p>
|
|
17
|
+
<div class="input-box">
|
|
18
|
+
<label for="generatorJsonFiles">JSON Files</label>
|
|
19
|
+
<input type="file" name="generatorJsonFiles" id="generatorJsonFiles" multiple="multiple"/>
|
|
20
|
+
</div>
|
|
21
|
+
<hr/>
|
|
22
|
+
<label for="generatorData">Generator data (if not empty this data field will be used instead of the files):</label>
|
|
23
|
+
<textarea name="generatorData" id="generatorData" class="generatorData" cols="30" rows="10" placeholder="JSON data for skills import process"></textarea>
|
|
24
|
+
</div>
|
|
25
|
+
<div class="submit-container">
|
|
26
|
+
<input type="submit" class="button button-primary button-objects-import" value="Import"/>
|
|
27
|
+
<img class="hidden loading" src="/assets/web/loading.gif"/>
|
|
28
|
+
</div>
|
|
29
|
+
</form>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
<script type="text/javascript">
|
|
33
|
+
let sampleDataElement = document.querySelector('.set-sample-data');
|
|
34
|
+
let generatorDataElement = document.querySelector('.generatorData');
|
|
35
|
+
if(sampleDataElement && generatorDataElement){
|
|
36
|
+
sampleDataElement.addEventListener('click', () => {
|
|
37
|
+
generatorDataElement.value = JSON.stringify({
|
|
38
|
+
"options": {
|
|
39
|
+
"removeAll": false,
|
|
40
|
+
"override": false,
|
|
41
|
+
"update": true
|
|
42
|
+
},
|
|
43
|
+
"defaults": {
|
|
44
|
+
"properties": {
|
|
45
|
+
"autoValidation": 0,
|
|
46
|
+
"skillDelay": 1500,
|
|
47
|
+
"castTime": 0,
|
|
48
|
+
"usesLimit": 0,
|
|
49
|
+
"range": 0,
|
|
50
|
+
"rangeAutomaticValidation": 1,
|
|
51
|
+
"rangePropertyX": "state/x",
|
|
52
|
+
"rangePropertyY": "state/y",
|
|
53
|
+
"rangeTargetPropertyX": null,
|
|
54
|
+
"rangeTargetPropertyY": null,
|
|
55
|
+
"allowSelfTarget": 0,
|
|
56
|
+
"criticalChance": 0,
|
|
57
|
+
"criticalMultiplier": 1,
|
|
58
|
+
"criticalFixedValue": 0,
|
|
59
|
+
"customData": null
|
|
60
|
+
},
|
|
61
|
+
"animations": {
|
|
62
|
+
"appendSkillKeyOnAnimationImage": true,
|
|
63
|
+
"defaults": {
|
|
64
|
+
"enabled": true,
|
|
65
|
+
"type": "spritesheet",
|
|
66
|
+
"frameWidth": 64,
|
|
67
|
+
"frameHeight": 70,
|
|
68
|
+
"start": 0
|
|
69
|
+
},
|
|
70
|
+
"bullet": {
|
|
71
|
+
"img": "_bullet",
|
|
72
|
+
"end": 3,
|
|
73
|
+
"repeat": -1,
|
|
74
|
+
"frameRate": 1,
|
|
75
|
+
"dir": 3
|
|
76
|
+
},
|
|
77
|
+
"cast": {
|
|
78
|
+
"img": "_cast",
|
|
79
|
+
"end": 3,
|
|
80
|
+
"repeat": -1,
|
|
81
|
+
"destroyTime": 2000,
|
|
82
|
+
"depthByPlayer": "above"
|
|
83
|
+
|
|
84
|
+
},
|
|
85
|
+
"hit": {
|
|
86
|
+
"img": "_hit",
|
|
87
|
+
"end": 4,
|
|
88
|
+
"repeat": 0,
|
|
89
|
+
"depthByPlayer": "above"
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"attack": {
|
|
93
|
+
"affectedProperty": "stats/hp",
|
|
94
|
+
"allowEffectBelowZero": 0,
|
|
95
|
+
"applyDirectDamage": 0,
|
|
96
|
+
"attackProperties": "stats/atk,stats/stamina,stats/speed",
|
|
97
|
+
"defenseProperties": "stats/def,stats/stamina,stats/speed",
|
|
98
|
+
"aimProperties": "stats/aim",
|
|
99
|
+
"dodgeProperties": "stats/dodge",
|
|
100
|
+
"dodgeFullEnabled": 0,
|
|
101
|
+
"dodgeOverAimSuccess": 1,
|
|
102
|
+
"damageAffected": 0,
|
|
103
|
+
"criticalAffected": 0
|
|
104
|
+
},
|
|
105
|
+
"physicalData": {
|
|
106
|
+
"magnitude": 0,
|
|
107
|
+
"objectWidth": 0,
|
|
108
|
+
"objectHeight": 0,
|
|
109
|
+
"validateTargetOnHit": 0
|
|
110
|
+
},
|
|
111
|
+
"targetEffects": {
|
|
112
|
+
"minValue": 0,
|
|
113
|
+
"maxValue": 0,
|
|
114
|
+
"minProperty": null,
|
|
115
|
+
"maxProperty": null
|
|
116
|
+
},
|
|
117
|
+
"ownerEffects": {
|
|
118
|
+
"minValue": 0,
|
|
119
|
+
"maxValue": 0,
|
|
120
|
+
"minProperty": null,
|
|
121
|
+
"maxProperty": null
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
"skills": {
|
|
125
|
+
"punch": {
|
|
126
|
+
"classPathLevelRelations": {"all": "1"},
|
|
127
|
+
"objectsRelations": {"enemy_1": "player", "enemy_2": "player"},
|
|
128
|
+
"properties": {
|
|
129
|
+
"skillDelay": 1000,
|
|
130
|
+
"range": 50,
|
|
131
|
+
"criticalChance": 10,
|
|
132
|
+
"criticalMultiplier": 2
|
|
133
|
+
},
|
|
134
|
+
"typeData": {
|
|
135
|
+
"key": "attack",
|
|
136
|
+
"properties": {
|
|
137
|
+
"hitDamage": 3
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
"throwRock": {
|
|
142
|
+
"classPathLevelRelations": {"all": "2"},
|
|
143
|
+
"objectsRelations": {"enemy_1": "player", "enemy_2": "player"},
|
|
144
|
+
"properties": {
|
|
145
|
+
"skillDelay": 2000,
|
|
146
|
+
"range": 250,
|
|
147
|
+
"criticalChance": 10,
|
|
148
|
+
"criticalMultiplier": 2
|
|
149
|
+
},
|
|
150
|
+
"typeData": {
|
|
151
|
+
"key": "physical_attack",
|
|
152
|
+
"properties": {
|
|
153
|
+
"hitDamage": 5
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
"physicalData": {
|
|
157
|
+
"magnitude": 350,
|
|
158
|
+
"objectWidth": 5,
|
|
159
|
+
"objectHeight": 5
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
"heal": {
|
|
163
|
+
"classPathLevelRelations": {"sorcerer": "3"},
|
|
164
|
+
"properties": {
|
|
165
|
+
"skillDelay": 1500,
|
|
166
|
+
"allowSelfTarget": 1,
|
|
167
|
+
"castTime": 2000
|
|
168
|
+
},
|
|
169
|
+
"typeData": {
|
|
170
|
+
"key": "effect"
|
|
171
|
+
},
|
|
172
|
+
"animations": {
|
|
173
|
+
"cast": {},
|
|
174
|
+
"hit": {}
|
|
175
|
+
},
|
|
176
|
+
"clearPrevious": ["targetEffects", "ownerEffects", "ownerConditions"],
|
|
177
|
+
"ownerConditions": [{
|
|
178
|
+
"key": "available_mp",
|
|
179
|
+
"propertyKey": "stats/mp",
|
|
180
|
+
"conditional": "ge",
|
|
181
|
+
"value": 2
|
|
182
|
+
}],
|
|
183
|
+
"ownerEffects": [{
|
|
184
|
+
"key": "dec_mp",
|
|
185
|
+
"propertyKey": "stats/mp",
|
|
186
|
+
"operationKey": "2",
|
|
187
|
+
"value": 2
|
|
188
|
+
}],
|
|
189
|
+
"targetEffects": [{
|
|
190
|
+
"key": "heal",
|
|
191
|
+
"propertyKey": "stats/mp",
|
|
192
|
+
"operationKey": "1",
|
|
193
|
+
"value": 5,
|
|
194
|
+
"maxProperty": "statsBase/hp"
|
|
195
|
+
}]
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
</script>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<div class="entity-view {{&entityName}}-view">
|
|
2
|
+
<h2>{{&templateTitle}}</h2>
|
|
3
|
+
{{#fields}}
|
|
4
|
+
<div class="view-field">
|
|
5
|
+
<span class="field-name">{{&name}}</span>
|
|
6
|
+
<span class="field-value">{{&value}}</span>
|
|
7
|
+
</div>
|
|
8
|
+
{{/fields}}
|
|
9
|
+
<div class="actions">
|
|
10
|
+
<a class="button button-primary" href="{{&entityNewRoute}}">Create New</a>
|
|
11
|
+
<a class="button button-primary" href="{{&entityEditRoute}}">Edit</a>
|
|
12
|
+
<a class="button button-secondary" href="{{&entityListRoute}}">Back</a>
|
|
13
|
+
<form class="form-delete" name="delete-form-{{&id}}" id="delete-form-{{&id}}" action="{{&entityDeleteRoute}}" method="post">
|
|
14
|
+
<span>
|
|
15
|
+
<input type="hidden" name="ids[]" value="{{&id}}"/>
|
|
16
|
+
<button class="button button-danger" type="submit">Delete</button>
|
|
17
|
+
</span>
|
|
18
|
+
</form>
|
|
19
|
+
<div class="extra-actions">
|
|
20
|
+
{{&extraContent}}
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
package/bin/reldens-cms.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
*
|
|
@@ -7,15 +7,27 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
const { Manager } = require('../index');
|
|
10
|
+
const { Logger } = require('@reldens/utils');
|
|
10
11
|
|
|
11
12
|
let args = process.argv.slice(2);
|
|
12
|
-
let projectRoot = args[0] ||
|
|
13
|
+
let projectRoot = args[0] || process.cwd();
|
|
13
14
|
|
|
14
|
-
let manager = new Manager({
|
|
15
|
-
|
|
16
|
-
});
|
|
15
|
+
let manager = new Manager({projectRoot});
|
|
16
|
+
Logger.debug('Reldens CMS Manager instance created.', {configuration: manager.config});
|
|
17
17
|
|
|
18
|
-
manager.start().
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
let started = manager.start().then((result) => {
|
|
19
|
+
if(!result){
|
|
20
|
+
Logger.info('Reldens CMS started by command failed.');
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
Logger.info('Reldens CMS started by command.');
|
|
24
|
+
return true;
|
|
25
|
+
}).catch((error) => {
|
|
26
|
+
Logger.error('Failed to start CMS:', error);
|
|
27
|
+
process.exit();
|
|
21
28
|
});
|
|
29
|
+
|
|
30
|
+
if(!started){
|
|
31
|
+
Logger.error('Reldens CMS start process failed.');
|
|
32
|
+
process.exit();
|
|
33
|
+
}
|
package/index.js
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
const { Manager } = require('./lib/manager');
|
|
8
8
|
const { Installer } = require('./lib/installer');
|
|
9
|
-
const {
|
|
9
|
+
const { Frontend } = require('./lib/frontend');
|
|
10
10
|
|
|
11
11
|
module.exports = {
|
|
12
12
|
Manager,
|
|
13
13
|
Installer,
|
|
14
|
-
|
|
14
|
+
Frontend
|
|
15
15
|
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - EntitiesLoader
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { FileHandler } = require('@reldens/server-utils');
|
|
8
|
+
const { Logger, sc } = require('@reldens/utils');
|
|
9
|
+
|
|
10
|
+
class EntitiesLoader
|
|
11
|
+
{
|
|
12
|
+
|
|
13
|
+
constructor(props)
|
|
14
|
+
{
|
|
15
|
+
this.projectRoot = sc.get(props, 'projectRoot', './');
|
|
16
|
+
this.generatedEntitiesModelsFolder = FileHandler.joinPaths(this.projectRoot, 'generated-entities', 'models');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
loadEntities(driverKey)
|
|
20
|
+
{
|
|
21
|
+
let entitiesPath = FileHandler.joinPaths(
|
|
22
|
+
this.generatedEntitiesModelsFolder,
|
|
23
|
+
driverKey,
|
|
24
|
+
'registered-models-'+driverKey+'.js'
|
|
25
|
+
);
|
|
26
|
+
if(!FileHandler.exists(entitiesPath)){
|
|
27
|
+
Logger.warning('Entities file not found: ' + entitiesPath);
|
|
28
|
+
return {};
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
let loadedEntities = require(entitiesPath);
|
|
32
|
+
return {
|
|
33
|
+
entities: loadedEntities.entitiesConfig,
|
|
34
|
+
entitiesRaw: loadedEntities.rawRegisteredEntities,
|
|
35
|
+
translations: loadedEntities.entitiesTranslations,
|
|
36
|
+
}
|
|
37
|
+
} catch(error){
|
|
38
|
+
Logger.error('Failed to load generated entities: ' + error.message);
|
|
39
|
+
}
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports.EntitiesLoader = EntitiesLoader;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
*
|
|
3
|
-
* Reldens - CMS -
|
|
3
|
+
* Reldens - CMS - Frontend
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -8,12 +8,13 @@ const { FileHandler } = require('@reldens/server-utils');
|
|
|
8
8
|
const { Logger, sc } = require('@reldens/utils');
|
|
9
9
|
const mustache = require('mustache');
|
|
10
10
|
|
|
11
|
-
class
|
|
11
|
+
class Frontend
|
|
12
12
|
{
|
|
13
13
|
|
|
14
14
|
constructor(props)
|
|
15
15
|
{
|
|
16
16
|
this.app = sc.get(props, 'app', false);
|
|
17
|
+
this.appServerFactory = sc.get(props, 'appServerFactory', false);
|
|
17
18
|
this.dataServer = sc.get(props, 'dataServer', false);
|
|
18
19
|
this.projectRoot = sc.get(props, 'projectRoot', './');
|
|
19
20
|
this.templatesPath = FileHandler.joinPaths(this.projectRoot, 'templates');
|
|
@@ -45,11 +46,14 @@ class Storefront
|
|
|
45
46
|
|
|
46
47
|
setupStaticAssets()
|
|
47
48
|
{
|
|
48
|
-
if(!this.app || !this.publicPath){
|
|
49
|
+
if(!this.app || !this.appServerFactory || !this.publicPath){
|
|
49
50
|
return false;
|
|
50
51
|
}
|
|
51
|
-
this.
|
|
52
|
-
|
|
52
|
+
if(this.appServerFactory && this.appServerFactory.applicationFramework){
|
|
53
|
+
this.app.use(this.appServerFactory.applicationFramework.static(this.publicPath));
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
53
57
|
}
|
|
54
58
|
|
|
55
59
|
async handleRequest(req, res)
|
|
@@ -225,4 +229,4 @@ class Storefront
|
|
|
225
229
|
|
|
226
230
|
}
|
|
227
231
|
|
|
228
|
-
module.exports.
|
|
232
|
+
module.exports.Frontend = Frontend;
|
package/lib/installer.js
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
const { FileHandler, Encryptor } = require('@reldens/server-utils');
|
|
8
8
|
const { DriversMap, EntitiesGenerator, PrismaSchemaGenerator } = require('@reldens/storage');
|
|
9
|
+
const { EntitiesLoader } = require('./entities-loader');
|
|
10
|
+
const { AdminEntitiesGenerator } = require('./admin-entities-generator');
|
|
9
11
|
const { Logger, sc } = require('@reldens/utils');
|
|
10
12
|
const mustache = require('mustache');
|
|
11
13
|
|
|
@@ -15,6 +17,7 @@ class Installer
|
|
|
15
17
|
constructor(props)
|
|
16
18
|
{
|
|
17
19
|
this.app = sc.get(props, 'app', false);
|
|
20
|
+
this.appServer = sc.get(props, 'appServer', false);
|
|
18
21
|
this.appServerFactory = sc.get(props, 'appServerFactory', false);
|
|
19
22
|
this.projectRoot = sc.get(props, 'projectRoot', './');
|
|
20
23
|
this.encoding = sc.get(props, 'encoding', 'utf8');
|
|
@@ -24,6 +27,11 @@ class Installer
|
|
|
24
27
|
this.installerPath = FileHandler.joinPaths(this.modulePath, 'install');
|
|
25
28
|
this.migrationsPath = FileHandler.joinPaths(this.modulePath, 'migrations');
|
|
26
29
|
this.defaultTemplatesPath = FileHandler.joinPaths(this.modulePath, 'templates');
|
|
30
|
+
this.moduleAdminPath = FileHandler.joinPaths(this.modulePath, 'admin');
|
|
31
|
+
this.indexTemplatePath = FileHandler.joinPaths(this.defaultTemplatesPath, 'index.js.dist');
|
|
32
|
+
this.postInstallCallback = sc.get(props, 'postInstallCallback', false);
|
|
33
|
+
this.entitiesLoader = new EntitiesLoader({projectRoot: this.projectRoot});
|
|
34
|
+
this.adminEntitiesGenerator = new AdminEntitiesGenerator();
|
|
27
35
|
}
|
|
28
36
|
|
|
29
37
|
isInstalled()
|
|
@@ -31,7 +39,7 @@ class Installer
|
|
|
31
39
|
return FileHandler.exists(this.installLockPath);
|
|
32
40
|
}
|
|
33
41
|
|
|
34
|
-
async prepareSetup(app, appServerFactory)
|
|
42
|
+
async prepareSetup(app, appServer, appServerFactory)
|
|
35
43
|
{
|
|
36
44
|
if(!app){
|
|
37
45
|
Logger.error('Missing app on prepareSetup for Installer.');
|
|
@@ -43,6 +51,7 @@ class Installer
|
|
|
43
51
|
}
|
|
44
52
|
this.app = app;
|
|
45
53
|
this.appServerFactory = appServerFactory;
|
|
54
|
+
this.appServer = appServer;
|
|
46
55
|
app.use('/install-assets', appServerFactory.applicationFramework.static(this.installerPath, {index: false}));
|
|
47
56
|
app.use(appServerFactory.session({
|
|
48
57
|
secret: Encryptor.generateSecretKey(),
|
|
@@ -63,18 +72,44 @@ class Installer
|
|
|
63
72
|
if(this.isInstalled()){
|
|
64
73
|
return next();
|
|
65
74
|
}
|
|
66
|
-
|
|
75
|
+
let urlPath = req._parsedUrl.pathname;
|
|
76
|
+
if('' === urlPath || '/' === urlPath){
|
|
67
77
|
let installerIndexPath = FileHandler.joinPaths(this.installerPath, 'index.html');
|
|
68
78
|
if(!FileHandler.exists(installerIndexPath)){
|
|
69
79
|
return res.status(500).send('Installer template not found.');
|
|
70
80
|
}
|
|
71
81
|
let content = FileHandler.readFile(installerIndexPath);
|
|
72
82
|
let contentParams = req.session?.templateVariables || this.fetchDefaults();
|
|
83
|
+
let errorParam = req.query?.error;
|
|
84
|
+
if(errorParam){
|
|
85
|
+
contentParams.errorMessage = this.getErrorMessage(errorParam);
|
|
86
|
+
}
|
|
73
87
|
return res.send(mustache.render(content, contentParams));
|
|
74
88
|
}
|
|
89
|
+
if('/install' !== urlPath){
|
|
90
|
+
return res.redirect('/');
|
|
91
|
+
}
|
|
75
92
|
next();
|
|
76
93
|
}
|
|
77
94
|
|
|
95
|
+
getErrorMessage(errorCode)
|
|
96
|
+
{
|
|
97
|
+
let errorMessages = {
|
|
98
|
+
'invalid-driver': 'Invalid storage driver selected.',
|
|
99
|
+
'connection-failed': 'Database connection failed. Please check your credentials.',
|
|
100
|
+
'raw-query-not-found': 'Query method not found in driver.',
|
|
101
|
+
'sql-file-not-found': 'SQL installation file not found.',
|
|
102
|
+
'sql-tables-creation-failed': 'Failed to create database tables.',
|
|
103
|
+
'sql-default-user-error': 'Failed to create default user.',
|
|
104
|
+
'installation-entities-generation-failed': 'Failed to generate entities.',
|
|
105
|
+
'installation-process-failed': 'Installation process failed.',
|
|
106
|
+
'installation-entities-callback-failed': 'Failed to process entities for callback.',
|
|
107
|
+
'configuration-error': 'Configuration error while completing installation.',
|
|
108
|
+
'already-installed': 'The application is already installed.'
|
|
109
|
+
};
|
|
110
|
+
return errorMessages[errorCode] || 'An unknown error occurred during installation.';
|
|
111
|
+
}
|
|
112
|
+
|
|
78
113
|
async executeInstallProcess(req, res)
|
|
79
114
|
{
|
|
80
115
|
if(this.isInstalled()){
|
|
@@ -129,7 +164,7 @@ class Installer
|
|
|
129
164
|
if(FileHandler.exists(defaultUserSqlPath)){
|
|
130
165
|
let queryUserResult = await this.executeQueryFile(dbDriver, defaultUserSqlPath);
|
|
131
166
|
if(!queryUserResult){
|
|
132
|
-
Logger.error('Default user creation failed.');
|
|
167
|
+
Logger.error('Default user creation failed.', queryUserResult);
|
|
133
168
|
return res.redirect('/?error=sql-default-user-error');
|
|
134
169
|
}
|
|
135
170
|
Logger.info('Created default user.');
|
|
@@ -144,16 +179,45 @@ class Installer
|
|
|
144
179
|
Logger.error('Installation error: '+error.message);
|
|
145
180
|
return res.redirect('/?error=installation-process-failed');
|
|
146
181
|
}
|
|
147
|
-
if('' === templateVariables['app-admin-path']){
|
|
148
|
-
templateVariables['app-admin-path'] = '/reldens-admin';
|
|
149
|
-
}
|
|
150
182
|
try {
|
|
151
183
|
await this.createEnvFile(templateVariables);
|
|
152
|
-
await this.createLockFile();
|
|
153
184
|
await this.prepareProjectDirectories();
|
|
185
|
+
await this.copyAdminDirectory();
|
|
186
|
+
await this.createIndexJsFile(templateVariables);
|
|
187
|
+
if(sc.isFunction(this.postInstallCallback)){
|
|
188
|
+
let loadedEntities = this.entitiesLoader.loadEntities(selectedDriver);
|
|
189
|
+
if(loadedEntities.rawRegisteredEntities){
|
|
190
|
+
dbDriver.rawEntities = {};
|
|
191
|
+
let entityNames = Object.keys(loadedEntities.rawRegisteredEntities);
|
|
192
|
+
for(let i = 0; i < entityNames.length; i++){
|
|
193
|
+
let entityName = entityNames[i];
|
|
194
|
+
dbDriver.rawEntities[entityName] = loadedEntities.rawRegisteredEntities[entityName];
|
|
195
|
+
}
|
|
196
|
+
await dbDriver.generateEntities();
|
|
197
|
+
let adminEntities = this.adminEntitiesGenerator.generate(
|
|
198
|
+
loadedEntities.rawRegisteredEntities,
|
|
199
|
+
dbDriver.entityManager.entities
|
|
200
|
+
);
|
|
201
|
+
if(this.appServer && sc.isFunction(this.appServer.close)){
|
|
202
|
+
await this.appServer.close();
|
|
203
|
+
}
|
|
204
|
+
Logger.debug('Running postInstallCallback.');
|
|
205
|
+
await this.postInstallCallback({
|
|
206
|
+
entities: adminEntities,
|
|
207
|
+
rawEntities: loadedEntities.rawRegisteredEntities,
|
|
208
|
+
entitiesConfig: loadedEntities.entitiesConfig || {},
|
|
209
|
+
entitiesTranslations: loadedEntities.entitiesTranslations || {}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
await this.createLockFile();
|
|
154
214
|
Logger.info('Installation successful!');
|
|
155
|
-
let
|
|
156
|
-
|
|
215
|
+
let successContent = 'Installation successful! Run "node ." to start your CMS.';
|
|
216
|
+
let successFileContent = FileHandler.readFile(FileHandler.joinPaths(this.installerPath, 'success.html'));
|
|
217
|
+
if(successFileContent){
|
|
218
|
+
successContent = mustache.render(successFileContent, {adminPath: templateVariables['app-admin-path']});
|
|
219
|
+
}
|
|
220
|
+
return res.send(successContent);
|
|
157
221
|
} catch (error) {
|
|
158
222
|
Logger.error('Configuration error: '+error.message);
|
|
159
223
|
return res.redirect('/?error=configuration-error');
|
|
@@ -213,56 +277,80 @@ class Installer
|
|
|
213
277
|
dbDriver: templateVariables['db-storage-driver'],
|
|
214
278
|
adminPath: templateVariables['app-admin-path'],
|
|
215
279
|
adminSecret: Encryptor.generateSecretKey(),
|
|
216
|
-
host: templateVariables['app-host']
|
|
217
|
-
port: templateVariables['app-port']
|
|
280
|
+
host: templateVariables['app-host'],
|
|
281
|
+
port: templateVariables['app-port']
|
|
218
282
|
});
|
|
219
283
|
return FileHandler.writeFile(this.envFilePath, envContent);
|
|
220
284
|
}
|
|
221
285
|
|
|
286
|
+
async createIndexJsFile(templateVariables)
|
|
287
|
+
{
|
|
288
|
+
if(!FileHandler.exists(this.indexTemplatePath)){
|
|
289
|
+
Logger.error('Index.js template not found: '+this.indexTemplatePath);
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
let indexTemplate = FileHandler.readFile(this.indexTemplatePath);
|
|
293
|
+
let driverKey = templateVariables['db-storage-driver'];
|
|
294
|
+
let indexContent = mustache.render(indexTemplate, {driverKey});
|
|
295
|
+
let indexFilePath = FileHandler.joinPaths(this.projectRoot, 'index.js');
|
|
296
|
+
if(FileHandler.exists(indexFilePath)){
|
|
297
|
+
Logger.info('Index.js file already exists, the CMS installer will not override the existent one.');
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
return FileHandler.writeFile(indexFilePath, indexContent);
|
|
301
|
+
}
|
|
302
|
+
|
|
222
303
|
async createLockFile()
|
|
223
304
|
{
|
|
224
|
-
return FileHandler.writeFile(this.installLockPath,
|
|
225
|
-
|
|
305
|
+
return FileHandler.writeFile(this.installLockPath, 'Installation completed on '+new Date().toISOString());
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async copyAdminDirectory()
|
|
309
|
+
{
|
|
310
|
+
let projectAdminPath = FileHandler.joinPaths(this.projectRoot, 'admin');
|
|
311
|
+
if(FileHandler.exists(projectAdminPath)){
|
|
312
|
+
Logger.info('Admin folder already exists in project root.');
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
315
|
+
if(!FileHandler.exists(this.moduleAdminPath)){
|
|
316
|
+
Logger.error('Admin folder not found in module path: '+this.moduleAdminPath);
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
FileHandler.copyFolderSync(this.moduleAdminPath, projectAdminPath);
|
|
320
|
+
Logger.info('Admin folder copied to project root.');
|
|
321
|
+
return true;
|
|
226
322
|
}
|
|
227
323
|
|
|
228
324
|
async prepareProjectDirectories()
|
|
229
325
|
{
|
|
230
326
|
let projectTemplatesPath = FileHandler.joinPaths(this.projectRoot, 'templates');
|
|
231
|
-
|
|
232
|
-
FileHandler.createFolder(projectTemplatesPath);
|
|
233
|
-
}
|
|
327
|
+
FileHandler.createFolder(projectTemplatesPath);
|
|
234
328
|
let projectPublicPath = FileHandler.joinPaths(this.projectRoot, 'public');
|
|
235
|
-
|
|
236
|
-
FileHandler.createFolder(projectPublicPath);
|
|
237
|
-
}
|
|
329
|
+
FileHandler.createFolder(projectPublicPath);
|
|
238
330
|
let projectCssPath = FileHandler.joinPaths(projectPublicPath, 'css');
|
|
239
|
-
|
|
240
|
-
FileHandler.createFolder(projectCssPath);
|
|
241
|
-
}
|
|
331
|
+
FileHandler.createFolder(projectCssPath);
|
|
242
332
|
let projectJsPath = FileHandler.joinPaths(projectPublicPath, 'js');
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
FileHandler.
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
FileHandler.
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
FileHandler.
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
FileHandler.copyFile(defaultJsPath, FileHandler.joinPaths(projectJsPath, 'scripts.js'));
|
|
265
|
-
}
|
|
333
|
+
FileHandler.createFolder(projectJsPath);
|
|
334
|
+
FileHandler.copyFile(
|
|
335
|
+
FileHandler.joinPaths(this.defaultTemplatesPath, 'page.html'),
|
|
336
|
+
FileHandler.joinPaths(projectTemplatesPath, 'page.html')
|
|
337
|
+
);
|
|
338
|
+
FileHandler.copyFile(
|
|
339
|
+
FileHandler.joinPaths(this.defaultTemplatesPath, '404.html'),
|
|
340
|
+
FileHandler.joinPaths(projectTemplatesPath, '404.html')
|
|
341
|
+
);
|
|
342
|
+
FileHandler.copyFile(
|
|
343
|
+
FileHandler.joinPaths(this.defaultTemplatesPath, 'layout.html'),
|
|
344
|
+
FileHandler.joinPaths(projectTemplatesPath, 'layout.html')
|
|
345
|
+
);
|
|
346
|
+
FileHandler.copyFile(
|
|
347
|
+
FileHandler.joinPaths(this.defaultTemplatesPath, 'css', 'styles.css'),
|
|
348
|
+
FileHandler.joinPaths(projectCssPath, 'styles.css')
|
|
349
|
+
);
|
|
350
|
+
FileHandler.copyFile(
|
|
351
|
+
FileHandler.joinPaths(this.defaultTemplatesPath, 'js', 'scripts.js'),
|
|
352
|
+
FileHandler.joinPaths(projectJsPath, 'scripts.js')
|
|
353
|
+
);
|
|
266
354
|
return true;
|
|
267
355
|
}
|
|
268
356
|
|