@reldens/cms 0.47.0 → 0.49.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/.claude/advanced-usage-guide.md +98 -0
- package/.claude/api-reference.md +146 -0
- package/.claude/configuration-guide.md +126 -0
- package/.claude/database-schema.md +32 -0
- package/.claude/forms-system-guide.md +193 -0
- package/.claude/installation-guide.md +223 -0
- package/.claude/multi-domain-i18n-guide.md +178 -0
- package/.claude/password-management-guide.md +426 -0
- package/.claude/search-guide.md +66 -0
- package/.claude/templating-system-guide.md +349 -0
- package/CLAUDE.md +92 -3
- package/README.md +71 -1447
- package/bin/reldens-cms-update-password.js +246 -0
- package/lib/admin-manager/router-contents.js +14 -2
- package/lib/admin-manager/router.js +1 -0
- package/lib/manager.js +70 -8
- package/lib/password-encryption-handler.js +94 -0
- package/lib/template-engine.js +8 -2
- package/package.json +5 -4
- package/templates/.env.dist +1 -1
- package/templates/page.html +15 -11
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
# Enhanced Templating System Guide
|
|
2
|
+
|
|
3
|
+
## System Variables
|
|
4
|
+
|
|
5
|
+
Every template has access to system variables providing context about the current request:
|
|
6
|
+
|
|
7
|
+
```html
|
|
8
|
+
{{currentRequest.baseUrl}}
|
|
9
|
+
{{currentRequest.protocol}}
|
|
10
|
+
{{currentRequest.host}}
|
|
11
|
+
{{currentRequest.path}}
|
|
12
|
+
{{currentRequest.method}}
|
|
13
|
+
{{currentRequest.userAgent}}
|
|
14
|
+
{{currentRequest.isSecure}}
|
|
15
|
+
|
|
16
|
+
{{currentRoute.id}}
|
|
17
|
+
{{currentRoute.path}}
|
|
18
|
+
{{currentRoute.title}}
|
|
19
|
+
{{currentRoute.template}}
|
|
20
|
+
{{currentRoute.layout}}
|
|
21
|
+
|
|
22
|
+
{{currentDomain.current}}
|
|
23
|
+
{{currentDomain.default}}
|
|
24
|
+
{{currentDomain.resolved}}
|
|
25
|
+
|
|
26
|
+
{{systemInfo.environment}}
|
|
27
|
+
{{systemInfo.nodeVersion}}
|
|
28
|
+
{{systemInfo.timestamp}}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Template Functions
|
|
32
|
+
|
|
33
|
+
### URL Transformers
|
|
34
|
+
|
|
35
|
+
The CMS provides three URL transformers with different behaviors for CDN support:
|
|
36
|
+
|
|
37
|
+
#### [url()] - Public URL (No CDN)
|
|
38
|
+
|
|
39
|
+
Generates URLs using the **public base URL** without CDN support.
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
[url(/articles)]
|
|
43
|
+
[url(/contact#form)]
|
|
44
|
+
[url(/css/styles.css)]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Output:**
|
|
48
|
+
- Without CDN: `https://example.com/css/styles.css`
|
|
49
|
+
- With CDN configured: `https://example.com/css/styles.css` (same, CDN ignored)
|
|
50
|
+
|
|
51
|
+
**Use for:** Internal application routes, API endpoints, form actions.
|
|
52
|
+
|
|
53
|
+
#### [cdn()] - CDN or Public URL
|
|
54
|
+
|
|
55
|
+
Generates URLs using the **CDN URL if configured**, otherwise falls back to public URL.
|
|
56
|
+
|
|
57
|
+
```html
|
|
58
|
+
[cdn(/css/styles.css)]
|
|
59
|
+
[cdn(/js/scripts.js)]
|
|
60
|
+
[cdn(/assets/web/logo.png)]
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Output:**
|
|
64
|
+
- Without CDN: `https://example.com/css/styles.css`
|
|
65
|
+
- With CDN configured: `https://cdn.example.com/css/styles.css`
|
|
66
|
+
|
|
67
|
+
**Use for:** Static assets outside `/assets` folder (CSS, JS, fonts, etc.).
|
|
68
|
+
|
|
69
|
+
#### [asset()] - CDN or Public URL + /assets Prefix
|
|
70
|
+
|
|
71
|
+
Generates URLs using the **CDN URL if configured** and **automatically prepends `/assets`** to the path.
|
|
72
|
+
|
|
73
|
+
```html
|
|
74
|
+
[asset(/web/logo.png)]
|
|
75
|
+
[asset(/images/hero.jpg)]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Output:**
|
|
79
|
+
- Without CDN: `https://example.com/assets/web/logo.png`
|
|
80
|
+
- With CDN configured: `https://cdn.example.com/assets/web/logo.png`
|
|
81
|
+
|
|
82
|
+
**Use for:** Static assets inside `/assets` folder (images, downloads, media files).
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
### URL Transformer Best Practices
|
|
87
|
+
|
|
88
|
+
**Based on folder structure:**
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
public/
|
|
92
|
+
assets/ # Use [asset(/file)] OR [cdn(/assets/file)]
|
|
93
|
+
web/
|
|
94
|
+
images/
|
|
95
|
+
downloads/
|
|
96
|
+
css/ # Use [cdn(/css/file)]
|
|
97
|
+
js/ # Use [cdn(/js/file)]
|
|
98
|
+
fonts/ # Use [cdn(/fonts/file)]
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Examples:**
|
|
102
|
+
|
|
103
|
+
```html
|
|
104
|
+
<!-- Files in public/assets/ -->
|
|
105
|
+
<meta property="og:image" content="[asset(/web/logo.png)]"/>
|
|
106
|
+
<img src="[asset(/images/hero.jpg)]" alt="Hero"/>
|
|
107
|
+
|
|
108
|
+
<!-- Files in public/css/, public/js/, etc. -->
|
|
109
|
+
<link rel="stylesheet" href="[cdn(/css/styles.css)]"/>
|
|
110
|
+
<script src="[cdn(/js/scripts.js)]"></script>
|
|
111
|
+
|
|
112
|
+
<!-- Internal routes -->
|
|
113
|
+
<form action="[url(/api/submit)]" method="post">
|
|
114
|
+
<a href="[url(/articles)]">Articles</a>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Key Rules:**
|
|
118
|
+
- `[asset(/file)]` adds `/assets` prefix automatically → Use for files in `/assets` folder
|
|
119
|
+
- `[cdn(/assets/file)]` requires full path → Alternative for files in `/assets` folder
|
|
120
|
+
- `[cdn(/css/file)]` requires full path → Use for files outside `/assets` (css, js, fonts)
|
|
121
|
+
- `[url(/path)]` never uses CDN → Use for application routes only
|
|
122
|
+
|
|
123
|
+
**Recommendation:** Use `[asset()]` for `/assets` files (cleaner syntax), `[cdn()]` for everything else.
|
|
124
|
+
|
|
125
|
+
### Date Formatting
|
|
126
|
+
|
|
127
|
+
```html
|
|
128
|
+
[date()]
|
|
129
|
+
[date(now, Y-m-d)]
|
|
130
|
+
[date(2024-01-01, d/m/Y)]
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Internationalization
|
|
134
|
+
|
|
135
|
+
```html
|
|
136
|
+
[translate(welcome.message)]
|
|
137
|
+
[t(hello.world, Hello World!)]
|
|
138
|
+
[t(greeting, Hi {name}!, {name: John})]
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Enhanced Context Passing
|
|
142
|
+
|
|
143
|
+
Child content blocks and partials receive context from parent pages:
|
|
144
|
+
|
|
145
|
+
```html
|
|
146
|
+
<entity name="cmsBlocks" field="name" value="article-sidebar"/>
|
|
147
|
+
|
|
148
|
+
{{currentEntity.title}}
|
|
149
|
+
{{currentEntity.id}}
|
|
150
|
+
{{currentEntity.template}}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Entity Rendering
|
|
154
|
+
|
|
155
|
+
### Single Entity
|
|
156
|
+
|
|
157
|
+
```html
|
|
158
|
+
<entity name="cmsBlocks" field="name" value="header-main"/>
|
|
159
|
+
<entity name="cmsBlocks" field="name" value="sidebar-left"/>
|
|
160
|
+
<entity name="articles" id="123"/>
|
|
161
|
+
<entity name="cmsPages" id="1"/>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Single Field Collections
|
|
165
|
+
|
|
166
|
+
Extract and concatenate a single field from multiple records:
|
|
167
|
+
|
|
168
|
+
```html
|
|
169
|
+
<collection name="cmsBlocks" filters="{status: 'active', category: 'navigation'}" field="content"/>
|
|
170
|
+
<collection name="articles" filters="{featured: true}" field="title"/>
|
|
171
|
+
<collection name="articles" filters="{featured: true}" field="title" data="{limit: 5, sortBy: 'created_at', sortDirection: 'desc'}"/>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Loop Collections
|
|
175
|
+
|
|
176
|
+
```html
|
|
177
|
+
<collection name="cmsBlocks" filters="{status: 'active'}">
|
|
178
|
+
<div class="block">
|
|
179
|
+
<h3>{{row.title}}</h3>
|
|
180
|
+
<div class="content">{{row.content}}</div>
|
|
181
|
+
</div>
|
|
182
|
+
</collection>
|
|
183
|
+
|
|
184
|
+
<collection name="articles" filters="{category: 'technology'}">
|
|
185
|
+
<div class="article">
|
|
186
|
+
<h4>{{row.title}}</h4>
|
|
187
|
+
<p>{{row.summary}}</p>
|
|
188
|
+
<img src="{{row.featured_image}}" alt="{{row.title}}">
|
|
189
|
+
</div>
|
|
190
|
+
</collection>
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Paginated Collections
|
|
194
|
+
|
|
195
|
+
```html
|
|
196
|
+
<collection name="articles"
|
|
197
|
+
filters="{featured: true}"
|
|
198
|
+
data="{limit: 10, sortBy: 'created_at', sortDirection: 'desc'}"
|
|
199
|
+
pagination="articles-1"
|
|
200
|
+
container="pagedCollection"
|
|
201
|
+
prevPages="2"
|
|
202
|
+
nextPages="2">
|
|
203
|
+
<div class="article-card">
|
|
204
|
+
<h4>{{row.title}}</h4>
|
|
205
|
+
<p>{{row.summary}}</p>
|
|
206
|
+
<span class="date">{{row.created_at}}</span>
|
|
207
|
+
</div>
|
|
208
|
+
</collection>
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Pagination Attributes:**
|
|
212
|
+
|
|
213
|
+
- `pagination="collection-id"` - Enables pagination with unique identifier
|
|
214
|
+
- `container="templateName"` - Custom pagination template (defaults to "pagedCollection")
|
|
215
|
+
- `prevPages="2"` - Number of previous page links to show (default: 2)
|
|
216
|
+
- `nextPages="2"` - Number of next page links to show (default: 2)
|
|
217
|
+
|
|
218
|
+
**Pagination URL Parameters:**
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
/articles?articles-1-key={"page":2,"limit":10,"sortBy":"created_at","sortDirection":"desc"}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**Available Pagination Template Variables:**
|
|
225
|
+
|
|
226
|
+
- `{{&collectionContentForCurrentPage}}` - Rendered collection items for current page
|
|
227
|
+
- `{{currentPage}}` - Current page number
|
|
228
|
+
- `{{totalPages}}` - Total number of pages
|
|
229
|
+
- `{{totalRecords}}` - Total number of records
|
|
230
|
+
- `{{prevPageUrl}}` / `{{nextPageUrl}}` - Previous/next page URLs
|
|
231
|
+
- `{{&prevPageLabel}}` / `{{&nextPageLabel}}` - Previous/next link labels
|
|
232
|
+
- `{{#prevPages}}` / `{{#nextPages}}` - Arrays of page objects with pageUrl and pageLabel
|
|
233
|
+
- `{{hasNextPage}}` / `{{hasPrevPage}}` - Boolean flags for navigation availability
|
|
234
|
+
|
|
235
|
+
## Custom Partials
|
|
236
|
+
|
|
237
|
+
### HTML-style Syntax
|
|
238
|
+
|
|
239
|
+
```html
|
|
240
|
+
<partial name="hero"
|
|
241
|
+
sectionStyle=" bg-black"
|
|
242
|
+
bigTextHtml="A free open-source platform!"
|
|
243
|
+
mediumTextHtml="Build with Node.js, MySQL"
|
|
244
|
+
imageUrl="/assets/web/logo.png"
|
|
245
|
+
imageAlt="Logo" />
|
|
246
|
+
|
|
247
|
+
<partial name="productCard"
|
|
248
|
+
title="Premium Package"
|
|
249
|
+
price="$99"
|
|
250
|
+
highlighted="true">
|
|
251
|
+
</partial>
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Mustache Syntax
|
|
255
|
+
|
|
256
|
+
```html
|
|
257
|
+
{{>hero -{
|
|
258
|
+
bigTextHtml: "Welcome!",
|
|
259
|
+
mediumTextHtml: "Build with Node.js",
|
|
260
|
+
imageUrl: "https://example.com/hero.jpg",
|
|
261
|
+
ctaText: "Get Started",
|
|
262
|
+
ctaLink: "/documentation"
|
|
263
|
+
}-}}
|
|
264
|
+
|
|
265
|
+
<collection name="cmsPages" filters="{featured: true}" data="{limit: 3}">
|
|
266
|
+
{{>cardView -{row}-}}
|
|
267
|
+
</collection>
|
|
268
|
+
|
|
269
|
+
{{>productCard -{
|
|
270
|
+
title: "Premium Package",
|
|
271
|
+
price: "$99",
|
|
272
|
+
features: ["Advanced Analytics", "Priority Support"],
|
|
273
|
+
highlighted: true
|
|
274
|
+
}-}}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## Collection Query Options
|
|
278
|
+
|
|
279
|
+
Collections support advanced query parameters:
|
|
280
|
+
|
|
281
|
+
- **limit** - Maximum number of records to return
|
|
282
|
+
- **offset** - Number of records to skip (for pagination)
|
|
283
|
+
- **sortBy** - Field name to sort by
|
|
284
|
+
- **sortDirection** - Sort direction ('asc' or 'desc')
|
|
285
|
+
|
|
286
|
+
### Examples
|
|
287
|
+
|
|
288
|
+
```html
|
|
289
|
+
<collection name="articles" filters="{}" field="title" data="{limit: 5, sortBy: 'title'}"/>
|
|
290
|
+
|
|
291
|
+
<collection name="articles" filters="{published: true}" data="{limit: 10, offset: 20, sortBy: 'created_at', sortDirection: 'desc'}">
|
|
292
|
+
<article>{{row.title}}</article>
|
|
293
|
+
</collection>
|
|
294
|
+
|
|
295
|
+
<collection name="articles" filters="{featured: true}" data="{limit: 3, sortBy: 'created_at', sortDirection: 'desc'}">
|
|
296
|
+
<div class="featured-article">{{row.title}}</div>
|
|
297
|
+
</collection>
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Layout System
|
|
301
|
+
|
|
302
|
+
### page.html - Full HTML wrapper
|
|
303
|
+
|
|
304
|
+
```html
|
|
305
|
+
<!DOCTYPE html>
|
|
306
|
+
<html lang="{{locale}}">
|
|
307
|
+
<head>
|
|
308
|
+
<title>{{title}}</title>
|
|
309
|
+
<meta name="description" content="{{description}}"/>
|
|
310
|
+
<link href="[url(/css/styles.css)]" rel="stylesheet"/>
|
|
311
|
+
</head>
|
|
312
|
+
<body class="{{siteHandle}}">
|
|
313
|
+
{{&content}}
|
|
314
|
+
<script src="[url(/js/scripts.js)]"></script>
|
|
315
|
+
</body>
|
|
316
|
+
</html>
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### layouts/default.html - Body content only
|
|
320
|
+
|
|
321
|
+
```html
|
|
322
|
+
<entity name="cmsBlocks" field="name" value="header-main"/>
|
|
323
|
+
|
|
324
|
+
<main id="main" class="main-container">
|
|
325
|
+
<div class="container">
|
|
326
|
+
<div class="row">
|
|
327
|
+
<div class="col-md-3">
|
|
328
|
+
<entity name="cmsBlocks" field="name" value="sidebar-left"/>
|
|
329
|
+
</div>
|
|
330
|
+
<div class="col-md-9">
|
|
331
|
+
{{&content}}
|
|
332
|
+
</div>
|
|
333
|
+
</div>
|
|
334
|
+
</div>
|
|
335
|
+
</main>
|
|
336
|
+
|
|
337
|
+
<entity name="cmsBlocks" field="name" value="footer-main"/>
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Content Blocks
|
|
341
|
+
|
|
342
|
+
Create reusable content blocks in the cms_blocks table via admin panel:
|
|
343
|
+
|
|
344
|
+
```sql
|
|
345
|
+
INSERT INTO cms_blocks (name, title, content) VALUES
|
|
346
|
+
('contact-info', 'Contact Information', '<p>Email: info@example.com</p>'),
|
|
347
|
+
('article-sidebar', 'Article Categories',
|
|
348
|
+
'<div class="categories"><h3>Categories</h3><ul><li><a href="[url(/articles/technology)]">Technology</a></li></ul></div>');
|
|
349
|
+
```
|
package/CLAUDE.md
CHANGED
|
@@ -14,6 +14,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|
|
14
14
|
- Template reloading for development
|
|
15
15
|
- Caching system for performance
|
|
16
16
|
- Event-driven architecture for extensibility
|
|
17
|
+
- Password encryption and management - Automatic password encryption with PBKDF2
|
|
17
18
|
|
|
18
19
|
## Key Commands
|
|
19
20
|
|
|
@@ -24,10 +25,61 @@ npx reldens-cms
|
|
|
24
25
|
# Generate entities from database
|
|
25
26
|
npx reldens-cms-generate-entities
|
|
26
27
|
|
|
28
|
+
# Update user password via CLI
|
|
29
|
+
npx reldens-cms-update-password --email=admin@example.com
|
|
30
|
+
|
|
27
31
|
# Or via npm scripts
|
|
28
32
|
npm run generate-entities
|
|
29
33
|
```
|
|
30
34
|
|
|
35
|
+
## Password Management
|
|
36
|
+
|
|
37
|
+
### CLI Password Update Command
|
|
38
|
+
|
|
39
|
+
The CMS provides a dedicated CLI command for securely updating user passwords:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Interactive mode (recommended - will prompt for password)
|
|
43
|
+
npx reldens-cms-update-password --email=admin@example.com
|
|
44
|
+
|
|
45
|
+
# By username
|
|
46
|
+
npx reldens-cms-update-password --username=admin
|
|
47
|
+
|
|
48
|
+
# With password in command (less secure)
|
|
49
|
+
npx reldens-cms-update-password --email=admin@example.com --password=newPassword123
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Options**:
|
|
53
|
+
- `--email=[email]` - User email address
|
|
54
|
+
- `--username=[username]` - User username
|
|
55
|
+
- `--password=[password]` - New password (prompted if not provided)
|
|
56
|
+
- `--help` or `-h` - Show this help message
|
|
57
|
+
|
|
58
|
+
**Notes**:
|
|
59
|
+
- Works with any storage driver (prisma, objection-js, mikro-orm)
|
|
60
|
+
- Uses `RELDENS_STORAGE_DRIVER` environment variable to detect the driver
|
|
61
|
+
- Automatically loads entities and Prisma client (if using Prisma)
|
|
62
|
+
|
|
63
|
+
### Automatic Password Encryption
|
|
64
|
+
|
|
65
|
+
The CMS automatically encrypts passwords when saving user records through the admin panel using the `PasswordEncryptionHandler`:
|
|
66
|
+
|
|
67
|
+
- Listens to `reldens.adminBeforeEntitySave` event
|
|
68
|
+
- Detects password field changes in users entity
|
|
69
|
+
- Encrypts passwords using PBKDF2 (100k iterations, SHA-512)
|
|
70
|
+
- Stores passwords in `salt:hash` format
|
|
71
|
+
- **Enabled by default** - can be disabled with `enablePasswordEncryption: false` in Manager config
|
|
72
|
+
|
|
73
|
+
**Password Encryption Algorithm**:
|
|
74
|
+
- **Method**: PBKDF2 (Password-Based Key Derivation Function 2)
|
|
75
|
+
- **Iterations**: 100,000
|
|
76
|
+
- **Key Length**: 64 bytes
|
|
77
|
+
- **Digest**: SHA-512
|
|
78
|
+
- **Salt Length**: 32 bytes (randomly generated)
|
|
79
|
+
- **Storage Format**: `salt:hash` (192 characters total)
|
|
80
|
+
|
|
81
|
+
See `.claude/password-management-guide.md` for comprehensive password management documentation.
|
|
82
|
+
|
|
31
83
|
## Architecture Overview
|
|
32
84
|
|
|
33
85
|
The CMS follows a modular architecture with specialized classes following SOLID principles:
|
|
@@ -39,6 +91,7 @@ The CMS follows a modular architecture with specialized classes following SOLID
|
|
|
39
91
|
- Handles configuration and environment variables
|
|
40
92
|
- Manages multi-domain setup and security
|
|
41
93
|
- Coordinates service lifecycle
|
|
94
|
+
- Initializes password encryption handler
|
|
42
95
|
|
|
43
96
|
**lib/frontend.js** - Frontend orchestrator
|
|
44
97
|
- Coordinates all frontend operations
|
|
@@ -52,6 +105,18 @@ The CMS follows a modular architecture with specialized classes following SOLID
|
|
|
52
105
|
- Processes file uploads
|
|
53
106
|
- Builds admin UI from entity configurations
|
|
54
107
|
|
|
108
|
+
**lib/password-encryption-handler.js** - Password encryption handler
|
|
109
|
+
- Automatic password encryption for users entity
|
|
110
|
+
- Event-driven architecture
|
|
111
|
+
- PBKDF2 encryption with 100k iterations
|
|
112
|
+
- Detects and skips already-encrypted passwords
|
|
113
|
+
- Configurable entity and field names
|
|
114
|
+
|
|
115
|
+
**lib/admin-manager/router-contents.js** - Admin routing and form handling
|
|
116
|
+
- Password field handling (type="password", empty on edit)
|
|
117
|
+
- Password updates optional (skip if empty when editing)
|
|
118
|
+
- Configurable password field names via `passwordFieldNames`
|
|
119
|
+
|
|
55
120
|
### Installation & Setup
|
|
56
121
|
|
|
57
122
|
**lib/installer.js** - Installation orchestrator
|
|
@@ -252,7 +317,7 @@ The CMS uses these core tables:
|
|
|
252
317
|
- **cms_pages_meta** - Page-specific metadata
|
|
253
318
|
|
|
254
319
|
### User Management (Optional)
|
|
255
|
-
- **users** - User authentication
|
|
320
|
+
- **users** - User authentication (with encrypted passwords)
|
|
256
321
|
- **roles** - Role definitions
|
|
257
322
|
|
|
258
323
|
## Template System
|
|
@@ -342,13 +407,21 @@ templates/
|
|
|
342
407
|
|
|
343
408
|
**Template Functions:**
|
|
344
409
|
```html
|
|
345
|
-
[url(/articles)] <!-- URL generation -->
|
|
346
|
-
[
|
|
410
|
+
[url(/articles)] <!-- URL generation (no CDN) -->
|
|
411
|
+
[cdn(/css/styles.css)] <!-- CDN URL (or public URL if no CDN) -->
|
|
412
|
+
[asset(/web/logo.png)] <!-- Asset URL with /assets prefix (uses CDN if available) -->
|
|
347
413
|
[date(now, Y-m-d)] <!-- Date formatting -->
|
|
348
414
|
[translate(welcome.message)] <!-- i18n -->
|
|
349
415
|
[t(key, Default, {var: value})] <!-- i18n with interpolation -->
|
|
350
416
|
```
|
|
351
417
|
|
|
418
|
+
**URL Transformer Selection:**
|
|
419
|
+
- `[url()]` - Application routes (never uses CDN)
|
|
420
|
+
- `[cdn()]` - Static assets outside `/assets` (CSS, JS, fonts)
|
|
421
|
+
- `[asset()]` - Static assets inside `/assets` folder (images, media)
|
|
422
|
+
|
|
423
|
+
See `.claude/templating-system-guide.md` for detailed URL transformer documentation.
|
|
424
|
+
|
|
352
425
|
## Configuration
|
|
353
426
|
|
|
354
427
|
### Environment Variables (.env)
|
|
@@ -399,6 +472,9 @@ const cms = new Manager({
|
|
|
399
472
|
},
|
|
400
473
|
adminRoleId: 99,
|
|
401
474
|
|
|
475
|
+
// Password encryption (enabled by default)
|
|
476
|
+
enablePasswordEncryption: true, // Set to false to disable
|
|
477
|
+
|
|
402
478
|
// Performance
|
|
403
479
|
cache: true,
|
|
404
480
|
reloadTime: -1, // Development: reload on every request
|
|
@@ -444,6 +520,7 @@ The CMS provides extensive event hooks for customization:
|
|
|
444
520
|
- `reldens.setupAdminRouter` - Setup admin routes
|
|
445
521
|
- `reldens.setupAdminRoutes` - After route setup
|
|
446
522
|
- `reldens.setupAdminManagers` - After manager setup
|
|
523
|
+
- `reldens.adminBeforeEntitySave` - Before entity save (used by password encryption handler)
|
|
447
524
|
|
|
448
525
|
### Template Reloading Events
|
|
449
526
|
- `reldens.templateReloader.templatesChanged` - Templates changed
|
|
@@ -495,6 +572,8 @@ Define custom entity configurations in `entitiesConfig`:
|
|
|
495
572
|
## Security Features
|
|
496
573
|
|
|
497
574
|
- **Authentication** - Role-based admin access
|
|
575
|
+
- **Password Encryption** - PBKDF2 with 100k iterations, SHA-512
|
|
576
|
+
- **Automatic Password Encryption** - Event-driven encryption on save (enabled by default)
|
|
498
577
|
- **CSRF Protection** - Via session tokens
|
|
499
578
|
- **File Upload Validation** - MIME type and extension checking
|
|
500
579
|
- **Entity Access Control** - Public/private entity rules
|
|
@@ -518,6 +597,8 @@ Define custom entity configurations in `entitiesConfig`:
|
|
|
518
597
|
8. **Event hooks are async** - use await when emitting events
|
|
519
598
|
9. **Template reloading is development-only** - disable in production
|
|
520
599
|
10. **Multi-domain requires proper configuration** - set up domain mapping correctly
|
|
600
|
+
11. **Password encryption is automatic** - enabled by default for users entity
|
|
601
|
+
12. **Never store plain text passwords** - always use Encryptor.encryptPassword()
|
|
521
602
|
|
|
522
603
|
## Common File Paths
|
|
523
604
|
|
|
@@ -528,6 +609,8 @@ Define custom entity configurations in `entitiesConfig`:
|
|
|
528
609
|
- **Public assets:** `public/`
|
|
529
610
|
- **Environment:** `.env`
|
|
530
611
|
- **Install lock:** `install.lock`
|
|
612
|
+
- **Password CLI:** `bin/reldens-cms-update-password.js`
|
|
613
|
+
- **Password handler:** `lib/password-encryption-handler.js`
|
|
531
614
|
|
|
532
615
|
## Troubleshooting
|
|
533
616
|
|
|
@@ -551,6 +634,12 @@ Define custom entity configurations in `entitiesConfig`:
|
|
|
551
634
|
- Check entity access configuration
|
|
552
635
|
- Verify relationship mappings
|
|
553
636
|
|
|
637
|
+
### Password Issues
|
|
638
|
+
- Verify password is encrypted (contains `:` and is 192 chars)
|
|
639
|
+
- Check `enablePasswordEncryption` is `true` in Manager config
|
|
640
|
+
- Use CLI command for password updates: `npx reldens-cms-update-password`
|
|
641
|
+
- For debugging, set `RELDENS_LOG_LEVEL=9` to see password encryption logs
|
|
642
|
+
|
|
554
643
|
## Dependencies
|
|
555
644
|
|
|
556
645
|
- **@reldens/storage** - Database abstraction layer
|