domma-cms 0.2.1 → 0.5.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 (166) hide show
  1. package/README.md +3 -3
  2. package/admin/css/admin.css +1 -1200
  3. package/admin/dist/domma/domma-tools.css +2313 -0
  4. package/admin/dist/domma/domma-tools.min.js +10 -0
  5. package/admin/index.html +4 -0
  6. package/admin/js/api.js +1 -242
  7. package/admin/js/app.js +9 -279
  8. package/admin/js/config/sidebar-config.js +1 -115
  9. package/admin/js/lib/card.js +1 -63
  10. package/admin/js/lib/image-editor.js +1 -869
  11. package/admin/js/lib/markdown-toolbar.js +54 -421
  12. package/admin/js/templates/action-editor.html +171 -0
  13. package/admin/js/templates/actions-list.html +19 -0
  14. package/admin/js/templates/api-reference.html +1411 -0
  15. package/admin/js/templates/block-editor.html +158 -0
  16. package/admin/js/templates/blocks.html +8 -0
  17. package/admin/js/templates/collection-editor.html +47 -0
  18. package/admin/js/templates/collection-entries.html +3 -0
  19. package/admin/js/templates/collections.html +51 -4
  20. package/admin/js/templates/documentation.html +258 -0
  21. package/admin/js/templates/form-editor.html +238 -0
  22. package/{plugins/form-builder/admin → admin/js}/templates/form-submissions.html +30 -30
  23. package/{plugins/form-builder/admin/templates/forms-list.html → admin/js/templates/forms.html} +17 -17
  24. package/admin/js/templates/layouts.html +44 -7
  25. package/admin/js/templates/login.html +29 -4
  26. package/admin/js/templates/my-profile.html +17 -0
  27. package/admin/js/templates/page-editor.html +48 -0
  28. package/admin/js/templates/pages.html +6 -1
  29. package/admin/js/templates/pro-docs.html +259 -0
  30. package/admin/js/templates/role-editor.html +59 -0
  31. package/admin/js/templates/roles.html +10 -0
  32. package/admin/js/templates/settings.html +137 -18
  33. package/admin/js/templates/tutorials.html +81 -0
  34. package/admin/js/templates/user-editor.html +7 -0
  35. package/admin/js/templates/users.html +3 -1
  36. package/admin/js/templates/view-editor.html +201 -0
  37. package/admin/js/templates/view-preview.html +51 -0
  38. package/admin/js/templates/views-list.html +19 -0
  39. package/admin/js/views/action-editor.js +1 -0
  40. package/admin/js/views/actions-list.js +1 -0
  41. package/admin/js/views/api-reference.js +1 -0
  42. package/admin/js/views/block-editor.js +8 -0
  43. package/admin/js/views/blocks.js +4 -0
  44. package/admin/js/views/collection-editor.js +3 -487
  45. package/admin/js/views/collection-entries.js +1 -484
  46. package/admin/js/views/collections.js +1 -153
  47. package/admin/js/views/dashboard.js +1 -56
  48. package/admin/js/views/documentation.js +1 -12
  49. package/admin/js/views/form-editor.js +8 -0
  50. package/admin/js/views/form-submissions.js +1 -0
  51. package/admin/js/views/forms.js +1 -0
  52. package/admin/js/views/index.js +1 -39
  53. package/admin/js/views/layouts.js +9 -42
  54. package/admin/js/views/login.js +7 -251
  55. package/admin/js/views/media.js +1 -240
  56. package/admin/js/views/my-profile.js +1 -0
  57. package/admin/js/views/navigation.js +14 -212
  58. package/admin/js/views/page-editor.js +72 -661
  59. package/admin/js/views/pages.js +5 -72
  60. package/admin/js/views/plugins.js +13 -90
  61. package/admin/js/views/pro-docs.js +1 -0
  62. package/admin/js/views/role-editor.js +1 -0
  63. package/admin/js/views/roles.js +4 -0
  64. package/admin/js/views/settings.js +3 -199
  65. package/admin/js/views/tutorials.js +1 -12
  66. package/admin/js/views/user-editor.js +1 -88
  67. package/admin/js/views/users.js +4 -76
  68. package/admin/js/views/view-editor.js +1 -0
  69. package/admin/js/views/view-preview.js +1 -0
  70. package/admin/js/views/views-list.js +1 -0
  71. package/bin/cli.js +1 -1
  72. package/config/auth.json +2 -17
  73. package/config/connections.json.bak +9 -0
  74. package/config/connections.json.example +9 -0
  75. package/config/navigation.json +15 -0
  76. package/config/plugins.json +19 -29
  77. package/config/server.json +6 -6
  78. package/config/site.json +17 -6
  79. package/package.json +24 -10
  80. package/plugins/domma-effects/public/celebrations/core/canvas.js +2 -104
  81. package/plugins/domma-effects/public/celebrations/core/particles.js +1 -144
  82. package/plugins/domma-effects/public/celebrations/core/physics.js +1 -166
  83. package/plugins/domma-effects/public/celebrations/index.js +1 -535
  84. package/plugins/domma-effects/public/celebrations/themes/christmas.js +1 -1805
  85. package/plugins/domma-effects/public/celebrations/themes/guy-fawkes.js +1 -1477
  86. package/plugins/domma-effects/public/celebrations/themes/halloween.js +1 -1837
  87. package/plugins/domma-effects/public/celebrations/themes/st-andrews.js +1 -1175
  88. package/plugins/domma-effects/public/celebrations/themes/st-davids.js +1 -1258
  89. package/plugins/domma-effects/public/celebrations/themes/st-georges.js +1 -1754
  90. package/plugins/domma-effects/public/celebrations/themes/st-patricks.js +1 -1290
  91. package/plugins/domma-effects/public/celebrations/themes/valentines.js +1 -1361
  92. package/plugins/example-analytics/stats.json +21 -12
  93. package/plugins/theme-roller/admin/templates/theme-roller.html +71 -0
  94. package/plugins/theme-roller/admin/views/theme-roller-view.js +403 -0
  95. package/plugins/theme-roller/config.js +1 -0
  96. package/plugins/theme-roller/plugin.js +233 -0
  97. package/plugins/theme-roller/plugin.json +31 -0
  98. package/plugins/theme-roller/public/active-theme.css +0 -0
  99. package/plugins/theme-roller/public/inject-head-late.html +1 -0
  100. package/public/css/forms.css +1 -0
  101. package/public/css/site.css +1 -302
  102. package/public/js/btt.js +1 -90
  103. package/public/js/cookie-consent.js +1 -61
  104. package/public/js/form-logic-engine.js +1 -0
  105. package/public/js/forms.js +1 -0
  106. package/public/js/site.js +1 -204
  107. package/scripts/build.js +194 -129
  108. package/scripts/pro.js +254 -0
  109. package/scripts/reset.js +33 -8
  110. package/scripts/seed.js +343 -78
  111. package/scripts/setup.js +5 -4
  112. package/server/middleware/auth.js +136 -97
  113. package/server/routes/api/actions.js +200 -0
  114. package/server/routes/api/auth.js +292 -116
  115. package/server/routes/api/blocks.js +84 -0
  116. package/server/routes/api/collections.js +88 -23
  117. package/{plugins/form-builder/plugin.js → server/routes/api/forms.js} +483 -505
  118. package/server/routes/api/layouts.js +49 -25
  119. package/server/routes/api/media.js +118 -93
  120. package/server/routes/api/navigation.js +40 -37
  121. package/server/routes/api/pages.js +132 -118
  122. package/server/routes/api/plugins.js +6 -3
  123. package/server/routes/api/settings.js +104 -89
  124. package/server/routes/api/users.js +27 -21
  125. package/server/routes/api/views.js +148 -0
  126. package/server/routes/public.js +124 -108
  127. package/server/server.js +269 -173
  128. package/server/services/actions.js +387 -0
  129. package/server/services/adapterRegistry.js +98 -0
  130. package/server/services/adapters/FileAdapter.js +192 -0
  131. package/server/services/adapters/MongoAdapter.js +220 -0
  132. package/server/services/blocks.js +162 -0
  133. package/server/services/collections.js +74 -86
  134. package/server/services/connectionManager.js +102 -0
  135. package/server/services/content.js +312 -307
  136. package/{plugins/form-builder → server/services}/email.js +126 -103
  137. package/server/services/forms.js +173 -0
  138. package/server/services/markdown.js +1378 -648
  139. package/server/services/permissionRegistry.js +173 -0
  140. package/server/services/presetCollections.js +251 -0
  141. package/server/services/renderer.js +75 -1
  142. package/server/services/roles.js +227 -0
  143. package/server/services/rowAccess.js +104 -0
  144. package/server/services/userProfiles.js +199 -0
  145. package/server/services/users.js +281 -212
  146. package/server/services/views.js +280 -0
  147. package/server/templates/page.html +119 -113
  148. package/plugins/form-builder/admin/templates/form-editor.html +0 -171
  149. package/plugins/form-builder/admin/templates/form-settings.html +0 -29
  150. package/plugins/form-builder/admin/views/form-editor.js +0 -1442
  151. package/plugins/form-builder/admin/views/form-settings.js +0 -38
  152. package/plugins/form-builder/admin/views/form-submissions.js +0 -295
  153. package/plugins/form-builder/admin/views/forms-list.js +0 -164
  154. package/plugins/form-builder/config.js +0 -9
  155. package/plugins/form-builder/data/forms/consent.json +0 -104
  156. package/plugins/form-builder/data/forms/contact-details.json +0 -63
  157. package/plugins/form-builder/data/forms/contacts.json +0 -66
  158. package/plugins/form-builder/data/submissions/consent.json +0 -13
  159. package/plugins/form-builder/data/submissions/contact-details.json +0 -1
  160. package/plugins/form-builder/data/submissions/contacts.json +0 -26
  161. package/plugins/form-builder/plugin.json +0 -52
  162. package/plugins/form-builder/public/form-logic-engine.js +0 -568
  163. package/plugins/form-builder/public/inject-body.html +0 -352
  164. package/plugins/form-builder/public/inject-head.html +0 -58
  165. package/plugins/form-builder/public/package.json +0 -1
  166. package/scripts/copy-domma.js +0 -48
