@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.
Files changed (47) hide show
  1. package/README.md +1 -1
  2. package/admin/assets/admin/filters.png +0 -0
  3. package/admin/assets/admin/list.png +0 -0
  4. package/admin/reldens-admin-client.css +830 -0
  5. package/admin/reldens-admin-client.js +272 -0
  6. package/admin/templates/dashboard.html +1 -0
  7. package/admin/templates/default-copyright.html +5 -0
  8. package/admin/templates/edit.html +25 -0
  9. package/admin/templates/fields/edit/button.html +3 -0
  10. package/admin/templates/fields/edit/checkbox.html +1 -0
  11. package/admin/templates/fields/edit/file.html +2 -0
  12. package/admin/templates/fields/edit/radio.html +1 -0
  13. package/admin/templates/fields/edit/select.html +5 -0
  14. package/admin/templates/fields/edit/text.html +1 -0
  15. package/admin/templates/fields/edit/textarea.html +1 -0
  16. package/admin/templates/fields/view/boolean.html +1 -0
  17. package/admin/templates/fields/view/image.html +4 -0
  18. package/admin/templates/fields/view/images.html +7 -0
  19. package/admin/templates/fields/view/link.html +1 -0
  20. package/admin/templates/fields/view/links.html +6 -0
  21. package/admin/templates/fields/view/text.html +1 -0
  22. package/admin/templates/layout.html +37 -0
  23. package/admin/templates/list-content.html +70 -0
  24. package/admin/templates/list.html +35 -0
  25. package/admin/templates/login.html +19 -0
  26. package/admin/templates/management.html +22 -0
  27. package/admin/templates/maps-wizard-maps-selection.html +85 -0
  28. package/admin/templates/maps-wizard.html +341 -0
  29. package/admin/templates/objects-import.html +143 -0
  30. package/admin/templates/pagination-link.html +1 -0
  31. package/admin/templates/sidebar-header.html +4 -0
  32. package/admin/templates/sidebar-item.html +3 -0
  33. package/admin/templates/sidebar.html +11 -0
  34. package/admin/templates/skills-import.html +201 -0
  35. package/admin/templates/view.html +23 -0
  36. package/bin/reldens-cms.js +20 -8
  37. package/index.js +2 -2
  38. package/lib/entities-loader.js +45 -0
  39. package/lib/{storefront.js → frontend.js} +10 -6
  40. package/lib/installer.js +133 -45
  41. package/lib/manager.js +65 -26
  42. package/migrations/default-user.sql +2 -1
  43. package/package.json +2 -2
  44. package/templates/.env.dist +11 -11
  45. package/templates/css/styles.css +1 -1
  46. package/templates/index.js.dist +32 -0
  47. 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>
@@ -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
- projectRoot
16
- });
15
+ let manager = new Manager({projectRoot});
16
+ Logger.debug('Reldens CMS Manager instance created.', {configuration: manager.config});
17
17
 
18
- manager.start().catch((error) => {
19
- console.error('Failed to start CMS:', error);
20
- process.exit(1);
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 { Storefront } = require('./lib/storefront');
9
+ const { Frontend } = require('./lib/frontend');
10
10
 
11
11
  module.exports = {
12
12
  Manager,
13
13
  Installer,
14
- Storefront
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 - Storefront
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 Storefront
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.app.use(this.app._router.constructor.static(this.publicPath));
52
- return true;
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.Storefront = Storefront;
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
- if('' === req._parsedUrl.pathname || '/' === req._parsedUrl.pathname){
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 adminPath = templateVariables['app-admin-path'];
156
- return res.redirect(adminPath);
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'] || 'http://localhost',
217
- port: templateVariables['app-port'] || '8000'
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
- 'Installation completed on '+new Date().toISOString());
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
- if(!FileHandler.exists(projectTemplatesPath)){
232
- FileHandler.createFolder(projectTemplatesPath);
233
- }
327
+ FileHandler.createFolder(projectTemplatesPath);
234
328
  let projectPublicPath = FileHandler.joinPaths(this.projectRoot, 'public');
235
- if(!FileHandler.exists(projectPublicPath)){
236
- FileHandler.createFolder(projectPublicPath);
237
- }
329
+ FileHandler.createFolder(projectPublicPath);
238
330
  let projectCssPath = FileHandler.joinPaths(projectPublicPath, 'css');
239
- if(!FileHandler.exists(projectCssPath)){
240
- FileHandler.createFolder(projectCssPath);
241
- }
331
+ FileHandler.createFolder(projectCssPath);
242
332
  let projectJsPath = FileHandler.joinPaths(projectPublicPath, 'js');
243
- if(!FileHandler.exists(projectJsPath)){
244
- FileHandler.createFolder(projectJsPath);
245
- }
246
- let defaultPagePath = FileHandler.joinPaths(this.defaultTemplatesPath, 'page.html');
247
- let defaultNotFoundPath = FileHandler.joinPaths(this.defaultTemplatesPath, '404.html');
248
- let defaultLayoutPath = FileHandler.joinPaths(this.defaultTemplatesPath, 'layout.html');
249
- if(FileHandler.exists(defaultPagePath)){
250
- FileHandler.copyFile(defaultPagePath, FileHandler.joinPaths(projectTemplatesPath, 'page.html'));
251
- }
252
- if(FileHandler.exists(defaultNotFoundPath)){
253
- FileHandler.copyFile(defaultNotFoundPath, FileHandler.joinPaths(projectTemplatesPath, '404.html'));
254
- }
255
- if(FileHandler.exists(defaultLayoutPath)){
256
- FileHandler.copyFile(defaultLayoutPath, FileHandler.joinPaths(projectTemplatesPath, 'layout.html'));
257
- }
258
- let defaultCssPath = FileHandler.joinPaths(this.defaultTemplatesPath, 'css', 'styles.css');
259
- let defaultJsPath = FileHandler.joinPaths(this.defaultTemplatesPath, 'js', 'scripts.js');
260
- if(FileHandler.exists(defaultCssPath)){
261
- FileHandler.copyFile(defaultCssPath, FileHandler.joinPaths(projectCssPath, 'styles.css'));
262
- }
263
- if(FileHandler.exists(defaultJsPath)){
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