@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 +117 -13
- package/bin/reldens-cms-generate-entities.js +7 -2
- package/bin/reldens-cms.js +154 -50
- package/install/css/installer.css +1 -0
- package/install/index.html +128 -124
- package/lib/frontend.js +274 -268
- package/lib/installer.js +636 -497
- package/lib/manager.js +540 -507
- package/lib/mysql-installer.js +93 -0
- package/lib/prisma-subprocess-worker.js +97 -0
- package/lib/template-engine/asset-transformer.js +43 -41
- package/lib/template-engine/date-transformer.js +4 -5
- package/lib/template-engine/system-variables-provider.js +127 -108
- package/lib/template-engine/url-transformer.js +41 -41
- package/lib/template-engine.js +223 -213
- package/migrations/default-homepage.sql +1 -1
- package/package.json +5 -5
- package/templates/.env.dist +1 -0
- package/templates/index.js.dist +30 -32
- package/templates/js/cookie-consent.js +411 -0
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
|
|
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
|
-
- **
|
|
13
|
-
- **
|
|
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
|
-
|
|
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
|
-
│
|
|
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(
|
|
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)){
|
package/bin/reldens-cms.js
CHANGED
|
@@ -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
|
-
|
|
14
|
-
|
|
15
|
-
let
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
'
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
+
});
|