@@ -1,108 +1,124 @@
1
- /**
2
- * Public Site Routes
3
- * Catch-all that resolves URL paths to Markdown pages and renders them server-side.
4
- * Draft pages are not served publicly.
5
- * The admin panel is excluded (handled by static serving).
6
- */
7
- import {getPage} from '../services/content.js';
8
- import {renderPage} from '../services/renderer.js';
9
- import {config} from '../config.js';
10
-
11
- export async function publicRoutes(fastify) {
12
- // Admin panel: serve index.html for all /admin/* paths (SPA fallback)
13
- fastify.get('/admin', async (request, reply) => {
14
- return reply.redirect('/admin/');
15
- });
16
-
17
- // Health check
18
- fastify.get('/api/health', async () => ({ status: 'ok' }));
19
-
20
- // Public pages catch-all
21
- fastify.get('/*', async (request, reply) => {
22
- const rawPath = request.params['*'];
23
-
24
- // Skip non-page paths (assets handled by static plugin)
25
- if (rawPath.includes('.')) {
26
- return reply.callNotFound();
27
- }
28
-
29
- const urlPath = '/' + (rawPath || '');
30
-
31
- // Try exact path first, then with /index suffix for directory-style URLs
32
- let page = await getPage(urlPath);
33
-
34
- // Try fetching index of a directory path
35
- if (!page && !urlPath.endsWith('/index')) {
36
- page = await getPage(urlPath.replace(/\/$/, '') || '/');
37
- }
38
-
39
- if (!page) {
40
- reply.status(404);
41
- return reply.type('text/html').send(await render404(urlPath));
42
- }
43
-
44
- // Don't serve draft pages publicly
45
- if (page.status !== 'published') {
46
- reply.status(404);
47
- return reply.type('text/html').send(await render404(urlPath));
48
- }
49
-
50
- // Enforce page visibility
51
- if (page.visibility && page.visibility !== 'public') {
52
- let userRole = null;
53
- try {
54
- const decoded = await request.jwtVerify();
55
- userRole = decoded.role;
56
- } catch { /* no token — treat as unauthenticated */
57
- }
58
-
59
- const roles = config.auth.roles;
60
- const userLevel = roles[userRole]?.level ?? Infinity;
61
- const requiredLevel = roles[page.visibility]?.level ?? 0;
62
-
63
- if (userLevel > requiredLevel) {
64
- reply.status(403);
65
- return reply.type('text/html').send(accessDeniedHtml(urlPath));
66
- }
67
- }
68
-
69
- const html = await renderPage(page);
70
- return reply.type('text/html').send(html);
71
- });
72
- }
73
-
74
- /**
75
- * Render a 404 response — tries content/pages/404.md first, falls back to
76
- * a minimal inline page so the site theme is applied when possible.
77
- */
78
- async function render404(urlPath) {
79
- try {
80
- const page404 = await getPage('/404');
81
- if (page404 && page404.status === 'published') {
82
- return await renderPage(page404);
83
- }
84
- } catch { /* fall through */ }
85
- return notFoundHtml(urlPath);
86
- }
87
-
88
- function accessDeniedHtml(urlPath) {
89
- return `<!DOCTYPE html>
90
- <html lang="en-GB">
91
- <head><meta charset="UTF-8"><title>403 Access Denied</title>
92
- <style>body{font-family:system-ui,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0;background:#111;color:#eee;}
93
- .box{text-align:center;} h1{font-size:6rem;margin:0;color:#666;} p{color:#999;}</style>
94
- </head>
95
- <body><div class="box"><h1>403</h1><p>You don't have permission to view <code>${urlPath}</code></p><p><a href="/" style="color:#aaa">Go home</a></p></div></body>
96
- </html>`;
97
- }
98
-
99
- function notFoundHtml(urlPath) {
100
- return `<!DOCTYPE html>
101
- <html lang="en-GB">
102
- <head><meta charset="UTF-8"><title>404 Not Found</title>
103
- <style>body{font-family:system-ui,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0;background:#111;color:#eee;}
104
- .box{text-align:center;} h1{font-size:6rem;margin:0;color:#666;} p{color:#999;}</style>
105
- </head>
106
- <body><div class="box"><h1>404</h1><p>No page found at <code>${urlPath}</code></p><p><a href="/" style="color:#aaa">Go home</a></p></div></body>
107
- </html>`;
108
- }
1
+ /**
2
+ * Public Site Routes
3
+ * Catch-all that resolves URL paths to Markdown pages and renders them server-side.
4
+ * Draft pages are not served publicly.
5
+ * The admin panel is excluded (handled by static serving).
6
+ */
7
+ import {getPage} from '../services/content.js';
8
+ import {renderPage} from '../services/renderer.js';
9
+ import {getRoleLevel} from '../services/roles.js';
10
+
11
+ /**
12
+ * Escape user-controlled strings before interpolating into HTML.
13
+ * Prevents reflected XSS in error pages.
14
+ *
15
+ * @param {string} str
16
+ * @returns {string}
17
+ */
18
+ function escapeHtml(str) {
19
+ return String(str)
20
+ .replace(/&/g, '&amp;')
21
+ .replace(/</g, '&lt;')
22
+ .replace(/>/g, '&gt;')
23
+ .replace(/"/g, '&quot;')
24
+ .replace(/'/g, '&#39;');
25
+ }
26
+
27
+ export async function publicRoutes(fastify) {
28
+ // Admin panel: serve index.html for all /admin/* paths (SPA fallback)
29
+ fastify.get('/admin', async (request, reply) => {
30
+ return reply.redirect('/admin/');
31
+ });
32
+
33
+ // Health check
34
+ fastify.get('/api/health', async () => ({ status: 'ok' }));
35
+
36
+ // Public pages catch-all
37
+ fastify.get('/*', async (request, reply) => {
38
+ const rawPath = request.params['*'];
39
+
40
+ // Skip non-page paths (assets handled by static plugin)
41
+ if (rawPath.includes('.')) {
42
+ return reply.callNotFound();
43
+ }
44
+
45
+ const urlPath = '/' + (rawPath || '');
46
+
47
+ // Try exact path first, then with /index suffix for directory-style URLs
48
+ let page = await getPage(urlPath);
49
+
50
+ // Try fetching index of a directory path
51
+ if (!page && !urlPath.endsWith('/index')) {
52
+ page = await getPage(urlPath.replace(/\/$/, '') || '/');
53
+ }
54
+
55
+ if (!page) {
56
+ reply.status(404);
57
+ return reply.type('text/html').send(await render404(urlPath));
58
+ }
59
+
60
+ // Don't serve draft pages publicly
61
+ if (page.status !== 'published') {
62
+ reply.status(404);
63
+ return reply.type('text/html').send(await render404(urlPath));
64
+ }
65
+
66
+ // Enforce page visibility
67
+ if (page.visibility && page.visibility !== 'public') {
68
+ let userRole = null;
69
+ try {
70
+ const decoded = await request.jwtVerify();
71
+ userRole = decoded.role;
72
+ } catch { /* no token — treat as unauthenticated */
73
+ }
74
+
75
+ const userLevel = getRoleLevel(userRole);
76
+ const visibilityLevel = getRoleLevel(page.visibility);
77
+ const requiredLevel = visibilityLevel === Infinity ? 0 : visibilityLevel;
78
+
79
+ if (userLevel > requiredLevel) {
80
+ reply.status(403);
81
+ return reply.type('text/html').send(accessDeniedHtml(urlPath));
82
+ }
83
+ }
84
+
85
+ const html = await renderPage(page);
86
+ return reply.type('text/html').send(html);
87
+ });
88
+ }
89
+
90
+ /**
91
+ * Render a 404 response — tries content/pages/404.md first, falls back to
92
+ * a minimal inline page so the site theme is applied when possible.
93
+ */
94
+ async function render404(urlPath) {
95
+ try {
96
+ const page404 = await getPage('/404');
97
+ if (page404 && page404.status === 'published') {
98
+ return await renderPage(page404);
99
+ }
100
+ } catch { /* fall through */ }
101
+ return notFoundHtml(urlPath);
102
+ }
103
+
104
+ function accessDeniedHtml(urlPath) {
105
+ return `<!DOCTYPE html>
106
+ <html lang="en-GB">
107
+ <head><meta charset="UTF-8"><title>403 Access Denied</title>
108
+ <style>body{font-family:system-ui,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0;background:#111;color:#eee;}
109
+ .box{text-align:center;} h1{font-size:6rem;margin:0;color:#666;} p{color:#999;}</style>
110
+ </head>
111
+ <body><div class="box"><h1>403</h1><p>You don't have permission to view <code>${escapeHtml(urlPath)}</code></p><p><a href="/" style="color:#aaa">Go home</a></p></div></body>
112
+ </html>`;
113
+ }
114
+
115
+ function notFoundHtml(urlPath) {
116
+ return `<!DOCTYPE html>
117
+ <html lang="en-GB">
118
+ <head><meta charset="UTF-8"><title>404 Not Found</title>
119
+ <style>body{font-family:system-ui,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0;background:#111;color:#eee;}
120
+ .box{text-align:center;} h1{font-size:6rem;margin:0;color:#666;} p{color:#999;}</style>
121
+ </head>
122
+ <body><div class="box"><h1>404</h1><p>No page found at <code>${escapeHtml(urlPath)}</code></p><p><a href="/" style="color:#aaa">Go home</a></p></div></body>
123
+ </html>`;
124
+ }