@reldens/cms 0.27.0 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,15 +2,17 @@
2
2
 
3
3
  # Reldens CMS
4
4
 
5
- A powerful, flexible Content Management System built with Node.js, featuring an admin panel, multi-domain frontend support, enhanced templating with reusable content blocks, system variables, internationalization, template reloading, dynamic forms, and automated installation.
5
+ A powerful, flexible Content Management System built with Node.js, featuring an admin panel, multi-domain frontend support, enhanced templating with reusable content blocks, system variables, internationalization, template reloading, dynamic forms, and automated installation with subprocess handling.
6
6
 
7
7
  ## Features
8
8
 
9
9
  ### - Quick Setup
10
- - **Web-based installer** with a guided setup process
10
+ - **Web-based installer** with guided setup process and subprocess management
11
+ - **Subprocess installation handling** for complex operations with progress tracking
11
12
  - **Automatic database schema creation** and seeding
12
- - **Environment configuration generation**
13
- - **Directory structure initialization**
13
+ - **Intelligent dependency management** with npm integration
14
+ - **Environment configuration generation** with template validation
15
+ - **Directory structure initialization** with asset copying
14
16
 
15
17
  ### - Frontend Engine
16
18
  - **Multi-domain support** with domain-specific templates and partials
@@ -65,6 +67,11 @@ A powerful, flexible Content Management System built with Node.js, featuring an
65
67
  ### Core Classes
66
68
  The CMS uses a modular architecture with specialized classes:
67
69
 
70
+ **Installation & Management:**
71
+ - `Installer` - Installation orchestrator with subprocess management and progress tracking
72
+ - `Manager` - Main CMS orchestrator with service initialization and configuration management
73
+ - `MySQLInstaller` - Database-specific installation with schema migration support
74
+
68
75
  **Frontend Orchestrator:**
69
76
  - `Frontend` - Main orchestrator class that coordinates all frontend operations
70
77
 
@@ -102,11 +109,23 @@ This architecture follows SOLID principles, providing better:
102
109
 
103
110
  ## Installation
104
111
 
105
- ### Method 1: Automated Web Installer
112
+ Install the CMS package and its dependencies:
113
+ ```bash
114
+ npm install @reldens/cms
115
+ ```
116
+
117
+ Then use the web installer or create the files manually.
118
+
119
+ ### Method 1: Automated Web Installer with Subprocess Handling
106
120
  ```bash
107
121
  npx reldens-cms
108
122
  ```
109
- Navigate to `http://localhost:8080` and follow the installation wizard.
123
+ Navigate to `http://localhost:8080` and follow the installation wizard with:
124
+ - **Automatic dependency checking** and installation
125
+ - **Subprocess progress tracking** for complex operations
126
+ - **Database connection validation** with real-time feedback
127
+ - **SSL certificate configuration** for production
128
+ - **Post-installation callbacks** for custom initialization
110
129
 
111
130
  ### Method 2: Manual Setup
112
131
  ```javascript
@@ -118,7 +137,12 @@ const cms = new Manager({
118
137
  cmsPages: { public: true, operations: ['read'] },
119
138
  articles: { public: true, operations: ['read'] },
120
139
  users: { public: false }
121
- }
140
+ },
141
+ // Enhanced manager configuration
142
+ authenticationMethod: 'db-users', // or 'custom'
143
+ adminRoleId: 99,
144
+ cache: true,
145
+ reloadTime: 0 // Disable template reloading for production
122
146
  });
123
147
 
124
148
  cms.start();
@@ -203,6 +227,71 @@ const cms = new Manager({
203
227
  });
204
228
  ```
205
229
 
