domma-cms 0.1.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 (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +469 -0
  3. package/admin/css/admin.css +1123 -0
  4. package/admin/index.html +72 -0
  5. package/admin/js/api.js +210 -0
  6. package/admin/js/app.js +270 -0
  7. package/admin/js/config/sidebar-config.js +107 -0
  8. package/admin/js/lib/card.js +63 -0
  9. package/admin/js/lib/image-editor.js +869 -0
  10. package/admin/js/lib/markdown-toolbar.js +421 -0
  11. package/admin/js/templates/dashboard.html +50 -0
  12. package/admin/js/templates/documentation.html +237 -0
  13. package/admin/js/templates/layouts.html +11 -0
  14. package/admin/js/templates/login.html +58 -0
  15. package/admin/js/templates/media.html +16 -0
  16. package/admin/js/templates/navigation.html +50 -0
  17. package/admin/js/templates/page-editor.html +126 -0
  18. package/admin/js/templates/pages.html +18 -0
  19. package/admin/js/templates/plugins.html +12 -0
  20. package/admin/js/templates/settings.html +190 -0
  21. package/admin/js/templates/tutorials.html +233 -0
  22. package/admin/js/templates/user-editor.html +12 -0
  23. package/admin/js/templates/users.html +10 -0
  24. package/admin/js/views/dashboard.js +48 -0
  25. package/admin/js/views/documentation.js +12 -0
  26. package/admin/js/views/index.js +33 -0
  27. package/admin/js/views/layouts.js +49 -0
  28. package/admin/js/views/login.js +254 -0
  29. package/admin/js/views/media.js +240 -0
  30. package/admin/js/views/navigation.js +152 -0
  31. package/admin/js/views/page-editor.js +479 -0
  32. package/admin/js/views/pages.js +64 -0
  33. package/admin/js/views/plugins.js +100 -0
  34. package/admin/js/views/settings.js +64 -0
  35. package/admin/js/views/tutorials.js +12 -0
  36. package/admin/js/views/user-editor.js +88 -0
  37. package/admin/js/views/users.js +73 -0
  38. package/bin/cli.js +334 -0
  39. package/config/auth.json +20 -0
  40. package/config/content.json +10 -0
  41. package/config/navigation.json +63 -0
  42. package/config/plugins.json +47 -0
  43. package/config/presets.json +34 -0
  44. package/config/server.json +6 -0
  45. package/config/site.json +33 -0
  46. package/package.json +67 -0
  47. package/plugins/back-to-top/admin/templates/back-to-top-settings.html +55 -0
  48. package/plugins/back-to-top/admin/views/back-to-top-settings.js +44 -0
  49. package/plugins/back-to-top/config.js +10 -0
  50. package/plugins/back-to-top/plugin.js +24 -0
  51. package/plugins/back-to-top/plugin.json +36 -0
  52. package/plugins/back-to-top/public/inject-body.html +105 -0
  53. package/plugins/cookie-consent/admin/templates/cookie-consent-settings.html +113 -0
  54. package/plugins/cookie-consent/admin/views/cookie-consent-settings.js +73 -0
  55. package/plugins/cookie-consent/config.js +30 -0
  56. package/plugins/cookie-consent/plugin.js +24 -0
  57. package/plugins/cookie-consent/plugin.json +36 -0
  58. package/plugins/cookie-consent/public/inject-body.html +69 -0
  59. package/plugins/custom-css/admin/templates/custom-css.html +17 -0
  60. package/plugins/custom-css/admin/views/custom-css.js +35 -0
  61. package/plugins/custom-css/config.js +1 -0
  62. package/plugins/custom-css/data/custom.css +0 -0
  63. package/plugins/custom-css/plugin.js +63 -0
  64. package/plugins/custom-css/plugin.json +32 -0
  65. package/plugins/custom-css/public/inject-head.html +1 -0
  66. package/plugins/domma-effects/admin/templates/domma-effects.html +488 -0
  67. package/plugins/domma-effects/admin/views/domma-effects.js +56 -0
  68. package/plugins/domma-effects/config.js +9 -0
  69. package/plugins/domma-effects/plugin.js +22 -0
  70. package/plugins/domma-effects/plugin.json +36 -0
  71. package/plugins/domma-effects/public/celebrations/core/canvas.js +111 -0
  72. package/plugins/domma-effects/public/celebrations/core/particles.js +144 -0
  73. package/plugins/domma-effects/public/celebrations/core/physics.js +166 -0
  74. package/plugins/domma-effects/public/celebrations/index.js +535 -0
  75. package/plugins/domma-effects/public/celebrations/themes/christmas.js +1805 -0
  76. package/plugins/domma-effects/public/celebrations/themes/guy-fawkes.js +1477 -0
  77. package/plugins/domma-effects/public/celebrations/themes/halloween.js +1837 -0
  78. package/plugins/domma-effects/public/celebrations/themes/st-andrews.js +1175 -0
  79. package/plugins/domma-effects/public/celebrations/themes/st-davids.js +1258 -0
  80. package/plugins/domma-effects/public/celebrations/themes/st-georges.js +1754 -0
  81. package/plugins/domma-effects/public/celebrations/themes/st-patricks.js +1290 -0
  82. package/plugins/domma-effects/public/celebrations/themes/valentines.js +1361 -0
  83. package/plugins/domma-effects/public/inject-body.html +268 -0
  84. package/plugins/example-analytics/admin/templates/analytics.html +10 -0
  85. package/plugins/example-analytics/admin/views/analytics.js +51 -0
  86. package/plugins/example-analytics/config.js +6 -0
  87. package/plugins/example-analytics/plugin.js +58 -0
  88. package/plugins/example-analytics/plugin.json +27 -0
  89. package/plugins/example-analytics/public/inject-body.html +13 -0
  90. package/plugins/example-analytics/public/inject-head.html +1 -0
  91. package/plugins/example-analytics/stats.json +1 -0
  92. package/plugins/form-builder/admin/templates/form-editor.html +158 -0
  93. package/plugins/form-builder/admin/templates/form-settings.html +29 -0
  94. package/plugins/form-builder/admin/templates/form-submissions.html +30 -0
  95. package/plugins/form-builder/admin/templates/forms-list.html +17 -0
  96. package/plugins/form-builder/admin/views/form-editor.js +817 -0
  97. package/plugins/form-builder/admin/views/form-settings.js +38 -0
  98. package/plugins/form-builder/admin/views/form-submissions.js +295 -0
  99. package/plugins/form-builder/admin/views/forms-list.js +164 -0
  100. package/plugins/form-builder/config.js +9 -0
  101. package/plugins/form-builder/data/forms/contact-details.json +63 -0
  102. package/plugins/form-builder/data/forms/contact.json +52 -0
  103. package/plugins/form-builder/data/submissions/contact-details.json +1 -0
  104. package/plugins/form-builder/data/submissions/contact.json +14 -0
  105. package/plugins/form-builder/email.js +103 -0
  106. package/plugins/form-builder/plugin.js +454 -0
  107. package/plugins/form-builder/plugin.json +56 -0
  108. package/plugins/form-builder/public/inject-body.html +270 -0
  109. package/plugins/form-builder/public/inject-head.html +42 -0
  110. package/public/css/site.css +189 -0
  111. package/public/js/site.js +109 -0
  112. package/scripts/copy-domma.js +48 -0
  113. package/scripts/fresh.js +41 -0
  114. package/scripts/reset.js +124 -0
  115. package/scripts/seed.js +666 -0
  116. package/scripts/setup.js +263 -0
  117. package/server/config.js +56 -0
  118. package/server/middleware/auth.js +97 -0
  119. package/server/routes/api/auth.js +116 -0
  120. package/server/routes/api/layouts.js +25 -0
  121. package/server/routes/api/media.js +93 -0
  122. package/server/routes/api/navigation.js +37 -0
  123. package/server/routes/api/pages.js +118 -0
  124. package/server/routes/api/plugins.js +46 -0
  125. package/server/routes/api/settings.js +25 -0
  126. package/server/routes/api/users.js +110 -0
  127. package/server/routes/public.js +108 -0
  128. package/server/server.js +169 -0
  129. package/server/services/content.js +298 -0
  130. package/server/services/images.js +334 -0
  131. package/server/services/markdown.js +297 -0
  132. package/server/services/plugins.js +246 -0
  133. package/server/services/renderer.js +80 -0
  134. package/server/services/users.js +212 -0
  135. package/server/templates/page.html +78 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Darryl Waterhouse
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,469 @@
1
+ # Domma CMS
2
+
3
+ [![Node ≥18](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![npm](https://img.shields.io/npm/v/domma-cms)](https://www.npmjs.com/package/domma-cms)
6
+
7
+ A flat-file CMS with no database. Pages are Markdown files, config is JSON, and the admin panel is a full SPA. Powered
8
+ by [Fastify](https://fastify.dev) on the backend and [Domma](https://npmjs.com/package/domma-js) on the frontend.
9
+
10
+ ---
11
+
12
+ ## Table of Contents
13
+
14
+ - [Quick Start](#quick-start)
15
+ - [Requirements](#requirements)
16
+ - [Project Structure](#project-structure)
17
+ - [Configuration](#configuration)
18
+ - [Content](#content)
19
+ - [Admin Panel](#admin-panel)
20
+ - [Plugins](#plugins)
21
+ - [Bundled Plugins](#bundled-plugins)
22
+ - [Building a Plugin](#building-a-plugin)
23
+ - [Scripts Reference](#scripts-reference)
24
+ - [Contributing](#contributing)
25
+ - [Licence](#licence)
26
+
27
+ ---
28
+
29
+ ## Quick Start
30
+
31
+ ```bash
32
+ npx domma-cms my-site
33
+ cd my-site
34
+ npm run dev
35
+ ```
36
+
37
+ Open **http://localhost:3050/admin** and log in with the credentials you created during setup.
38
+
39
+ > The setup wizard runs automatically after `npm install`. To run it again manually: `npm run setup`.
40
+
41
+ ---
42
+
43
+ ## Requirements
44
+
45
+ - **Node.js** 18 or higher
46
+ - **npm** 7 or higher
47
+ - **pm2** (for production) — `npm install -g pm2`
48
+
49
+ ---
50
+
51
+ ## Project Structure
52
+
53
+ After scaffolding, your project looks like this:
54
+
55
+ ```
56
+ my-site/
57
+ ├── admin/ # Admin SPA (Domma frontend)
58
+ │ ├── index.html
59
+ │ └── js/
60
+ │ ├── app.js # Router, auth, sidebar init
61
+ │ ├── api.js # Authenticated API client (auto token refresh)
62
+ │ ├── views/ # Admin view modules
63
+ │ └── templates/ # Companion HTML templates
64
+
65
+ ├── config/ # All site configuration (JSON)
66
+ │ ├── site.json # Title, tagline, theme, SEO, footer
67
+ │ ├── navigation.json # Navbar brand, items, variant, position
68
+ │ ├── plugins.json # Plugin enabled/disabled state + settings
69
+ │ ├── auth.json # JWT expiry, roles, bcrypt rounds
70
+ │ ├── content.json # Content directory paths and page defaults
71
+ │ └── server.json # Port, host, CORS, upload limits
72
+
73
+ ├── content/ # All user content (created on first run)
74
+ │ ├── pages/ # Markdown pages (maps to public URLs)
75
+ │ ├── media/ # Uploaded files
76
+ │ └── users/ # User accounts ({uuid}.json)
77
+
78
+ ├── plugins/ # CMS plugins
79
+ │ ├── form-builder/
80
+ │ ├── example-analytics/
81
+ │ ├── back-to-top/
82
+ │ └── cookie-consent/
83
+
84
+ ├── public/ # Public frontend assets (CSS, JS)
85
+ ├── scripts/ # CLI utilities (setup, reset, seed, etc.)
86
+ ├── server/ # Fastify backend
87
+ │ ├── server.js # Entry point
88
+ │ ├── config.js # Config loader (getConfig / saveConfig)
89
+ │ ├── middleware/ # Auth (JWT, roles)
90
+ │ ├── routes/ # API + public SSR routes
91
+ │ ├── services/ # Content, users, plugins, markdown, renderer
92
+ │ └── templates/ # Public HTML shell (page.html)
93
+
94
+ ├── .env # Secrets — JWT_SECRET, NODE_ENV
95
+ ├── .env.example # Template for .env
96
+ └── package.json
97
+ ```
98
+
99
+ ---
100
+
101
+ ## Configuration
102
+
103
+ All configuration lives in `config/` as JSON files. They are editable directly or via the admin panel.
104
+
105
+ ### `config/site.json`
106
+
107
+ Controls the public-facing site identity.
108
+
109
+ ```json
110
+ {
111
+ "title": "My Site",
112
+ "tagline": "Powered by Domma CMS",
113
+ "theme": "charcoal-dark",
114
+ "seo": {
115
+ "defaultTitle": "My Site",
116
+ "titleSeparator": " | ",
117
+ "defaultDescription": "A site built with Domma CMS"
118
+ },
119
+ "footer": {
120
+ "copyright": "© 2026 My Site. All rights reserved.",
121
+ "links": [
122
+ { "text": "Privacy Policy", "url": "/privacy" },
123
+ { "text": "Contact", "url": "/contact" }
124
+ ]
125
+ }
126
+ }
127
+ ```
128
+
129
+ **Available themes:** `charcoal-dark`, `charcoal-light`, `ocean-dark`, `ocean-light`, `forest-dark`, `forest-light`,
130
+ `sunset-dark`, `sunset-light`, `royal-dark`, `royal-light`, `lemon-dark`, `lemon-light`, `silver-dark`, `silver-light`,
131
+ `grayve`, `christmas-dark`, `christmas-light`
132
+
133
+ ### `config/navigation.json`
134
+
135
+ Controls the public navbar.
136
+
137
+ ```json
138
+ {
139
+ "brand": { "text": "My Site", "logo": null, "url": "/" },
140
+ "items": [
141
+ { "text": "Home", "url": "/", "icon": "home" },
142
+ { "text": "About", "url": "/about", "icon": "info" },
143
+ { "text": "Contact", "url": "/contact", "icon": "mail" }
144
+ ],
145
+ "variant": "dark",
146
+ "position": "sticky"
147
+ }
148
+ ```
149
+
150
+ Dropdown menus are supported via nested `items` arrays on any item.
151
+
152
+ ### `config/plugins.json`
153
+
154
+ Enables or disables plugins and stores their settings.
155
+
156
+ ```json
157
+ {
158
+ "form-builder": {
159
+ "enabled": true,
160
+ "settings": { "smtp": { "host": "localhost" } }
161
+ }
162
+ }
163
+ ```
164
+
165
+ ### `.env`
166
+
167
+ Secrets only — never commit this file.
168
+
169
+ ```
170
+ JWT_SECRET=your-64-char-random-string
171
+ NODE_ENV=development
172
+ ```
173
+
174
+ Generate a strong secret with: `node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"`
175
+
176
+ ---
177
+
178
+ ## Content
179
+
180
+ Pages are Markdown files in `content/pages/`. The filename determines the public URL.
181
+
182
+ | File path | Public URL |
183
+ |-------------------------------------|---------------------|
184
+ | `content/pages/index.md` | `/` |
185
+ | `content/pages/about.md` | `/about` |
186
+ | `content/pages/blog/hello-world.md` | `/blog/hello-world` |
187
+ | `content/pages/services/index.md` | `/services` |
188
+
189
+ ### Frontmatter
190
+
191
+ Every page starts with a YAML frontmatter block:
192
+
193
+ ```yaml
194
+ ---
195
+ title: My Page
196
+ slug: my-page
197
+ description: A short description for SEO
198
+ layout: default
199
+ status: published
200
+ sortOrder: 1
201
+ showInNav: false
202
+ sidebar: false
203
+ seo:
204
+ title: My Page | My Site
205
+ description: A short description for SEO
206
+ createdAt: '2026-01-01T00:00:00.000Z'
207
+ updatedAt: '2026-01-01T00:00:00.000Z'
208
+ ---
209
+
210
+ # My Page
211
+
212
+ Page content goes here in Markdown.
213
+ ```
214
+
215
+ | Field | Description |
216
+ |-------------------|---------------------------------------------------------------------------|
217
+ | `title` | Page title (used in `<title>` tag and admin listing) |
218
+ | `slug` | URL slug (usually matches the filename) |
219
+ | `description` | Short description for SEO |
220
+ | `layout` | Layout preset to use (`default` or any preset from `config/presets.json`) |
221
+ | `status` | `published` or `draft` — draft pages return 404 on the public site |
222
+ | `sortOrder` | Integer used to order pages in admin listings |
223
+ | `showInNav` | Whether to include this page in auto-generated nav menus |
224
+ | `sidebar` | Whether to show a sidebar on this page |
225
+ | `seo.title` | Overrides the `<title>` tag |
226
+ | `seo.description` | Overrides the meta description |
227
+
228
+ ### Plugin Shortcodes
229
+
230
+ Some plugins inject content via HTML attributes:
231
+
232
+ ```html
233
+ <!-- Form Builder plugin — embed any form by slug -->
234
+ <div data-form="contact"></div>
235
+ <div data-form="feedback"></div>
236
+ ```
237
+
238
+ Forms are created in the admin panel under **Plugins → Forms**.
239
+
240
+ ---
241
+
242
+ ## Admin Panel
243
+
244
+ Access the admin panel at **http://localhost:3050/admin**.
245
+
246
+ ### First Login
247
+
248
+ Run `npm run setup` (or `make setup`) to create your admin account, set a site title, and choose a theme. The wizard is
249
+ safe to re-run — it skips any steps already completed.
250
+
251
+ ### Roles
252
+
253
+ | Role | Level | Can do |
254
+ |--------------|-------|-------------------------------------------------------------|
255
+ | `admin` | 0 | Everything, including user management and plugin config |
256
+ | `manager` | 1 | Manage pages, media, navigation, layouts, and site settings |
257
+ | `editor` | 2 | Create and edit pages and media |
258
+ | `subscriber` | 3 | Read-only access |
259
+
260
+ Permissions per resource are defined in `config/auth.json`.
261
+
262
+ ### Navigation
263
+
264
+ The sidebar groups content by role:
265
+
266
+ - **Overview** — Dashboard
267
+ - **Structure** — Navigation, Layouts *(admin and manager only)*
268
+ - **Content** — Pages, Media
269
+ - **Configuration** — Users, Site Settings *(admin and manager only)*
270
+ - **Plugins** — Plugin manager and plugin settings *(admin only)*
271
+
272
+ ---
273
+
274
+ ## Plugins
275
+
276
+ Plugins extend Domma CMS with backend routes, public page injections, and admin panel views.
277
+
278
+ ### Bundled Plugins
279
+
280
+ | Plugin | Description |
281
+ |--------------------|---------------------------------------------------------------------------------------------------|
282
+ | **Form Builder** | Visual form builder — create arbitrary forms, store submissions, trigger email and webhook actions |
283
+ | **Analytics** | Basic page view tracking stored as a flat JSON file |
284
+ | **Back to Top** | Configurable scroll-to-top button injected into every public page |
285
+ | **Cookie Consent** | GDPR cookie consent banner with per-category toggles |
286
+
287
+ Enable or disable any plugin in the admin panel under **Plugins**, or directly in `config/plugins.json`.
288
+
289
+ ---
290
+
291
+ ### Building a Plugin
292
+
293
+ A plugin is a directory under `plugins/` containing three mandatory files:
294
+
295
+ ```
296
+ plugins/my-plugin/
297
+ ├── plugin.json # Manifest
298
+ ├── plugin.js # Fastify plugin (backend)
299
+ └── config.js # Default settings
300
+ ```
301
+
302
+ #### `plugin.json`
303
+
304
+ ```json
305
+ {
306
+ "name": "my-plugin",
307
+ "displayName": "My Plugin",
308
+ "version": "1.0.0",
309
+ "description": "What this plugin does.",
310
+ "author": "Your Name",
311
+ "date": "2026-01-01",
312
+ "icon": "package",
313
+
314
+ "admin": {
315
+ "sidebar": [
316
+ {
317
+ "id": "my-plugin",
318
+ "text": "My Plugin",
319
+ "icon": "package",
320
+ "url": "#/plugins/my-plugin",
321
+ "section": "#/plugins/my-plugin"
322
+ }
323
+ ],
324
+ "routes": [
325
+ {
326
+ "path": "/plugins/my-plugin",
327
+ "view": "plugin-my-plugin-settings",
328
+ "title": "My Plugin - Domma CMS"
329
+ }
330
+ ],
331
+ "views": {
332
+ "plugin-my-plugin-settings": {
333
+ "entry": "my-plugin/admin/views/my-plugin-settings.js",
334
+ "exportName": "myPluginSettingsView"
335
+ }
336
+ }
337
+ },
338
+
339
+ "inject": {
340
+ "head": "public/inject-head.html",
341
+ "bodyEnd": "public/inject-body.html"
342
+ }
343
+ }
344
+ ```
345
+
346
+ All fields except `admin` and `inject` are required. If mandatory fields are missing, the plugin is skipped at startup
347
+ with a warning — the server never crashes.
348
+
349
+ #### `plugin.js`
350
+
351
+ A standard Fastify plugin. Use `getPluginSettings` to read merged settings (defaults + user overrides):
352
+
353
+ ```js
354
+ import { getPluginSettings } from '../../server/services/plugins.js';
355
+
356
+ export default async function myPlugin(fastify, opts) {
357
+ fastify.get('/api/plugins/my-plugin/hello', async (req, reply) => {
358
+ const settings = getPluginSettings('my-plugin');
359
+ return { message: `Hello from ${settings.greeting}` };
360
+ });
361
+ }
362
+ ```
363
+
364
+ #### `config.js`
365
+
366
+ Export a plain object with default settings. These are deep-merged with any user overrides stored in
367
+ `config/plugins.json`:
368
+
369
+ ```js
370
+ export default {
371
+ greeting: 'My Plugin',
372
+ enabled: true
373
+ };
374
+ ```
375
+
376
+ #### Admin View
377
+
378
+ Admin views are ES modules served as static files from `/plugins/`. Import `apiRequest` from the core API client for
379
+ authenticated requests with automatic token refresh:
380
+
381
+ ```js
382
+ import { apiRequest } from '/admin/js/api.js';
383
+
384
+ export const myPluginSettingsView = {
385
+ templateUrl: '/plugins/my-plugin/admin/templates/my-plugin-settings.html',
386
+
387
+ async onMount($container) {
388
+ const settings = await apiRequest('/plugins/my-plugin/settings');
389
+ // populate fields from settings…
390
+
391
+ $container.find('#save-btn').on('click', async () => {
392
+ await apiRequest('/plugins/my-plugin/settings', {
393
+ method: 'PUT',
394
+ body: JSON.stringify({ /* data */ })
395
+ });
396
+ E.toast('Settings saved.', { type: 'success' });
397
+ });
398
+ }
399
+ };
400
+ ```
401
+
402
+ `apiRequest(endpoint, options)` prepends `/api` automatically, handles `Bearer` tokens, and retries once with a
403
+ refreshed token on 401 before redirecting to login.
404
+
405
+ #### Public Injection
406
+
407
+ HTML files listed under `inject` in `plugin.json` are concatenated into `{{headInject}}` and `{{bodyEndInject}}` slots
408
+ in `server/templates/page.html` when the plugin is enabled.
409
+
410
+ ---
411
+
412
+ ## Scripts Reference
413
+
414
+ | npm script | make target | Description |
415
+ |----------------------|-------------------|---------------------------------------------------------------------|
416
+ | `npm run dev` | `make dev` | Start server with `--watch` (auto-restart on save) |
417
+ | `npm start` | `make start` | Start server via pm2 in cluster mode (one process per CPU core) |
418
+ | — | `make stop` | Stop the pm2 process |
419
+ | — | `make restart` | Restart the pm2 process |
420
+ | — | `make logs` | Tail pm2 logs |
421
+ | `npm install` | `make install` | Install dependencies |
422
+ | `npm run setup` | `make setup` | Interactive first-run wizard (admin account, theme, title) |
423
+ | `npm run reset` | `make reset` | Factory reset — wipes content, users, resets config to defaults |
424
+ | `npm run seed` | `make seed` | Seed sample pages and content |
425
+ | `npm run fresh` | `make fresh` | Reset + seed in one step |
426
+ | `npm run copy-domma` | `make copy-domma` | Copy Domma dist from `node_modules` to `public/` |
427
+ | — | `make check` | Syntax-check `bin/cli.js` |
428
+ | — | `make scaffold` | Dry-run the scaffolder, creates `test-site/` locally |
429
+
430
+ ---
431
+
432
+ ## Contributing
433
+
434
+ ### Running Locally
435
+
436
+ ```bash
437
+ git clone https://github.com/pinpointzero73/domma-cms.git
438
+ cd domma-cms
439
+ npm install
440
+ npm run setup
441
+ npm run dev
442
+ ```
443
+
444
+ The server runs on **http://localhost:3050** by default. Change the port in `config/server.json`.
445
+
446
+ ### Conventions
447
+
448
+ - **Backend** — Fastify 5, ESM (`"type": "module"`). Routes in `server/routes/`, services in `server/services/`.
449
+ - **Frontend** — Domma SPA in `admin/`. Views export `{ templateUrl, async onMount($container) }`. Templates live
450
+ alongside views in `admin/js/templates/`.
451
+ - **HTTP** — Always use `apiRequest` from `admin/js/api.js` — never raw `fetch()` — so token refresh is handled
452
+ automatically.
453
+ - **Storage** — Config changes go through `getConfig()` / `saveConfig()` from `server/config.js`. Never write config
454
+ files directly from routes.
455
+ - **Plugins** — Follow the three-file structure. Use `getPluginSettings(name)` to read settings — never read
456
+ `config/plugins.json` directly.
457
+
458
+ ### Adding a New Admin View
459
+
460
+ 1. Create `admin/js/views/my-view.js` exporting `{ templateUrl, onMount }`
461
+ 2. Create `admin/js/templates/my-view.html`
462
+ 3. Register the route in `admin/js/app.js`
463
+ 4. Add a sidebar entry in `admin/js/config/sidebar-config.js` with appropriate role guard
464
+
465
+ ---
466
+
467
+ ## Licence
468
+
469
+ MIT © Darryl Waterhouse