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, and automated installation.
- Web-based installer with a guided setup process
- Automatic database schema creation and seeding
- Environment configuration generation
- Directory structure initialization
- Multi-domain support with domain-specific templates and partials
- Dynamic routing from database-driven routes
-
Entity-based URLs (e.g.,
/articles/123
) - Template fallback system (domain → default → base)
- Layout system with body content layouts and page wrappers
-
Reusable content blocks with
<entity>
template functions - Collection rendering with filtering, sorting, pagination, and loop support
- Custom partial tags with HTML-style syntax for complex partials
- Entity access control for public/private content
- Static asset serving with Express integration as default
- Template engine with Mustache integration as default
- System variables for request, route, and domain context
- Enhanced context passing with currentEntity data in child blocks
- Template functions for URLs, assets, dates, and translations
- Event-driven rendering with hooks for customization
- Custom 404 handling
- Advanced search functionality with template data support
- Template reloading for development with configurable reload strategies
- Full CRUD operations for all entities including content blocks
- File upload handling with multiple storage buckets
- Role-based authentication and access control
- Advanced filtering and search across entity properties
- Bulk operations (delete multiple records)
- Relationship management with foreign key support
- Template-driven UI with customizable admin themes
- Multiple database drivers (Prisma by default, others via DriversMap)
- Automatic entity generation from a database schema
- Relationship mapping and foreign key handling
- Custom entity configuration with validation rules
- Translation support for entity labels and properties
- Content blocks management via cms_blocks table
- Entity access control via entities_access table
- Environment-based configuration (.env file)
- Modular service architecture with specialized classes for better maintainability
- Event-driven system with hooks for customization
- Extensible authentication (database users or custom callbacks)
- File security with path validation and dangerous key filtering
- Internationalization support with translation files
The CMS uses a modular architecture with specialized classes:
Frontend Orchestrator:
-
Frontend
- Main orchestrator class that coordinates all frontend operations
Template Management:
-
TemplateResolver
- Template discovery and domain resolution -
TemplateCache
- Template and partial caching management -
TemplateReloader
- Template reloading with file change detection
Request Processing:
-
RequestProcessor
- HTTP request routing and path handling -
SearchRequestHandler
- Dedicated search request processing
Content Management:
-
ContentRenderer
- Content generation and template processing -
EntityAccessManager
- Entity access control and loading
Response Handling:
-
ResponseManager
- HTTP response handling and caching logic
Template Processing:
-
TemplateEngine
- Core template rendering with enhanced context -
SystemVariablesProvider
- System variables for templates
This architecture follows SOLID principles, providing better:
- Testability - Individual components can be tested in isolation
- Maintainability - Changes to one area don't affect others
- Reusability - Components can be reused in different contexts
- Readability - Smaller, focused classes are easier to understand
npx reldens-cms
Navigate to http://localhost:8080
and follow the installation wizard.
const { Manager } = require('@reldens/cms');
const cms = new Manager({
projectRoot: process.cwd(),
entityAccess: {
cmsPages: { public: true, operations: ['read'] },
articles: { public: true, operations: ['read'] },
users: { public: false }
}
});
cms.start();
RELDENS_APP_HOST=http://localhost
RELDENS_APP_PORT=8080
RELDENS_ADMIN_ROUTE_PATH=/admin
RELDENS_ADMIN_SECRET=your-secret-key
RELDENS_DB_CLIENT=mysql
RELDENS_DB_HOST=localhost
RELDENS_DB_PORT=3306
RELDENS_DB_NAME=cms_db
RELDENS_DB_USER=username
RELDENS_DB_PASSWORD=password
RELDENS_STORAGE_DRIVER=prisma
RELDENS_DEFAULT_DOMAIN=example.com
RELDENS_DOMAIN_MAPPING={"dev.example.com":"development"}
RELDENS_SITE_KEY_MAPPING={"example.com":"main"}
Configure template reloading for development environments:
const cms = new Manager({
// Development: reload templates on every request when changes detected
reloadTime: -1,
// Production: disable template reloading (default)
reloadTime: 0,
// Interval-based: reload every 5 seconds when changes detected
reloadTime: 5000
});
Template Reloading Options:
-
reloadTime: 0
(default) - Template reloading disabled. Templates load once at startup. -
reloadTime: -1
- Reload templates on every request when file changes are detected. Best for active development. -
reloadTime: > 0
- Check for template changes at specified interval (milliseconds) and reload when needed. Good for development with lower overhead.
How it works:
- Tracks file modification times for admin and frontend templates
- Only reloads templates that have actually changed
- Automatically updates admin contents and frontend template cache
- Works with both admin panel templates and frontend templates/partials
- Zero performance impact when disabled (
reloadTime: 0
)
const entityConfig = {
articles: {
listProperties: ['title', 'status', 'created_at'],
showProperties: ['title', 'content', 'author', 'status'],
editProperties: ['title', 'content', 'author_id', 'status'],
filterProperties: ['status', 'author_id'],
titleProperty: 'title',
parentItemLabel: 'Content',
properties: {
title: { type: 'string', isRequired: true },
content: { type: 'text' },
author_id: { type: 'reference', reference: 'users' },
featured_image: {
type: 'string',
isUpload: true,
allowedTypes: 'image',
bucket: 'uploads'
}
}
}
};
const cms = new Manager({
entitiesConfig: entityConfig
});
# Simple search
/search?search=technology
# Entity-specific search with custom limit
/search?search=javascript&limit=20
# Custom template rendering
/search?search=news&renderPartial=newsListView&renderLayout=minimal
# Pass custom template variables
/search?search=articles&templateData[columnsClass]=col-md-4&templateData[showExcerpt]=true
# Multiple template variables
/search?search=technology&templateData[columnsClass]=col-lg-6&templateData[cardClass]=shadow-sm&templateData[showAuthor]=false
Templates receive dynamic data through URL parameters:
URL: /search?search=tech&templateData[columnsClass]=col-md-6&templateData[showDate]=true
Template (entriesListView.html):
<div class="{{columnsClass}}">
<div class="card">
<h3>{{row.title}}</h3>
<p>{{row.content}}</p>
{{#showDate}}
<span class="date">{{row.created_at}}</span>
{{/showDate}}
</div>
</div>
Default Values:
-
columnsClass
defaults tocol-lg-6
if not provided or empty - Custom variables can be added via
templateData[variableName]=value
// Custom search sets in Manager configuration
const searchSets = {
articlesSearch: {
entities: [{
name: 'articles',
fields: ['title', 'content', 'summary'],
relations: 'authors'
}],
pagination: {active: true, limit: 15, sortBy: 'created_at', sortDirection: 'desc'}
}
};
const cms = new Manager({
searchSets: searchSets
});
Every template has access to system variables providing context about the current request:
<!-- Current request information -->
{{currentRequest.baseUrl}} <!-- https://example.com -->
{{currentRequest.protocol}} <!-- https -->
{{currentRequest.host}} <!-- example.com -->
{{currentRequest.path}} <!-- /articles/123 -->
{{currentRequest.method}} <!-- GET -->
{{currentRequest.userAgent}} <!-- Browser information -->
{{currentRequest.isSecure}} <!-- true/false -->
<!-- Current route data (if matched) -->
{{currentRoute.id}} <!-- Route ID -->
{{currentRoute.path}} <!-- Route path pattern -->
{{currentRoute.title}} <!-- Route title -->
{{currentRoute.template}} <!-- Route template -->
{{currentRoute.layout}} <!-- Route layout -->
<!-- Current domain information -->
{{currentDomain.current}} <!-- Current domain -->
{{currentDomain.default}} <!-- Default domain -->
{{currentDomain.resolved}} <!-- Resolved domain -->
<!-- System information -->
{{systemInfo.environment}} <!-- development/production -->
{{systemInfo.nodeVersion}} <!-- Node.js version -->
{{systemInfo.timestamp}} <!-- Current timestamp -->
Templates support dynamic functions for common operations:
<!-- URL generation with current domain -->
[url(/articles)] <!-- https://example.com/articles -->
[url(/contact#form)] <!-- https://example.com/contact#form -->
[url(/css/styles.css)] <!-- https://example.com/css/styles.css -->
<!-- Asset URLs with domain -->
[asset(/assets/images/logo.png)] <!-- https://example.com/images/logo.png -->
<!-- Date formatting -->
[date()] <!-- Current date with default format -->
[date(now, Y-m-d)] <!-- 2024-01-15 -->
[date(2024-01-01, d/m/Y)] <!-- 01/01/2024 -->
<!-- Internationalization -->
[translate(welcome.message)] <!-- Translated text -->
[t(hello.world, Hello World!)] <!-- With fallback -->
[t(greeting, Hi {name}!, {name: John})] <!-- With interpolation -->
Child content blocks and partials receive context from parent pages:
<!-- In a CMS page or layout -->
<entity name="cmsBlocks" field="name" value="article-sidebar"/>
<!-- Inside the article-sidebar block, you can access: -->
{{currentEntity.title}} <!-- Parent page title -->
{{currentEntity.id}} <!-- Parent page ID -->
{{currentEntity.template}} <!-- Parent page template -->
<!-- Any other parent page properties -->
Templates support dynamic content blocks, entity rendering, and collections with advanced query options:
Single Entity Rendering:
<!-- Render by any identifier field (like 'name' for content blocks) -->
<entity name="cmsBlocks" field="name" value="header-main"/>
<entity name="cmsBlocks" field="name" value="sidebar-left"/>
<!-- Render by ID (default identifier) -->
<entity name="articles" id="123"/>
<entity name="cmsPages" id="1"/>
Single Field Collections:
<!-- Extract and concatenate a single field from multiple records -->
<collection name="cmsBlocks" filters="{status: 'active', category: 'navigation'}" field="content"/>
<collection name="articles" filters="{featured: true}" field="title"/>
<!-- With query options for sorting and limiting -->
<collection name="articles" filters="{featured: true}" field="title" data="{limit: 5, sortBy: 'created_at', sortDirection: 'desc'}"/>
<collection name="cmsBlocks" filters="{status: 'active'}" field="content" data="{limit: 3, offset: 10, sortBy: 'priority'}"/>
Loop Collections:
<!-- Loop through records with full template rendering -->
<collection name="cmsBlocks" filters="{status: 'active'}">
<div class="block">
<h3>{{row.title}}</h3>
<div class="content">{{row.content}}</div>
</div>
</collection>
<collection name="articles" filters="{category: 'technology'}">
<div class="article">
<h4>{{row.title}}</h4>
<p>{{row.summary}}</p>
<img src="{{row.featured_image}}" alt="{{row.title}}">
</div>
</collection>
<!-- With pagination and sorting -->
<collection name="articles" filters="{featured: true}" data="{limit: 10, offset: 0, sortBy: 'created_at', sortDirection: 'asc'}">
<div class="article-card">
<h4>{{row.title}}</h4>
<p>{{row.summary}}</p>
</div>
</collection>
<!-- Paginated collections with navigation -->
<collection name="articles"
filters="{featured: true}"
data="{limit: 10, sortBy: 'created_at', sortDirection: 'desc'}"
pagination="articles-1"
container="pagedCollection"
prevPages="2"
nextPages="2">
<div class="article-card">
<h4>{{row.title}}</h4>
<p>{{row.summary}}</p>
<span class="date">{{row.created_at}}</span>
</div>
</collection>
<!-- Multiple paginated collections on the same page -->
<collection name="news"
filters="{category: 'technology'}"
data="{limit: 5, sortBy: 'published_at'}"
pagination="news-tech"
container="customPager">
<article>{{row.title}}</article>
</collection>
<collection name="events"
filters="{upcoming: true}"
data="{limit: 8}"
pagination="events-upcoming"
prevPages="3"
nextPages="1">
<div class="event">{{row.title}} - {{row.date}}</div>
</collection>
Pagination Attributes:
-
pagination="collection-id"
- Enables pagination with unique identifier -
container="templateName"
- Custom pagination template (defaults to "pagedCollection") -
prevPages="2"
- Number of previous page links to show (default: 2) -
nextPages="2"
- Number of next page links to show (default: 2)
Pagination URL Parameters: Pagination state is managed via URL query parameters:
/articles?articles-1-key={"page":2,"limit":10,"sortBy":"created_at","sortDirection":"desc"}
/news?news-tech-key={"page":3,"limit":5,"category":"technology"}
Custom Pagination Template:
Create templates/partials/pagedCollection.html
:
<div class="row paginated-contents">
<div class="collection-content col-lg-12">
{{&collectionContentForCurrentPage}}
</div>
<div class="pagination col-lg-12">
<ul class="pagination-list">
{{#prevPageUrl}}
<li><a href="{{prevPageUrl}}" class="page-link">{{&prevPageLabel}}</a></li>
{{/prevPageUrl}}
{{#prevPages}}
<li><a href="{{pageUrl}}" class="page-link">{{&pageLabel}}</a></li>
{{/prevPages}}
<li class="current">{{¤tPage}}</li>
{{#nextPages}}
<li><a href="{{pageUrl}}" class="page-link">{{&pageLabel}}</a></li>
{{/nextPages}}
{{#nextPageUrl}}
<li><a href="{{nextPageUrl}}" class="page-link">{{&nextPageLabel}}</a></li>
{{/nextPageUrl}}
</ul>
</div>
</div>
Available Pagination Template Variables:
-
{{&collectionContentForCurrentPage}}
- Rendered collection items for current page -
{{currentPage}}
- Current page number -
{{totalPages}}
- Total number of pages -
{{totalRecords}}
- Total number of records -
{{prevPageUrl}}
/{{nextPageUrl}}
- Previous/next page URLs -
{{&prevPageLabel}}
/{{&nextPageLabel}}
- Previous/next link labels ("Previous"/"Next") -
{{#prevPages}}
/{{#nextPages}}
- Arrays of page objects withpageUrl
andpageLabel
-
{{hasNextPage}}
/{{hasPrevPage}}
- Boolean flags for navigation availability
Custom Partials with Variables:
New HTML-style partial tags:
<!-- Clean HTML-style syntax for complex partials -->
<partial name="hero"
sectionStyle=" bg-black"
bigTextHtml="A free open-source platform to create multiplayer games!"
mediumTextHtml="Build with Node.js, MySQL, Colyseus, and Phaser 3"
htmlContentWrapper='<div class="d-lg-flex"><a href="/documentation" target="_blank" class="btn-get-started">Get Started!</a><a href="https://demo.reldens.com/" target="_blank" class="btn-watch-video"> Demo </a></div>'
imageUrl="/assets/web/reldens-check.png"
imageAlt="Reldens - MMORPG Platform" />
<!-- Self-closing and open/close syntax both supported -->
<partial name="productCard"
title="Premium Package"
price="$99"
highlighted="true">
</partial>
Traditional Mustache syntax is still supported:
<!-- Call a partial with an inline JSON object -->
{{>hero -{
bigTextHtml: "A free open-source platform to create multiplayer games!",
mediumTextHtml: "Build with Node.js, MySQL, Colyseus, and Phaser 3",
imageUrl: "https://example.com/hero.jpg",
ctaText: "Get Started",
ctaLink: "/documentation"
}-}}
<!-- Call a partial within collections using row data -->
<collection name="cmsPages" filters="{featured: true}" data="{limit: 3}">
{{>cardView -{row}-}}
</collection>
<!-- Call a partial with mixed data -->
{{>productCard -{
title: "Premium Package",
price: "$99",
features: ["Advanced Analytics", "Priority Support", "Custom Themes"],
highlighted: true
}-}}
Example Partial Templates:
partials/hero.mustache:
<section class="hero{{#sectionStyle}}{{sectionStyle}}{{/sectionStyle}}">
<div class="hero-content">
<h1>{{&bigTextHtml}}</h1>
<p>{{&mediumTextHtml}}</p>
{{#htmlContentWrapper}}
{{&htmlContentWrapper}}
{{/htmlContentWrapper}}
{{#imageUrl}}
<img src="{{imageUrl}}" alt="{{imageAlt}}" class="hero-image">
{{/imageUrl}}
</div>
</section>
partials/cardView.mustache:
<div class="card">
<h3>{{title}}</h3>
<p>{{json_data.excerpt}}</p>
{{#json_data.featured_image}}
<img src="{{json_data.featured_image}}" alt="{{title}}">
{{/json_data.featured_image}}
{{#json_data.cta_text}}
<a href="{{json_data.cta_link}}" class="btn">{{json_data.cta_text}}</a>
{{/json_data.cta_text}}
</div>
Collections support advanced query parameters for pagination and sorting:
- limit - Maximum number of records to return
- offset - Number of records to skip (for pagination)
- sortBy - Field name to sort by
- sortDirection - Sort direction ('asc' or 'desc')
Examples:
<!-- Get the first 5 articles sorted by title -->
<collection name="articles" filters="{}" field="title" data="{limit: 5, sortBy: 'title'}"/>
<!-- Paginated results: skip first 20, get next 10 -->
<collection name="articles" filters="{published: true}" data="{limit: 10, offset: 20, sortBy: 'created_at', sortDirection: 'desc'}">
<article>{{row.title}}</article>
</collection>
<!-- The latest 3 featured articles by creation date -->
<collection name="articles" filters="{featured: true}" data="{limit: 3, sortBy: 'created_at', sortDirection: 'desc'}">
<div class="featured-article">{{row.title}}</div>
</collection>
Create translation files in the translations
directory:
translations/en.json:
{
"navigation": {
"home": "Home",
"about": "About Us",
"contact": "Contact"
},
"messages": {
"welcome": "Welcome to our site!",
"greeting": "Hello {name}!"
}
}
translations/es.json:
{
"navigation": {
"home": "Inicio",
"about": "Acerca de",
"contact": "Contacto"
},
"messages": {
"welcome": "¡Bienvenido a nuestro sitio!",
"greeting": "¡Hola {name}!"
}
}
<!-- Simple translation -->
[translate(navigation.home)]
<!-- With fallback -->
[t(navigation.home, Home)]
<!-- With interpolation -->
[t(messages.greeting, Hello!, {name: John})]
<!-- Locale detection from request headers or ?locale=es parameter -->
The CMS uses a two-tier layout system:
page.html - Full HTML wrapper:
<!DOCTYPE html>
<html lang="{{locale}}">
<head>
<title>{{title}}</title>
<meta name="description" content="{{description}}"/>
<link href="[url(/css/styles.css)]" rel="stylesheet"/>
</head>
<body class="{{siteHandle}}">
{{&content}}
<script src="[url(/js/scripts.js)]"></script>
</body>
</html>
layouts/default.html - Body content only:
<entity name="cmsBlocks" field="name" value="header-main"/>
<main id="main" class="main-container">
<div class="container">
<div class="row">
<div class="col-md-3">
<entity name="cmsBlocks" field="name" value="sidebar-left"/>
</div>
<div class="col-md-9">
{{&content}}
</div>
</div>
</div>
</main>
<entity name="cmsBlocks" field="name" value="footer-main"/>
Pages can use different layouts by setting the layout
field in cms_pages
:
-
default
- Header, sidebar, main content, footer -
full-width
- Full width without sidebars -
minimal
- Basic layout with minimal styling
Create reusable content blocks in the cms_blocks
table via the admin panel:
INSERT INTO cms_blocks (name, title, content) VALUES
('contact-info', 'Contact Information', '<p>Email: info@example.com</p>'),
('article-sidebar', 'Article Categories',
'<div class="categories"><h3>Categories</h3><ul><li><a href="[url(/articles/technology)]">Technology</a></li></ul></div>');
Control which entities are publicly accessible:
const cms = new Manager({
entityAccess: {
articles: { public: true, operations: ['read'] },
cmsPages: { public: true, operations: ['read'] },
users: { public: false }
}
});
templates/
├── layouts/
│ ├── default.html # Body content layouts
│ ├── full-width.html
│ └── minimal.html
├── domains/
│ ├── example.com/
│ │ ├── layouts/ # Domain-specific layouts
│ │ ├── partials/
│ │ │ ├── header.html
│ │ │ └── footer.html
│ │ ├── page.html # Domain-specific page wrapper
│ │ └── index.html
│ └── dev.example.com/
│ └── page.html
├── partials/
│ ├── header.html (default)
│ └── footer.html (default)
├── translations/
│ ├── en.json
│ ├── es.json
│ └── fr.json
├── page.html (base HTML wrapper)
└── 404.html
// Different configurations for development vs production
const isDevelopment = process.env.NODE_ENV === 'development';
const cms = new Manager({
// Enable aggressive template reloading in development
reloadTime: isDevelopment ? -1 : 0,
// Other development-friendly settings
cache: !isDevelopment,
entityAccess: {
articles: { public: true, operations: ['read'] },
cmsPages: { public: true, operations: ['read'] }
}
});
Development Workflow with Template Reloading:
- Set
reloadTime: -1
for instant template updates - Edit admin templates in
admin/templates/
- changes appear immediately - Edit frontend templates in
templates/
- changes appear on next page load - No server restart needed for template changes
- Switch to
reloadTime: 0
in production for optimal performance
The CMS provides hooks for customization through event listeners:
// Listen for template variable events
cms.events.on('reldens.afterVariablesCreated', (eventData) => {
// Add custom variables
eventData.variables.customData = {
timestamp: Date.now(),
version: '1.0.0'
};
});
// Listen for content processing events
cms.events.on('reldens.beforeContentProcess', (eventData) => {
// Modify content before processing
eventData.content = eventData.content.replace(/\[custom\]/g, 'Custom Value');
});
cms.events.on('reldens.afterContentProcess', (eventData) => {
// Modify processed content
eventData.processedContent += '\n<!-- Processed at ' + new Date() + ' -->';
});
// Listen for template reloading events
cms.events.on('reldens.templateReloader.templatesChanged', (eventData) => {
console.log('Templates changed:', eventData.changedFiles);
});
const customAuth = async (email, password, roleId) => {
const user = await yourAuthService.authenticate(email, password);
return user && user.role_id === roleId ? user : false;
};
const cms = new Manager({
authenticationMethod: 'custom',
authenticationCallback: customAuth
});
const uploadConfig = {
mimeTypes: {
image: ['image/jpeg', 'image/png', 'image/webp'],
document: ['application/pdf', 'text/plain']
},
allowedExtensions: {
image: ['.jpg', '.jpeg', '.png', '.webp'],
document: ['.pdf', '.txt']
}
};
const cms = new Manager(uploadConfig);
cms.events.on('reldens.setupAdminRoutes', ({adminManager}) => {
// Add custom admin routes
adminManager.adminRouter.get('/custom', (req, res) => {
res.send('Custom admin page');
});
});
cms.events.on('adminEntityExtraData', ({entitySerializedData, entity}) => {
// Add extra data to admin views
entitySerializedData.customField = 'Custom Value';
});
-
routes
- URL routing and SEO metadata -
cms_pages
- Page content with layout assignments -
cms_blocks
- Reusable content blocks -
entities_access
- Entity access control rules -
entities_meta
- Generic metadata storage -
cms_pages_meta
- Page-specific metadata
The installer provides checkboxes for:
- CMS core tables
- User authentication system
- Default admin user
- Default homepage
- Default content blocks
- Entity access control rules
-
start()
- Initialize and start the CMS -
isInstalled()
- Check if CMS is installed -
initializeServices()
- Initialize all services
-
initialize()
- Set up frontend routes and templates -
handleRequest(req, res)
- Main request handler -
renderRoute(route, domain, res, req)
- Route-based rendering -
setupStaticAssets()
- Configure static asset serving
-
findTemplatePath(templateName, domain)
- Template discovery with domain fallback -
findLayoutPath(layoutName, domain)
- Layout path resolution -
findTemplateByPath(path, domain)
- Template lookup by URL path -
resolveDomainToFolder(domain)
- Domain to folder mapping -
resolveDomainToSiteKey(domain)
- Domain to site key mapping
-
loadPartials()
- Load and cache template partials -
setupDomainTemplates()
- Initialize domain-specific templates -
getPartialsForDomain(domain)
- Get domain-specific partials with fallback
-
checkAndReloadAdminTemplates()
- Check and reload admin templates when changed -
checkAndReloadFrontendTemplates()
- Check and reload frontend templates when changed -
trackTemplateFiles(templatesPaths)
- Start tracking template files for changes -
shouldReloadAdminTemplates(mappedAdminTemplates)
- Check if admin templates need reloading -
shouldReloadFrontendTemplates(templatesPath, templateExtensions)
- Check if frontend templates need reloading -
handleAdminTemplateReload(adminManager)
- Complete admin template reload process -
handleFrontendTemplateReload(templateCache, templateResolver)
- Complete frontend template reload process
-
findRouteByPath(path, domain)
- Database route lookup -
handleRouteRedirect(route, res)
- Handle route redirects -
getDomainFromRequest(req)
- Extract domain from request -
buildCacheKey(path, req)
- Generate cache keys
-
renderWithTemplateContent(content, data, domain, req, route)
- Main content rendering -
generateRouteContent(route, domain, req)
- Route-based content generation -
generateTemplateContent(templatePath, domain, req, data)
- Template-based content generation -
fetchMetaFields(data)
- Process meta fields for templates
-
loadEntityAccessRules()
- Load entity access configuration -
isEntityAccessible(entityName)
- Check entity accessibility -
findEntityByPath(path)
- Entity lookup by URL path
-
renderWithCacheHandler(contentGenerator, errorHandler, responseHandler, domain, res, path, req)
- Generic cached response handler -
renderNotFound(domain, res, req)
- 404 error handling
-
handleSearchRequest(req, res)
- Process search requests with template data support
-
render(template, data, partials, domain, req, route, currentEntityData)
- Main template rendering with enhanced context -
processAllTemplateFunctions(template, domain, req, systemVariables)
- Process all template functions -
buildEnhancedRenderData(data, systemVariables, currentEntityData)
- Build template context with system variables
-
buildSystemVariables(req, route, domain)
- Create system variables for templates -
buildCurrentRequestData(req, domain)
- Build request context -
buildCurrentRouteData(route)
- Build route context -
buildCurrentDomainData(domain)
- Build domain context
-
Search.parseSearchParameters(query)
- Parse search query parameters including templateData -
Search.executeSearch(config)
- Execute search with configuration -
SearchRenderer.renderSearchResults(searchResults, config, domain, req)
- Render search results with template data
-
setupAdmin()
- Initialize admin panel -
generateListRouteContent()
- Entity list pages -
generateEditRouteContent()
- Entity edit forms -
processSaveEntity()
- Handle form submissions
-
prepareSetup()
- Setup installation routes -
executeInstallProcess()
- Run installation -
generateEntities()
- Create entity files
project/
├── admin/
│ └── templates/ # Admin panel templates
├── lib/
│ ├── frontend/ # Frontend specialized classes
│ │ ├── template-resolver.js
│ │ ├── template-cache.js
│ │ ├── request-processor.js
│ │ ├── entity-access-manager.js
│ │ ├── content-renderer.js
│ │ └── response-manager.js
│ ├── frontend.js # Main Frontend orchestrator
│ ├── template-reloader.js # Template reloading functionality
│ ├── search-request-handler.js
│ ├── search.js # Search functionality
│ ├── search-renderer.js # Search result rendering
│ └── template-engine.js # Core template processing
├── templates/
│ ├── layouts/ # Body content layouts
│ ├── domains/ # Domain-specific templates
│ ├── partials/ # Shared template partials
│ ├── page.html # Base HTML wrapper
│ └── 404.html # Error page
├── translations/
│ ├── en.json # English translations
│ ├── es.json # Spanish translations
│ └── fr.json # French translations
├── public/
│ ├── css/ # Stylesheets
│ ├── js/ # Client scripts
│ └── assets/ # Static assets
├── entities/ # Generated entity classes
├── .env # Environment configuration
├── install.lock # Installation lock file
└── index.js # Main application file
- Fork the repository
- Create a feature branch
- Follow the coding standards in the JavaScript rules
- Submit a pull request
Need something specific?
Request a feature here: https://www.reldens.com/features-request
https://www.reldens.com/documentation/cms
MIT License - see LICENSE file for details.