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.
- package/LICENSE +21 -0
- package/README.md +469 -0
- package/admin/css/admin.css +1123 -0
- package/admin/index.html +72 -0
- package/admin/js/api.js +210 -0
- package/admin/js/app.js +270 -0
- package/admin/js/config/sidebar-config.js +107 -0
- package/admin/js/lib/card.js +63 -0
- package/admin/js/lib/image-editor.js +869 -0
- package/admin/js/lib/markdown-toolbar.js +421 -0
- package/admin/js/templates/dashboard.html +50 -0
- package/admin/js/templates/documentation.html +237 -0
- package/admin/js/templates/layouts.html +11 -0
- package/admin/js/templates/login.html +58 -0
- package/admin/js/templates/media.html +16 -0
- package/admin/js/templates/navigation.html +50 -0
- package/admin/js/templates/page-editor.html +126 -0
- package/admin/js/templates/pages.html +18 -0
- package/admin/js/templates/plugins.html +12 -0
- package/admin/js/templates/settings.html +190 -0
- package/admin/js/templates/tutorials.html +233 -0
- package/admin/js/templates/user-editor.html +12 -0
- package/admin/js/templates/users.html +10 -0
- package/admin/js/views/dashboard.js +48 -0
- package/admin/js/views/documentation.js +12 -0
- package/admin/js/views/index.js +33 -0
- package/admin/js/views/layouts.js +49 -0
- package/admin/js/views/login.js +254 -0
- package/admin/js/views/media.js +240 -0
- package/admin/js/views/navigation.js +152 -0
- package/admin/js/views/page-editor.js +479 -0
- package/admin/js/views/pages.js +64 -0
- package/admin/js/views/plugins.js +100 -0
- package/admin/js/views/settings.js +64 -0
- package/admin/js/views/tutorials.js +12 -0
- package/admin/js/views/user-editor.js +88 -0
- package/admin/js/views/users.js +73 -0
- package/bin/cli.js +334 -0
- package/config/auth.json +20 -0
- package/config/content.json +10 -0
- package/config/navigation.json +63 -0
- package/config/plugins.json +47 -0
- package/config/presets.json +34 -0
- package/config/server.json +6 -0
- package/config/site.json +33 -0
- package/package.json +67 -0
- package/plugins/back-to-top/admin/templates/back-to-top-settings.html +55 -0
- package/plugins/back-to-top/admin/views/back-to-top-settings.js +44 -0
- package/plugins/back-to-top/config.js +10 -0
- package/plugins/back-to-top/plugin.js +24 -0
- package/plugins/back-to-top/plugin.json +36 -0
- package/plugins/back-to-top/public/inject-body.html +105 -0
- package/plugins/cookie-consent/admin/templates/cookie-consent-settings.html +113 -0
- package/plugins/cookie-consent/admin/views/cookie-consent-settings.js +73 -0
- package/plugins/cookie-consent/config.js +30 -0
- package/plugins/cookie-consent/plugin.js +24 -0
- package/plugins/cookie-consent/plugin.json +36 -0
- package/plugins/cookie-consent/public/inject-body.html +69 -0
- package/plugins/custom-css/admin/templates/custom-css.html +17 -0
- package/plugins/custom-css/admin/views/custom-css.js +35 -0
- package/plugins/custom-css/config.js +1 -0
- package/plugins/custom-css/data/custom.css +0 -0
- package/plugins/custom-css/plugin.js +63 -0
- package/plugins/custom-css/plugin.json +32 -0
- package/plugins/custom-css/public/inject-head.html +1 -0
- package/plugins/domma-effects/admin/templates/domma-effects.html +488 -0
- package/plugins/domma-effects/admin/views/domma-effects.js +56 -0
- package/plugins/domma-effects/config.js +9 -0
- package/plugins/domma-effects/plugin.js +22 -0
- package/plugins/domma-effects/plugin.json +36 -0
- package/plugins/domma-effects/public/celebrations/core/canvas.js +111 -0
- package/plugins/domma-effects/public/celebrations/core/particles.js +144 -0
- package/plugins/domma-effects/public/celebrations/core/physics.js +166 -0
- package/plugins/domma-effects/public/celebrations/index.js +535 -0
- package/plugins/domma-effects/public/celebrations/themes/christmas.js +1805 -0
- package/plugins/domma-effects/public/celebrations/themes/guy-fawkes.js +1477 -0
- package/plugins/domma-effects/public/celebrations/themes/halloween.js +1837 -0
- package/plugins/domma-effects/public/celebrations/themes/st-andrews.js +1175 -0
- package/plugins/domma-effects/public/celebrations/themes/st-davids.js +1258 -0
- package/plugins/domma-effects/public/celebrations/themes/st-georges.js +1754 -0
- package/plugins/domma-effects/public/celebrations/themes/st-patricks.js +1290 -0
- package/plugins/domma-effects/public/celebrations/themes/valentines.js +1361 -0
- package/plugins/domma-effects/public/inject-body.html +268 -0
- package/plugins/example-analytics/admin/templates/analytics.html +10 -0
- package/plugins/example-analytics/admin/views/analytics.js +51 -0
- package/plugins/example-analytics/config.js +6 -0
- package/plugins/example-analytics/plugin.js +58 -0
- package/plugins/example-analytics/plugin.json +27 -0
- package/plugins/example-analytics/public/inject-body.html +13 -0
- package/plugins/example-analytics/public/inject-head.html +1 -0
- package/plugins/example-analytics/stats.json +1 -0
- package/plugins/form-builder/admin/templates/form-editor.html +158 -0
- package/plugins/form-builder/admin/templates/form-settings.html +29 -0
- package/plugins/form-builder/admin/templates/form-submissions.html +30 -0
- package/plugins/form-builder/admin/templates/forms-list.html +17 -0
- package/plugins/form-builder/admin/views/form-editor.js +817 -0
- package/plugins/form-builder/admin/views/form-settings.js +38 -0
- package/plugins/form-builder/admin/views/form-submissions.js +295 -0
- package/plugins/form-builder/admin/views/forms-list.js +164 -0
- package/plugins/form-builder/config.js +9 -0
- package/plugins/form-builder/data/forms/contact-details.json +63 -0
- package/plugins/form-builder/data/forms/contact.json +52 -0
- package/plugins/form-builder/data/submissions/contact-details.json +1 -0
- package/plugins/form-builder/data/submissions/contact.json +14 -0
- package/plugins/form-builder/email.js +103 -0
- package/plugins/form-builder/plugin.js +454 -0
- package/plugins/form-builder/plugin.json +56 -0
- package/plugins/form-builder/public/inject-body.html +270 -0
- package/plugins/form-builder/public/inject-head.html +42 -0
- package/public/css/site.css +189 -0
- package/public/js/site.js +109 -0
- package/scripts/copy-domma.js +48 -0
- package/scripts/fresh.js +41 -0
- package/scripts/reset.js +124 -0
- package/scripts/seed.js +666 -0
- package/scripts/setup.js +263 -0
- package/server/config.js +56 -0
- package/server/middleware/auth.js +97 -0
- package/server/routes/api/auth.js +116 -0
- package/server/routes/api/layouts.js +25 -0
- package/server/routes/api/media.js +93 -0
- package/server/routes/api/navigation.js +37 -0
- package/server/routes/api/pages.js +118 -0
- package/server/routes/api/plugins.js +46 -0
- package/server/routes/api/settings.js +25 -0
- package/server/routes/api/users.js +110 -0
- package/server/routes/public.js +108 -0
- package/server/server.js +169 -0
- package/server/services/content.js +298 -0
- package/server/services/images.js +334 -0
- package/server/services/markdown.js +297 -0
- package/server/services/plugins.js +246 -0
- package/server/services/renderer.js +80 -0
- package/server/services/users.js +212 -0
- 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
|
+
[](https://nodejs.org)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](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
|