230
+ ## Advanced Installation Features
231
+
232
+ ### Subprocess Installation Handling
233
+ The installer now supports complex operations through subprocess management:
234
+
235
+ ```javascript
236
+ const { Installer } = require('@reldens/cms');
237
+
238
+ const installer = new Installer({
239
+ projectRoot: process.cwd(),
240
+ subprocessMaxAttempts: 1800, // 3 minutes timeout
241
+ postInstallCallback: async (props) => {
242
+ // Custom initialization after installation
243
+ console.log('Entities loaded:', Object.keys(props.loadedEntities.rawRegisteredEntities));
244
+ return true;
245
+ }
246
+ });
247
+
248
+ // The installer automatically handles:
249
+ // - Package dependency checking and installation
250
+ // - Database schema creation via subprocess
251
+ // - Prisma client generation with progress tracking
252
+ // - Entity generation with validation
253
+ // - Environment file creation
254
+ // - Directory structure setup
255
+ ```
256
+
257
+ ### Enhanced Manager Initialization
258
+ The Manager class now provides comprehensive service initialization:
259
+
260
+ ```javascript
261
+ const cms = new Manager({
262
+ // Server validation - automatically validates provided instances
263
+ app: customExpressApp, // Optional: provide your own Express app
264
+ appServer: customAppServer, // Optional: provide your own HTTP server
265
+ dataServer: customDataServer, // Optional: provide your own database driver
266
+ adminManager: customAdmin, // Optional: provide your own admin manager
267
+ frontend: customFrontend, // Optional: provide your own frontend handler
268
+
269
+ // Configuration validation
270
+ adminRoleId: 99, // Admin role ID for authentication
271
+ authenticationMethod: 'db-users', // or 'custom'
272
+ authenticationCallback: async (email, password, roleId) => {
273
+ // Custom authentication logic
274
+ return await yourAuthService.validate(email, password, roleId);
275
+ },
276
+
277
+ // Performance configuration
278
+ cache: true, // Enable caching
279
+ reloadTime: -1, // Template reloading for development
280
+
281
+ // Multi-domain configuration
282
+ defaultDomain: 'example.com',
283
+ domainMapping: {'dev.example.com': 'development'},
284
+ siteKeyMapping: {'example.com': 'main'}
285
+ });
286
+
287
+ // Manager automatically:
288
+ // - Validates all provided instances
289
+ // - Initializes missing services
290
+ // - Sets up entity access control
291
+ // - Generates admin entities
292
+ // - Configures template reloading
293
+ ```
294
+
206
295
  ## Dynamic Forms System
207
296
 
208
297
  ### Basic Form Usage
@@ -1206,6 +1295,22 @@ The installer provides checkboxes for:
1206
1295
  - `start()` - Initialize and start the CMS
1207
1296
  - `isInstalled()` - Check if CMS is installed
1208
1297
  - `initializeServices()` - Initialize all services
1298
+ - `validateProvidedServer()` - Validate provided server instance
1299
+ - `validateProvidedDataServer()` - Validate provided data server
1300
+ - `validateProvidedAdminManager()` - Validate provided admin manager
1301
+ - `validateProvidedFrontend()` - Validate provided frontend
1302
+ - `buildAppServerConfiguration()` - Build server configuration
1303
+ - `initializeCmsAfterInstall(props)` - Post-installation callback
1304
+
1305
+ ### Installer Class
1306
+ - `isInstalled()` - Check installation status
1307
+ - `configureAppServerRoutes(app, appServer, appServerFactory, renderEngine)` - Setup installer routes
1308
+ - `executeInstallProcess(req, res)` - Complete installation process
1309
+ - `runSubprocessInstallation(dbConfig, templateVariables)` - Handle subprocess operations
1310
+ - `checkAndInstallPackages(requiredPackages)` - Check and install dependencies
1311
+ - `generateEntities(server, isOverride, isInstallationMode, isDryPrisma, dbConfig)` - Generate entities
1312
+ - `createEnvFile(templateVariables)` - Create environment configuration
1313
+ - `copyAdminDirectory()` - Copy admin assets and templates
1209
1314
 
1210
1315
  ### Frontend Architecture Classes
1211
1316
 
@@ -1312,11 +1417,6 @@ The installer provides checkboxes for:
1312
1417
  - `generateEditRouteContent()` - Entity edit forms
1313
1418
  - `processSaveEntity()` - Handle form submissions
1314
1419
 
1315
- ### Installer Class
1316
- - `prepareSetup()` - Setup installation routes
1317
- - `executeInstallProcess()` - Run installation
1318
- - `generateEntities()` - Create entity files
1319
-
1320
1420
  ## File Structure
1321
1421
 
