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
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Cookie Consent Plugin — Default Configuration
3
+ */
4
+ export default {
5
+ // Wording
6
+ message: 'We use cookies to enhance your browsing experience, serve personalised content, and analyse our traffic. By clicking "Accept All", you consent to our use of cookies.',
7
+ acceptAllText: 'Accept All',
8
+ rejectAllText: 'Reject All',
9
+ customizeText: 'Customize',
10
+ savePreferencesText: 'Save Preferences',
11
+
12
+ // Policy links (empty cookiePolicyUrl hides the cookie policy link)
13
+ privacyPolicyText: 'Privacy Policy',
14
+ privacyPolicyUrl: '/privacy-policy',
15
+ cookiePolicyText: 'Cookie Policy',
16
+ cookiePolicyUrl: '',
17
+
18
+ // Appearance
19
+ position: 'bottom',
20
+ layout: 'bar',
21
+ theme: 'dark',
22
+
23
+ // Categories (Necessary is always shown and always required)
24
+ showFunctional: true,
25
+ showAnalytics: true,
26
+ showMarketing: true,
27
+
28
+ // Consent version — increment to re-prompt users who already accepted
29
+ consentVersion: '1.0'
30
+ };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Cookie Consent Plugin — Server
3
+ *
4
+ * Endpoints (prefix: /api/plugins/cookie-consent):
5
+ * GET /settings — public (called by inject-body IIFE)
6
+ * PUT /settings — admin-auth — saves user overrides
7
+ */
8
+ import {getPluginSettings, savePluginState} from '../../server/services/plugins.js';
9
+
10
+ export default async function cookieConsentPlugin(fastify, options) {
11
+ const {authenticate, requireAdmin} = options.auth;
12
+
13
+ // GET /settings — no auth, public read
14
+ fastify.get('/settings', async () => {
15
+ return getPluginSettings('cookie-consent');
16
+ });
17
+
18
+ // PUT /settings — admin only
19
+ fastify.put('/settings', {preHandler: [authenticate, requireAdmin]}, async (request) => {
20
+ const body = request.body || {};
21
+ savePluginState('cookie-consent', {settings: body});
22
+ return {ok: true};
23
+ });
24
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "cookie-consent",
3
+ "displayName": "Cookie Consent",
4
+ "version": "1.0.0",
5
+ "description": "Injects a configurable GDPR cookie consent banner into every public page.",
6
+ "author": "Darryl Waterhouse",
7
+ "date": "2026-03-02",
8
+ "icon": "shield",
9
+ "admin": {
10
+ "sidebar": [
11
+ {
12
+ "id": "cookie-consent",
13
+ "text": "Cookie Consent",
14
+ "icon": "shield",
15
+ "url": "#/plugins/cookie-consent",
16
+ "section": "#/plugins/cookie-consent"
17
+ }
18
+ ],
19
+ "routes": [
20
+ {
21
+ "path": "/plugins/cookie-consent",
22
+ "view": "plugin-cookie-consent-settings",
23
+ "title": "Cookie Consent - Domma CMS"
24
+ }
25
+ ],
26
+ "views": {
27
+ "plugin-cookie-consent-settings": {
28
+ "entry": "cookie-consent/admin/views/cookie-consent-settings.js",
29
+ "exportName": "cookieConsentSettingsView"
30
+ }
31
+ }
32
+ },
33
+ "inject": {
34
+ "bodyEnd": "public/inject-body.html"
35
+ }
36
+ }
@@ -0,0 +1,69 @@
1
+ <script>
2
+ (function () {
3
+ 'use strict';
4
+
5
+ if (!window.Domma || !window.Domma.components || !window.Domma.components.cookieConsent) return;
6
+
7
+ fetch('/api/plugins/cookie-consent/settings')
8
+ .then(function (r) {
9
+ return r.json();
10
+ })
11
+ .then(function (s) {
12
+ var categories = {
13
+ necessary: {
14
+ label: 'Necessary Cookies',
15
+ description: 'These cookies are essential for the website to function properly.',
16
+ required: true
17
+ }
18
+ };
19
+
20
+ if (s.showFunctional !== false) {
21
+ categories.functional = {
22
+ label: 'Functional Cookies',
23
+ description: 'These cookies enable personalised features and functionality.',
24
+ required: false
25
+ };
26
+ }
27
+
28
+ if (s.showAnalytics !== false) {
29
+ categories.analytics = {
30
+ label: 'Analytics Cookies',
31
+ description: 'These cookies help us understand how visitors interact with our website.',
32
+ required: false
33
+ };
34
+ }
35
+
36
+ if (s.showMarketing !== false) {
37
+ categories.marketing = {
38
+ label: 'Marketing Cookies',
39
+ description: 'These cookies are used to deliver relevant ads and marketing campaigns.',
40
+ required: false
41
+ };
42
+ }
43
+
44
+ var cfg = {
45
+ message: s.message,
46
+ acceptAllText: s.acceptAllText,
47
+ rejectAllText: s.rejectAllText,
48
+ customizeText: s.customizeText,
49
+ savePreferencesText: s.savePreferencesText,
50
+ privacyPolicyText: s.privacyPolicyText,
51
+ privacyPolicyUrl: s.privacyPolicyUrl || null,
52
+ cookiePolicyText: s.cookiePolicyText,
53
+ cookiePolicyUrl: s.cookiePolicyUrl || null,
54
+ position: s.position || 'bottom',
55
+ layout: s.layout || 'bar',
56
+ theme: s.theme || 'dark',
57
+ categories: categories,
58
+ consentVersion: s.consentVersion || '1.0',
59
+ autoShow: true,
60
+ animation: true
61
+ };
62
+
63
+ Domma.components.cookieConsent(cfg);
64
+ })
65
+ .catch(function () {
66
+ // Settings fetch failed — don't show banner
67
+ });
68
+ })();
69
+ </script>
@@ -0,0 +1,17 @@
1
+ <div class="view-header">
2
+ <h1><span data-icon="code"></span> Custom CSS</h1>
3
+ <button id="save-css-btn" class="btn btn-primary">
4
+ <span data-icon="save"></span> Save
5
+ </button>
6
+ </div>
7
+
8
+ <div class="card mb-4">
9
+ <div class="card-header"><h2>Editor</h2></div>
10
+ <div class="card-body">
11
+ <textarea id="custom-css-editor" class="form-input" style="font-family:monospace;min-height:420px;resize:vertical;" spellcheck="false" placeholder="/* Add your custom CSS here */"></textarea>
12
+ <p class="form-hint text-muted" style="margin-top:.6rem;font-size:.8rem;">
13
+ This CSS is injected into the <code>&lt;head&gt;</code> of every public page via <code>/api/plugins/custom-css/styles.css</code>.
14
+ Changes take effect immediately on save.
15
+ </p>
16
+ </div>
17
+ </div>
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Custom CSS Plugin — Admin Editor View
3
+ * Allows admins to write CSS injected into every public page.
4
+ */
5
+ import { apiRequest } from '/admin/js/api.js';
6
+
7
+ export const customCssView = {
8
+ templateUrl: '/plugins/custom-css/admin/templates/custom-css.html',
9
+
10
+ async onMount($container) {
11
+ // Load current CSS
12
+ try {
13
+ const { css } = await apiRequest('/plugins/custom-css/');
14
+ $container.find('#custom-css-editor').val(css || '');
15
+ } catch {
16
+ E.toast('Could not load custom CSS.', { type: 'error' });
17
+ }
18
+
19
+ // Save handler
20
+ $container.find('#save-css-btn').off('click').on('click', async () => {
21
+ const css = $container.find('#custom-css-editor').val();
22
+ try {
23
+ await apiRequest('/plugins/custom-css/', {
24
+ method: 'PUT',
25
+ body: JSON.stringify({ css })
26
+ });
27
+ E.toast('Custom CSS saved.', { type: 'success' });
28
+ } catch (err) {
29
+ E.toast(err.message || 'Failed to save CSS.', { type: 'error' });
30
+ }
31
+ });
32
+
33
+ Domma.icons.scan();
34
+ }
35
+ };
@@ -0,0 +1 @@
1
+ export default {};
File without changes
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Custom CSS Plugin — Server
3
+ * Serves admin-authored CSS and injects it into every public page via inject-head.html.
4
+ *
5
+ * Endpoints (prefix: /api/plugins/custom-css):
6
+ * GET /styles.css — public, no auth — serves the current custom CSS file
7
+ * GET / — admin, authenticated — returns { css: "..." }
8
+ * PUT / — admin, requireAdmin — saves request.body.css to data/custom.css
9
+ */
10
+ import fs from 'fs/promises';
11
+ import path from 'path';
12
+ import {fileURLToPath} from 'url';
13
+
14
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
15
+ const CSS_FILE = path.join(__dirname, 'data', 'custom.css');
16
+ const MAX_SIZE = 100 * 1024; // 100 KB
17
+
18
+ async function readCss() {
19
+ try {
20
+ return await fs.readFile(CSS_FILE, 'utf8');
21
+ } catch {
22
+ return '';
23
+ }
24
+ }
25
+
26
+ export default async function customCssPlugin(fastify, options) {
27
+ const { authenticate, requireAdmin } = options.auth;
28
+
29
+ // Ensure data directory exists
30
+ await fs.mkdir(path.join(__dirname, 'data'), { recursive: true });
31
+
32
+ // -----------------------------------------------------------------------
33
+ // GET /styles.css — public, no auth — serve the CSS file
34
+ // -----------------------------------------------------------------------
35
+ fastify.get('/styles.css', async (_request, reply) => {
36
+ const css = await readCss();
37
+ reply.header('Content-Type', 'text/css; charset=utf-8');
38
+ reply.header('Cache-Control', 'no-cache');
39
+ return css;
40
+ });
41
+
42
+ // -----------------------------------------------------------------------
43
+ // GET / — admin — return current CSS content as JSON
44
+ // -----------------------------------------------------------------------
45
+ fastify.get('/', { preHandler: [authenticate] }, async () => {
46
+ return { css: await readCss() };
47
+ });
48
+
49
+ // -----------------------------------------------------------------------
50
+ // PUT / — admin — save new CSS content
51
+ // -----------------------------------------------------------------------
52
+ fastify.put('/', { preHandler: [authenticate, requireAdmin] }, async (request, reply) => {
53
+ const { css } = request.body || {};
54
+ if (typeof css !== 'string') {
55
+ return reply.status(400).send({ error: 'css must be a string.' });
56
+ }
57
+ if (Buffer.byteLength(css, 'utf8') > MAX_SIZE) {
58
+ return reply.status(400).send({ error: 'CSS exceeds the 100 KB limit.' });
59
+ }
60
+ await fs.writeFile(CSS_FILE, css, 'utf8');
61
+ return { ok: true };
62
+ });
63
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "custom-css",
3
+ "displayName": "Custom CSS",
4
+ "version": "1.0.0",
5
+ "description": "Add custom CSS that is injected into every public page.",
6
+ "author": "Darryl Waterhouse",
7
+ "date": "2026-03-03",
8
+ "icon": "code",
9
+ "admin": {
10
+ "sidebar": [
11
+ {
12
+ "id": "custom-css",
13
+ "text": "Custom CSS",
14
+ "icon": "code",
15
+ "url": "#/plugins/custom-css",
16
+ "section": "#/plugins/custom-css"
17
+ }
18
+ ],
19
+ "routes": [
20
+ { "path": "/plugins/custom-css", "view": "plugin-custom-css", "title": "Custom CSS - Domma CMS" }
21
+ ],
22
+ "views": {
23
+ "plugin-custom-css": {
24
+ "entry": "custom-css/admin/views/custom-css.js",
25
+ "exportName": "customCssView"
26
+ }
27
+ }
28
+ },
29
+ "inject": {
30
+ "headLate": "public/inject-head.html"
31
+ }
32
+ }
@@ -0,0 +1 @@
1
+ <link rel="stylesheet" href="/api/plugins/custom-css/styles.css">