domma-cms 0.9.10 → 0.12.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 (125) hide show
  1. package/CLAUDE.md +248 -159
  2. package/admin/css/admin.css +1 -1
  3. package/admin/js/api.js +1 -1
  4. package/admin/js/app.js +7 -3
  5. package/admin/js/config/sidebar-config.js +1 -1
  6. package/admin/js/http-interceptor.js +1 -0
  7. package/admin/js/lib/card-builder.js +2 -2
  8. package/admin/js/lib/markdown-toolbar.js +5 -5
  9. package/admin/js/lib/safe-html.js +1 -0
  10. package/admin/js/lib/shortcode-modal.js +1 -0
  11. package/admin/js/templates/layouts.html +5 -4
  12. package/admin/js/templates/notifications.html +14 -0
  13. package/admin/js/templates/plugin-marketplace.html +16 -0
  14. package/admin/js/templates/plugins.html +17 -5
  15. package/admin/js/views/index.js +1 -1
  16. package/admin/js/views/layouts.js +1 -16
  17. package/admin/js/views/notifications.js +1 -0
  18. package/admin/js/views/page-editor.js +37 -33
  19. package/admin/js/views/plugin-marketplace.js +1 -0
  20. package/admin/js/views/plugins.js +16 -16
  21. package/config/navigation.json +5 -72
  22. package/config/plugins.json +10 -14
  23. package/config/presets.json +50 -13
  24. package/config/site.json +11 -63
  25. package/package.json +2 -1
  26. package/plugins/_template/admin/templates/index.html +17 -0
  27. package/plugins/_template/admin/views/index.js +19 -0
  28. package/plugins/_template/config.js +8 -0
  29. package/plugins/_template/plugin.js +23 -0
  30. package/plugins/_template/plugin.json +34 -0
  31. package/plugins/analytics/plugin.json +41 -31
  32. package/plugins/blog/admin/templates/blog.html +22 -0
  33. package/plugins/blog/admin/templates/categories.html +7 -0
  34. package/plugins/blog/admin/templates/comments.html +11 -0
  35. package/plugins/blog/admin/templates/post-editor.html +97 -0
  36. package/plugins/blog/admin/templates/settings.html +11 -0
  37. package/plugins/blog/admin/views/blog.js +183 -0
  38. package/plugins/blog/admin/views/categories.js +235 -0
  39. package/plugins/blog/admin/views/comments.js +187 -0
  40. package/plugins/blog/admin/views/post-editor.js +291 -0
  41. package/plugins/blog/admin/views/settings.js +100 -0
  42. package/plugins/blog/collections/categories/schema.json +12 -0
  43. package/plugins/blog/collections/comments/schema.json +16 -0
  44. package/plugins/blog/collections/posts/schema.json +19 -0
  45. package/plugins/blog/config.js +8 -0
  46. package/plugins/blog/plugin.js +352 -0
  47. package/plugins/blog/plugin.json +96 -0
  48. package/plugins/blog/roles/blog-author.json +10 -0
  49. package/plugins/blog/roles/blog-editor.json +12 -0
  50. package/plugins/blog/templates/author.html +9 -0
  51. package/plugins/blog/templates/category.html +9 -0
  52. package/plugins/blog/templates/index.html +9 -0
  53. package/plugins/blog/templates/post.html +17 -0
  54. package/plugins/blog/templates/tag.html +9 -0
  55. package/plugins/contacts/collections/user-contact-groups/schema.json +1 -1
  56. package/plugins/contacts/collections/user-contacts/schema.json +1 -1
  57. package/plugins/contacts/plugin.js +4 -10
  58. package/plugins/contacts/plugin.json +13 -3
  59. package/plugins/notes/collections/user-notes/schema.json +1 -1
  60. package/plugins/notes/plugin.js +3 -9
  61. package/plugins/notes/plugin.json +13 -3
  62. package/plugins/site-search/plugin.json +5 -2
  63. package/plugins/theme-switcher/plugin.json +1 -1
  64. package/plugins/todo/collections/todos/schema.json +1 -1
  65. package/plugins/todo/plugin.js +3 -9
  66. package/plugins/todo/plugin.json +13 -3
  67. package/public/css/site.css +1 -1
  68. package/public/js/site.js +1 -1
  69. package/scripts/build.js +48 -0
  70. package/scripts/create-plugin.js +113 -0
  71. package/scripts/fresh.js +6 -7
  72. package/scripts/gen-instance-secret.js +46 -0
  73. package/scripts/reset.js +3 -3
  74. package/scripts/setup.js +31 -13
  75. package/server/middleware/auth.js +48 -0
  76. package/server/middleware/managerAuth.js +36 -0
  77. package/server/routes/api/actions.js +1 -1
  78. package/server/routes/api/auth.js +4 -3
  79. package/server/routes/api/layouts.js +173 -49
  80. package/server/routes/api/notifications.js +155 -0
  81. package/server/routes/api/plugin-marketplace.js +75 -0
  82. package/server/routes/api/users.js +1 -1
  83. package/server/routes/api/views.js +1 -1
  84. package/server/routes/public.js +4 -9
  85. package/server/server.js +32 -3
  86. package/server/services/actions.js +1 -1
  87. package/server/services/managerClient.js +182 -0
  88. package/server/services/markdown.js +76 -9
  89. package/server/services/permissionRegistry.js +245 -173
  90. package/server/services/pluginInstaller.js +301 -0
  91. package/server/services/plugins.js +117 -10
  92. package/server/services/presetCollections.js +66 -251
  93. package/server/services/renderer.js +99 -0
  94. package/server/services/roles.js +191 -39
  95. package/server/services/users.js +1 -1
  96. package/server/services/views.js +1 -1
  97. package/server/templates/page.html +2 -2
  98. package/plugins/docs/admin/templates/docs.html +0 -69
  99. package/plugins/docs/admin/views/docs.js +0 -276
  100. package/plugins/docs/config.js +0 -8
  101. package/plugins/docs/data/documents/57e003f0-68f2-47dc-9c36-ed4b10ed3deb.json +0 -11
  102. package/plugins/docs/data/folders.json +0 -9
  103. package/plugins/docs/data/templates.json +0 -1
  104. package/plugins/docs/data/versions/57e003f0-68f2-47dc-9c36-ed4b10ed3deb/1.json +0 -5
  105. package/plugins/docs/plugin.js +0 -375
  106. package/plugins/docs/plugin.json +0 -23
  107. package/plugins/form-builder/data/forms/contacts.json +0 -66
  108. package/plugins/form-builder/data/forms/enquiries.json +0 -103
  109. package/plugins/form-builder/data/forms/feedback.json +0 -131
  110. package/plugins/form-builder/data/forms/notes.json +0 -79
  111. package/plugins/form-builder/data/forms/to-do.json +0 -100
  112. package/plugins/form-builder/data/submissions/contacts.json +0 -1
  113. package/plugins/form-builder/data/submissions/enquiries.json +0 -1
  114. package/plugins/form-builder/data/submissions/feedback.json +0 -1
  115. package/plugins/form-builder/data/submissions/notes.json +0 -1
  116. package/plugins/form-builder/data/submissions/to-do.json +0 -1
  117. package/plugins/garage/admin/templates/garage.html +0 -111
  118. package/plugins/garage/admin/views/garage.js +0 -622
  119. package/plugins/garage/collections/garage-vehicles/schema.json +0 -101
  120. package/plugins/garage/config.js +0 -18
  121. package/plugins/garage/data/vehicles.json +0 -70
  122. package/plugins/garage/plugin.js +0 -398
  123. package/plugins/garage/plugin.json +0 -33
  124. package/scripts/seed.js +0 -1996
  125. package/server/services/userTypes.js +0 -227