1322
1422
  ```
@@ -1341,7 +1441,11 @@ project/
1341
1441
  │ ├── dynamic-form.js # Forms validation and processing
1342
1442
  │ ├── dynamic-form-renderer.js # Forms template rendering
1343
1443
  │ ├── dynamic-form-request-handler.js # Forms request handling
1344
- └── template-engine.js # Core template processing
1444
+ ├── template-engine.js # Core template processing
1445
+ │ ├── installer.js # Installation with subprocess handling
1446
+ │ ├── manager.js # Main CMS orchestrator
1447
+ │ ├── mysql-installer.js # MySQL-specific installation
1448
+ │ └── prisma-subprocess-worker.js # Subprocess worker
1345
1449
  ├── templates/
1346
1450
  │ ├── layouts/ # Body content layouts
1347
1451
  │ ├── domains/ # Domain-specific templates
@@ -120,7 +120,12 @@ class CmsEntitiesGenerator
120
120
  }
121
121
  Logger.debug('Reldens CMS Manager instance created for entities generation.');
122
122
  await manager.initializeDataServer();
123
- let success = await manager.installer.generateEntities(manager.dataServer, this.isOverride, false, this.isDryPrisma);
123
+ let success = await manager.installer.generateEntities(
124
+ manager.dataServer,
125
+ this.isOverride,
126
+ false,
127
+ this.isDryPrisma
128
+ );
124
129
  if(!success){
125
130
  Logger.error('Entities generation failed.');
126
131
  return false;
@@ -135,7 +140,7 @@ class CmsEntitiesGenerator
135
140
  if(!clientPath){
136
141
  return false;
137
142
  }
138
- let resolvedPath = clientPath.startsWith('./')
143
+ let resolvedPath = clientPath.startsWith('./')
139
144
  ? FileHandler.joinPaths(process.cwd(), clientPath.substring(2))
140
145
  : clientPath;
141
146
  if(!FileHandler.exists(resolvedPath)){
@@ -1,51 +1,155 @@
1
1
  #!/usr/bin/env node
2
-
3
- /**
4
- *
5
- * Reldens - CMS - CLI
6
- *
7
- */
8
-
9
- const { Manager } = require('../index');
10
- const { Logger } = require('@reldens/utils');
11
- const { FileHandler } = require('@reldens/server-utils');
12
-
13
- let args = process.argv.slice(2);
14
- let projectRoot = args[0] || process.cwd();
15
- let indexPath = FileHandler.joinPaths(projectRoot, 'index.js');
16
-
17
- if(FileHandler.exists(indexPath)){
18
- require(indexPath);
19
- return;
20
- }
21
-
22
- let managerConfig = {projectRoot};
23
- let entitiesPath = FileHandler.joinPaths(
24
- projectRoot,
25
- 'generated-entities',
26
- 'models',
27
- 'prisma',
28
- 'registered-models-prisma.js'
29
- );
30
-
31
- if(FileHandler.exists(entitiesPath)){
32
- let entitiesModule = require(entitiesPath);
33
- managerConfig.rawRegisteredEntities = entitiesModule.rawRegisteredEntities;
34
- managerConfig.entitiesConfig = entitiesModule.entitiesConfig;
35
- managerConfig.entitiesTranslations = entitiesModule.entitiesTranslations;
36
- }
37
-
38
- let manager = new Manager(managerConfig);
39
- Logger.debug('Reldens CMS Manager instance created.', {configuration: manager.config});
40
-
41
- manager.start().then((result) => {
42
- if(!result){
43
- Logger.info('Reldens CMS started by command failed.');
44
- return false;
45
- }
46
- Logger.info('Reldens CMS started by command.');
47
- return true;
48
- }).catch((error) => {
49
- Logger.critical('Failed to start CMS:', error);
50
- process.exit();
51
- });
2
+
3
+ /**
4
+ *
5
+ * Reldens - CMS - CLI
6
+ *
7
+ */
8
+
9
+ const { Manager } = require('../index');
10
+ const { Logger } = require('@reldens/utils');
11
+ const { FileHandler } = require('@reldens/server-utils');
12
+ const readline = require('readline');
13
+ const dotenv = require('dotenv');
14
+
15
+ let args = process.argv.slice(2);
16
+ let projectRoot = args[0] || process.cwd();
17
+ let indexPath = FileHandler.joinPaths(projectRoot, 'index.js');
18
+
19
+ if(FileHandler.exists(indexPath)){
20
+ require(indexPath);
21
+ return;
22
+ }
23
+
24
+ async function checkRequiredPackages(projectRoot)
25
+ {
26
+ let requiredPackages = ['@reldens/cms'];
27
+ let missingPackages = [];
28
+ for(let packageName of requiredPackages){
29
+ let packagePath = FileHandler.joinPaths(projectRoot, 'node_modules', packageName);
30
+ if(!FileHandler.exists(packagePath)){
31
+ missingPackages.push(packageName);
32
+ }
33
+ }
34
+ let packagePath = FileHandler.joinPaths(projectRoot, 'node_modules', '@prisma/client');
35
+ if(!FileHandler.exists(packagePath)){
36
+ missingPackages.push('@prisma/client');
37
+ }
38
+ return missingPackages;
39
+ }
40
+
41
+ async function promptUserConfirmation(packages)
42
+ {
43
+ let rl = readline.createInterface({
44
+ input: process.stdin,
45
+ output: process.stdout
46
+ });
47
+ return new Promise((resolve) => {
48
+ Logger.info('Missing required packages: '+packages.join(', '));
49
+ Logger.info('These packages are required for the CMS to function properly.');
50
+ rl.question('Would you like to install them automatically? (y/N): ', (answer) => {
51
+ rl.close();
52
+ resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
53
+ });
54
+ });
55
+ }
56
+
57
+ async function installPackages(packages, projectRoot)
58
+ {
59
+ try {
60
+ Logger.info('Installing packages: npm install '+packages.join(' '));
61
+ let {execSync} = require('child_process');
62
+ let installCommand = 'npm install '+packages.join(' ');
63
+ execSync(installCommand, {stdio: 'inherit', cwd: projectRoot});
64
+ Logger.info('Dependencies installed successfully.');
65
+ return true;
66
+ } catch(error) {
67
+ Logger.error('Failed to install dependencies: '+error.message);
68
+ Logger.error('Please run manually: npm install '+packages.join(' '));
69
+ return false;
70
+ }
71
+ }
72
+
73
+ async function handlePackageInstallation(projectRoot)
74
+ {
75
+ let missingPackages = await checkRequiredPackages(projectRoot);
76
+ if(0 === missingPackages.length){
77
+ return true;
78
+ }
79
+ let userConfirmed = await promptUserConfirmation(missingPackages);
80
+ if(!userConfirmed){
81
+ Logger.info('Installation cancelled. Please install required packages manually and try again.');
82
+ return false;
83
+ }
84
+ return await installPackages(missingPackages, projectRoot);
85
+ }
86
+
87
+ async function createPrismaClientIfNeeded(projectRoot)
88
+ {
89
+ let envFilePath = FileHandler.joinPaths(projectRoot, '.env');
90
+ dotenv.config({path: envFilePath});
91
+ let storageDriver = process.env.RELDENS_STORAGE_DRIVER || 'prisma';
92
+ if('prisma' !== storageDriver){
93
+ return false;
94
+ }
95
+ let clientPath = FileHandler.joinPaths(projectRoot, 'prisma', 'client');
96
+ if(!FileHandler.exists(clientPath)){
97
+ Logger.critical('Prisma client not found at: '+clientPath);
98
+ Logger.critical('Please run "npx prisma generate" to generate the Prisma client.');
99
+ return false;
100
+ }
101
+ try {
102
+ let { PrismaClient } = require(clientPath);
103
+ return new PrismaClient();
104
+ } catch(error) {
105
+ Logger.critical('Failed to initialize Prisma client: '+error.message);
106
+ return false;
107
+ }
108
+ }
109
+
110
+ async function startManagerProcess()
111
+ {
112
+ let packageInstallResult = await handlePackageInstallation(projectRoot);
113
+ if(!packageInstallResult){
114
+ process.exit(1);
115
+ }
116
+
117
+ let managerConfig = {projectRoot};
118
+ let entitiesPath = FileHandler.joinPaths(
119
+ projectRoot,
120
+ 'generated-entities',
121
+ 'models',
122
+ 'prisma',
123
+ 'registered-models-prisma.js'
124
+ );
125
+
126
+ if(FileHandler.exists(entitiesPath)){
127
+ let entitiesModule = require(entitiesPath);
128
+ managerConfig.rawRegisteredEntities = entitiesModule.rawRegisteredEntities;
129
+ managerConfig.entitiesConfig = entitiesModule.entitiesConfig;
130
+ managerConfig.entitiesTranslations = entitiesModule.entitiesTranslations;
131
+ }
132
+
133
+ let prismaClient = await createPrismaClientIfNeeded(projectRoot);
134
+ if(prismaClient){
135
+ managerConfig.prismaClient = prismaClient;
136
+ }
137
+ let manager = new Manager(managerConfig);
138
+ Logger.debug('Reldens CMS Manager instance created.', {configuration: manager.config});
139
+ manager.start().then((result) => {
140
+ if(!result){
141
+ Logger.info('Reldens CMS started by command failed.');
142
+ return false;
143
+ }
144
+ Logger.info('Reldens CMS started by command.');
145
+ return true;
146
+ }).catch((error) => {
147
+ Logger.critical('Failed to start CMS:', error);
148
+ process.exit();
149
+ });
150
+ }
151
+
152
+ startManagerProcess().catch((error) => {
153
+ Logger.critical('Failed to handle package installation:', error);
154
+ process.exit(1);
155
+ });
@@ -17,6 +17,7 @@ body {
17
17
  .header {
18
18
  text-align: center;
19
19
  margin-bottom: 30px;
20
+ font-family: "Play", sans-serif;
20
21
  }
21
22
 
22
23
  .title {