@@ -1,173 +1,245 @@
1
- /**
2
- * Permission Registry
3
- * Single source of truth for all resource/action definitions in the CMS.
4
- * Adding a new resource or custom action is a one-file change here.
5
- */
6
-
7
- /** @type {ReadonlyArray<{key:string,label:string,description:string,icon:string,group:string,actions:{key:string,label:string,description:string}[]}>} */
8
- export const REGISTRY = Object.freeze([
9
- {
10
- key: 'pages',
11
- label: 'Pages',
12
- description: 'Manage site pages and their content.',
13
- icon: 'file-text',
14
- group: 'Content',
15
- actions: [
16
- {key: 'read', label: 'View', description: 'View pages and their content'},
17
- {key: 'create', label: 'Create', description: 'Create new pages'},
18
- {key: 'update', label: 'Edit', description: 'Edit existing pages'},
19
- {key: 'delete', label: 'Delete', description: 'Delete pages'}
20
- ]
21
- },
22
- {
23
- key: 'media',
24
- label: 'Media',
25
- description: 'Upload and manage media files.',
26
- icon: 'image',
27
- group: 'Content',
28
- actions: [
29
- {key: 'read', label: 'View', description: 'View media library'},
30
- {key: 'create', label: 'Upload', description: 'Upload new files'},
31
- {key: 'update', label: 'Edit', description: 'Rename or update file metadata'},
32
- {key: 'delete', label: 'Delete', description: 'Delete media files'}
33
- ]
34
- },
35
- {
36
- key: 'blocks',
37
- label: 'Blocks',
38
- description: 'Manage reusable content blocks.',
39
- icon: 'box',
40
- group: 'Content',
41
- actions: [
42
- {key: 'read', label: 'View', description: 'View content blocks'},
43
- {key: 'create', label: 'Create', description: 'Create new blocks'},
44
- {key: 'update', label: 'Edit', description: 'Edit existing blocks'},
45
- {key: 'delete', label: 'Delete', description: 'Delete blocks'}
46
- ]
47
- },
48
- {
49
- key: 'navigation',
50
- label: 'Navigation',
51
- description: 'Configure site navigation menus.',
52
- icon: 'menu',
53
- group: 'Structure',
54
- actions: [
55
- {key: 'read', label: 'View', description: 'View navigation menus'},
56
- {key: 'create', label: 'Create', description: 'Create new menu items'},
57
- {key: 'update', label: 'Edit', description: 'Edit navigation structure'},
58
- {key: 'delete', label: 'Delete', description: 'Remove menu items'}
59
- ]
60
- },
61
- {
62
- key: 'layouts',
63
- label: 'Layouts',
64
- description: 'Manage page layouts and templates.',
65
- icon: 'layout',
66
- group: 'Structure',
67
- actions: [
68
- {key: 'read', label: 'View', description: 'View layouts'},
69
- {key: 'create', label: 'Create', description: 'Create new layouts'},
70
- {key: 'update', label: 'Edit', description: 'Edit layouts'},
71
- {key: 'delete', label: 'Delete', description: 'Delete layouts'}
72
- ]
73
- },
74
- {
75
- key: 'collections',
76
- label: 'Collections',
77
- description: 'Manage data collections and entries.',
78
- icon: 'database',
79
- group: 'Data',
80
- actions: [
81
- {key: 'read', label: 'View', description: 'View collection entries'},
82
- {key: 'create', label: 'Create', description: 'Create new entries'},
83
- {key: 'update', label: 'Edit', description: 'Edit existing entries'},
84
- {key: 'delete', label: 'Delete', description: 'Delete entries'}
85
- ]
86
- },
87
- {
88
- key: 'views',
89
- label: 'Views',
90
- description: 'Create and manage data views.',
91
- icon: 'eye',
92
- group: 'Data',
93
- actions: [
94
- {key: 'read', label: 'View', description: 'View data views'},
95
- {key: 'create', label: 'Create', description: 'Create new views'},
96
- {key: 'update', label: 'Edit', description: 'Edit views'},
97
- {key: 'delete', label: 'Delete', description: 'Delete views'}
98
- ]
99
- },
100
- {
101
- key: 'actions',
102
- label: 'Actions',
103
- description: 'Configure automated actions.',
104
- icon: 'zap',
105
- group: 'Data',
106
- actions: [
107
- {key: 'read', label: 'View', description: 'View automated actions'},
108
- {key: 'create', label: 'Create', description: 'Create new actions'},
109
- {key: 'update', label: 'Edit', description: 'Edit actions'},
110
- {key: 'delete', label: 'Delete', description: 'Delete actions'}
111
- ]
112
- },
113
- {
114
- key: 'users',
115
- label: 'Users',
116
- description: 'Manage user accounts.',
117
- icon: 'users',
118
- group: 'Configuration',
119
- actions: [
120
- {key: 'read', label: 'View', description: 'View user accounts'},
121
- {key: 'create', label: 'Create', description: 'Create new users'},
122
- {key: 'update', label: 'Edit', description: 'Edit user accounts'},
123
- {key: 'delete', label: 'Delete', description: 'Delete users'}
124
- ]
125
- },
126
- {
127
- key: 'settings',
128
- label: 'Settings',
129
- description: 'Configure site settings.',
130
- icon: 'settings',
131
- group: 'Configuration',
132
- actions: [
133
- {key: 'read', label: 'View', description: 'View site settings'},
134
- {key: 'create', label: 'Create', description: 'Add new settings'},
135
- {key: 'update', label: 'Edit', description: 'Modify settings'},
136
- {key: 'delete', label: 'Delete', description: 'Remove settings'}
137
- ]
138
- },
139
- {
140
- key: 'plugins',
141
- label: 'Plugins',
142
- description: 'Manage CMS plugins.',
143
- icon: 'package',
144
- group: 'Configuration',
145
- actions: [
146
- {key: 'read', label: 'View', description: 'View installed plugins'},
147
- {key: 'create', label: 'Install', description: 'Install new plugins'},
148
- {key: 'update', label: 'Configure', description: 'Configure plugin settings'},
149
- {key: 'delete', label: 'Remove', description: 'Remove plugins'}
150
- ]
151
- }
152
- ]);
153
-
154
- /** Derived array of resource key strings — mirrors the old RESOURCES constant. */
155
- export const RESOURCES = REGISTRY.map(r => r.key);
156
-
157
- /** Default CRUD action keys — mirrors the old ACTIONS constant. */
158
- export const ACTIONS = ['read', 'create', 'update', 'delete'];
159
-
160
- /** Display order for permission groups. */
161
- export const GROUP_ORDER = ['Content', 'Structure', 'Data', 'Configuration'];
162
-
163
- /**
164
- * Return the action keys defined for a given resource.
165
- * Falls back to the default CRUD set if the resource is not in the registry.
166
- *
167
- * @param {string} resourceKey
168
- * @returns {string[]}
169
- */
170
- export function getActionsForResource(resourceKey) {
171
- const resource = REGISTRY.find(r => r.key === resourceKey);
172
- return resource ? resource.actions.map(a => a.key) : [...ACTIONS];
173
- }
1
+ /**
2
+ * Permission Registry
3
+ * Single source of truth for all resource/action definitions in the CMS.
4
+ * Adding a new resource or custom action is a one-file change here.
5
+ *
6
+ * Plugin-contributed resources live in _pluginContributed and are merged at
7
+ * call time via getEffectiveRegistry(). The base REGISTRY is never mutated.
8
+ */
9
+
10
+ /** @type {Array<{key:string,label:string,description:string,icon:string,group:string,plugin?:string,actions:{key:string,label:string,description:string}[]}>} */
11
+ export const REGISTRY = [
12
+ {
13
+ key: 'pages',
14
+ label: 'Pages',
15
+ description: 'Manage site pages and their content.',
16
+ icon: 'file-text',
17
+ group: 'Content',
18
+ actions: [
19
+ {key: 'read', label: 'View', description: 'View pages and their content'},
20
+ {key: 'create', label: 'Create', description: 'Create new pages'},
21
+ {key: 'update', label: 'Edit', description: 'Edit existing pages'},
22
+ {key: 'delete', label: 'Delete', description: 'Delete pages'}
23
+ ]
24
+ },
25
+ {
26
+ key: 'media',
27
+ label: 'Media',
28
+ description: 'Upload and manage media files.',
29
+ icon: 'image',
30
+ group: 'Content',
31
+ actions: [
32
+ {key: 'read', label: 'View', description: 'View media library'},
33
+ {key: 'create', label: 'Upload', description: 'Upload new files'},
34
+ {key: 'update', label: 'Edit', description: 'Rename or update file metadata'},
35
+ {key: 'delete', label: 'Delete', description: 'Delete media files'}
36
+ ]
37
+ },
38
+ {
39
+ key: 'blocks',
40
+ label: 'Blocks',
41
+ description: 'Manage reusable content blocks.',
42
+ icon: 'box',
43
+ group: 'Content',
44
+ actions: [
45
+ {key: 'read', label: 'View', description: 'View content blocks'},
46
+ {key: 'create', label: 'Create', description: 'Create new blocks'},
47
+ {key: 'update', label: 'Edit', description: 'Edit existing blocks'},
48
+ {key: 'delete', label: 'Delete', description: 'Delete blocks'}
49
+ ]
50
+ },
51
+ {
52
+ key: 'navigation',
53
+ label: 'Navigation',
54
+ description: 'Configure site navigation menus.',
55
+ icon: 'menu',
56
+ group: 'Structure',
57
+ actions: [
58
+ {key: 'read', label: 'View', description: 'View navigation menus'},
59
+ {key: 'create', label: 'Create', description: 'Create new menu items'},
60
+ {key: 'update', label: 'Edit', description: 'Edit navigation structure'},
61
+ {key: 'delete', label: 'Delete', description: 'Remove menu items'}
62
+ ]
63
+ },
64
+ {
65
+ key: 'layouts',
66
+ label: 'Layouts',
67
+ description: 'Manage page layouts and templates.',
68
+ icon: 'layout',
69
+ group: 'Structure',
70
+ actions: [
71
+ {key: 'read', label: 'View', description: 'View layouts'},
72
+ {key: 'create', label: 'Create', description: 'Create new layouts'},
73
+ {key: 'update', label: 'Edit', description: 'Edit layouts'},
74
+ {key: 'delete', label: 'Delete', description: 'Delete layouts'}
75
+ ]
76
+ },
77
+ {
78
+ key: 'collections',
79
+ label: 'Collections',
80
+ description: 'Manage data collections and entries.',
81
+ icon: 'database',
82
+ group: 'Data',
83
+ actions: [
84
+ {key: 'read', label: 'View', description: 'View collection entries'},
85
+ {key: 'create', label: 'Create', description: 'Create new entries'},
86
+ {key: 'update', label: 'Edit', description: 'Edit existing entries'},
87
+ {key: 'delete', label: 'Delete', description: 'Delete entries'}
88
+ ]
89
+ },
90
+ {
91
+ key: 'views',
92
+ label: 'Views',
93
+ description: 'Create and manage data views.',
94
+ icon: 'eye',
95
+ group: 'Data',
96
+ actions: [
97
+ {key: 'read', label: 'View', description: 'View data views'},
98
+ {key: 'create', label: 'Create', description: 'Create new views'},
99
+ {key: 'update', label: 'Edit', description: 'Edit views'},
100
+ {key: 'delete', label: 'Delete', description: 'Delete views'}
101
+ ]
102
+ },
103
+ {
104
+ key: 'actions',
105
+ label: 'Actions',
106
+ description: 'Configure automated actions.',
107
+ icon: 'zap',
108
+ group: 'Data',
109
+ actions: [
110
+ {key: 'read', label: 'View', description: 'View automated actions'},
111
+ {key: 'create', label: 'Create', description: 'Create new actions'},
112
+ {key: 'update', label: 'Edit', description: 'Edit actions'},
113
+ {key: 'delete', label: 'Delete', description: 'Delete actions'}
114
+ ]
115
+ },
116
+ {
117
+ key: 'users',
118
+ label: 'Users',
119
+ description: 'Manage user accounts.',
120
+ icon: 'users',
121
+ group: 'Configuration',
122
+ actions: [
123
+ {key: 'read', label: 'View', description: 'View user accounts'},
124
+ {key: 'create', label: 'Create', description: 'Create new users'},
125
+ {key: 'update', label: 'Edit', description: 'Edit user accounts'},
126
+ {key: 'delete', label: 'Delete', description: 'Delete users'}
127
+ ]
128
+ },
129
+ {
130
+ key: 'settings',
131
+ label: 'Settings',
132
+ description: 'Configure site settings.',
133
+ icon: 'settings',
134
+ group: 'Configuration',
135
+ actions: [
136
+ {key: 'read', label: 'View', description: 'View site settings'},
137
+ {key: 'create', label: 'Create', description: 'Add new settings'},
138
+ {key: 'update', label: 'Edit', description: 'Modify settings'},
139
+ {key: 'delete', label: 'Delete', description: 'Remove settings'}
140
+ ]
141
+ },
142
+ {
143
+ key: 'plugins',
144
+ label: 'Plugins',
145
+ description: 'Manage CMS plugins.',
146
+ icon: 'package',
147
+ group: 'Configuration',
148
+ actions: [
149
+ {key: 'read', label: 'View', description: 'View installed plugins'},
150
+ {key: 'create', label: 'Install', description: 'Install new plugins'},
151
+ {key: 'update', label: 'Configure', description: 'Configure plugin settings'},
152
+ {key: 'delete', label: 'Remove', description: 'Remove plugins'}
153
+ ]
154
+ },
155
+ {
156
+ key: 'notifications',
157
+ label: 'Notifications',
158
+ description: 'View and manage system notifications.',
159
+ icon: 'bell',
160
+ group: 'Configuration',
161
+ actions: [
162
+ {key: 'read', label: 'View', description: 'View notifications'},
163
+ {key: 'update', label: 'Dismiss', description: 'Mark notifications as read'},
164
+ {key: 'delete', label: 'Clear', description: 'Delete notifications'}
165
+ ]
166
+ }
167
+ ];
168
+
169
+ /**
170
+ * Plugin-contributed resources registered at runtime via registerPluginResource().
171
+ * Cleared on teardown via unregisterPluginResourcesByPlugin().
172
+ *
173
+ * @type {Array<{key:string,label:string,description:string,icon:string,group:string,plugin:string,actions:{key:string,label:string,description:string}[]}>}
174
+ */
175
+ const _pluginContributed = [];
176
+
177
+ /**
178
+ * Register a resource contributed by a plugin.
179
+ * Idempotent — if the key already exists in the effective registry it is skipped.
180
+ *
181
+ * @param {{key:string,label:string,description?:string,icon?:string,group?:string,actions?:{key:string,label:string,description?:string}[]}} resource
182
+ * @param {string} plugin - Plugin name that owns this resource
183
+ */
184
+ export function registerPluginResource(resource, plugin) {
185
+ const effective = getEffectiveRegistry();
186
+ if (effective.some(r => r.key === resource.key)) return;
187
+
188
+ _pluginContributed.push({
189
+ key: resource.key,
190
+ label: resource.label,
191
+ description: resource.description || `${resource.label} (${plugin} plugin)`,
192
+ icon: resource.icon || 'box',
193
+ group: resource.group || 'Plugins',
194
+ plugin,
195
+ actions: resource.actions?.map(a => ({
196
+ key: a.key,
197
+ label: a.label,
198
+ description: a.description || a.label
199
+ })) ?? ACTIONS.map(key => ({key, label: key, description: key}))
200
+ });
201
+ }
202
+
203
+ /**
204
+ * Remove all resources registered by a given plugin.
205
+ *
206
+ * @param {string} plugin - Plugin name
207
+ */
208
+ export function unregisterPluginResourcesByPlugin(plugin) {
209
+ let i = _pluginContributed.length;
210
+ while (i--) {
211
+ if (_pluginContributed[i].plugin === plugin) {
212
+ _pluginContributed.splice(i, 1);
213
+ }
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Return the combined registry: base resources + all plugin-contributed resources.
219
+ *
220
+ * @returns {typeof REGISTRY}
221
+ */
222
+ export function getEffectiveRegistry() {
223
+ return [...REGISTRY, ..._pluginContributed];
224
+ }
225
+
226
+ /** Derived array of base resource key strings. */
227
+ export const RESOURCES = REGISTRY.map(r => r.key);
228
+
229
+ /** Default CRUD action keys. */
230
+ export const ACTIONS = ['read', 'create', 'update', 'delete'];
231
+
232
+ /** Display order for permission groups. */
233
+ export const GROUP_ORDER = ['Content', 'Structure', 'Data', 'Configuration', 'Plugins'];
234
+
235
+ /**
236
+ * Return the action keys defined for a given resource (base or plugin-contributed).
237
+ * Falls back to the default CRUD set if the resource is not found.
238
+ *
239
+ * @param {string} resourceKey
240
+ * @returns {string[]}
241
+ */
242
+ export function getActionsForResource(resourceKey) {
243
+ const resource = getEffectiveRegistry().find(r => r.key === resourceKey);
244
+ return resource ? resource.actions.map(a => a.key) : [...ACTIONS];
245
+ }