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,227 +0,0 @@
1
- /**
2
- * User Types Service
3
- * Preset Collection — seeds roles on first startup, caches them in memory.
4
- * Auth middleware calls getRoleMap() / getPermissionsFor() at request time.
5
- */
6
- import fs from 'fs/promises';
7
- import path from 'path';
8
- import {v4 as uuidv4} from 'uuid';
9
- import {config} from '../config.js';
10
-
11
- const COLLECTIONS_DIR = path.resolve(config.content.collectionsDir);
12
- const SLUG = 'user-types';
13
- const DIR = path.join(COLLECTIONS_DIR, SLUG);
14
- const SCHEMA_PATH = path.join(DIR, 'schema.json');
15
- const DATA_PATH = path.join(DIR, 'data.json');
16
-
17
- export const RESOURCES = ['pages', 'settings', 'navigation', 'layouts', 'media', 'users', 'plugins', 'collections', 'views', 'actions', 'blocks'];
18
- export const ACTIONS = ['read', 'create', 'update', 'delete'];
19
-
20
- const PRESET_SCHEMA = {
21
- slug: SLUG,
22
- title: 'User Types',
23
- description: 'CMS role definitions — managed by the system.',
24
- preset: true,
25
- fields: [
26
- {name: 'name', label: 'Name (slug)', type: 'text', required: true},
27
- {name: 'label', label: 'Label', type: 'text', required: true},
28
- {name: 'level', label: 'Level', type: 'number', required: true},
29
- {
30
- name: 'permissions',
31
- label: 'Permissions',
32
- type: 'multi-select',
33
- options: RESOURCES.flatMap(r => [r, ...ACTIONS.map(a => `${r}.${a}`)])
34
- },
35
- {
36
- name: 'badgeClass', label: 'Badge Class', type: 'select',
37
- options: ['badge-danger', 'badge-warning', 'badge-info', 'badge-secondary', 'badge-success', 'badge-primary']
38
- }
39
- ],
40
- api: {
41
- create: {enabled: false, access: 'admin'},
42
- read: {enabled: false, access: 'admin'},
43
- update: {enabled: false, access: 'admin'},
44
- delete: {enabled: false, access: 'admin'}
45
- }
46
- };
47
-
48
- const SEED_ENTRIES = [
49
- {name: 'admin', label: 'Admin', level: 0, permissions: RESOURCES, badgeClass: 'badge-danger'},
50
- {
51
- name: 'manager',
52
- label: 'Manager',
53
- level: 1,
54
- permissions: ['pages', 'settings', 'navigation', 'layouts', 'media', 'users', 'collections', 'views', 'actions'],
55
- badgeClass: 'badge-warning'
56
- },
57
- {
58
- name: 'editor',
59
- label: 'Editor',
60
- level: 2,
61
- permissions: ['pages.read', 'pages.create', 'pages.update', 'media'],
62
- badgeClass: 'badge-info'
63
- },
64
- {name: 'subscriber', label: 'Subscriber', level: 3, permissions: [], badgeClass: 'badge-secondary'}
65
- ];
66
-
67
- // ---------------------------------------------------------------------------
68
- // In-memory cache
69
- // ---------------------------------------------------------------------------
70
-
71
- /** @type {Map<string,{label:string,level:number,badgeClass:string}>} */
72
- let roleMap = new Map();
73
-
74
- /** @type {Map<string,string[]>} resource (and resource.action) → role names */
75
- let permissionsMap = new Map();
76
-
77
- /** @type {Map<string,string[]>} role name → raw permissions array */
78
- let rawPermissionsMap = new Map();
79
-
80
- /**
81
- * Build in-memory maps from an array of data entries.
82
- * Supports both bare resource names ('pages') and dotted action strings ('pages.read').
83
- * Bare names expand to all four actions for backward compatibility.
84
- *
85
- * @param {object[]} entries
86
- */
87
- function buildCache(entries) {
88
- roleMap = new Map();
89
- permissionsMap = new Map();
90
- rawPermissionsMap = new Map();
91
-
92
- const addTo = (key, role) => {
93
- if (!permissionsMap.has(key)) permissionsMap.set(key, []);
94
- if (!permissionsMap.get(key).includes(role)) permissionsMap.get(key).push(role);
95
- };
96
-
97
- for (const entry of entries) {
98
- const d = entry.data;
99
- roleMap.set(d.name, {label: d.label, level: d.level, badgeClass: d.badgeClass || ''});
100
- rawPermissionsMap.set(d.name, d.permissions || []);
101
- for (const perm of (d.permissions || [])) {
102
- if (perm.includes('.')) {
103
- const [res] = perm.split('.');
104
- addTo(perm, d.name); // 'pages.read' → [role]
105
- addTo(res, d.name); // 'pages' → [role] (any-action union)
106
- } else {
107
- addTo(perm, d.name); // 'pages' (bare) → [role]
108
- for (const action of ACTIONS) {
109
- addTo(`${perm}.${action}`, d.name); // expand to all actions
110
- }
111
- }
112
- }
113
- }
114
- }
115
-
116
- // ---------------------------------------------------------------------------
117
- // Public API
118
- // ---------------------------------------------------------------------------
119
-
120
- /**
121
- * Seed the preset collection on first startup (no-op if data.json already exists).
122
- *
123
- * @returns {Promise<void>}
124
- */
125
- export async function seed() {
126
- await fs.mkdir(DIR, {recursive: true});
127
-
128
- // Always write schema (overwrite to keep in sync with code)
129
- await fs.writeFile(SCHEMA_PATH, JSON.stringify(PRESET_SCHEMA, null, 2) + '\n', 'utf8');
130
-
131
- // Only write data if it doesn't exist yet
132
- try {
133
- await fs.access(DATA_PATH);
134
- } catch {
135
- const entries = SEED_ENTRIES.map(data => ({
136
- id: uuidv4(),
137
- data,
138
- createdAt: new Date().toISOString(),
139
- updatedAt: new Date().toISOString()
140
- }));
141
- await fs.writeFile(DATA_PATH, JSON.stringify(entries, null, 2) + '\n', 'utf8');
142
- }
143
- }
144
-
145
- /**
146
- * Load the collection from disk into the in-memory cache.
147
- *
148
- * @returns {Promise<void>}
149
- */
150
- export async function load() {
151
- try {
152
- const raw = await fs.readFile(DATA_PATH, 'utf8');
153
- const entries = JSON.parse(raw);
154
- buildCache(entries);
155
- } catch (err) {
156
- console.warn('[userTypes] Failed to load user-types collection:', err.message);
157
- }
158
- }
159
-
160
- /**
161
- * Reload from disk — call after any CRUD on the user-types collection.
162
- *
163
- * @returns {Promise<void>}
164
- */
165
- export async function invalidate() {
166
- await load();
167
- }
168
-
169
- /**
170
- * Return the full role map.
171
- *
172
- * @returns {Map<string,{label:string,level:number,badgeClass:string}>}
173
- */
174
- export function getRoleMap() {
175
- return roleMap;
176
- }
177
-
178
- /**
179
- * Return the level for a named role, or Infinity if not found.
180
- *
181
- * @param {string} roleName
182
- * @returns {number}
183
- */
184
- export function getRoleLevel(roleName) {
185
- return roleMap.get(roleName)?.level ?? Infinity;
186
- }
187
-
188
- /**
189
- * Return the role names allowed to access a resource (and optional action).
190
- * - getPermissionsFor('pages') → roles with ANY action on pages (backward compat)
191
- * - getPermissionsFor('pages', 'delete')→ roles with delete on pages
192
- * - getPermissionsFor('pages.delete') → same as above (dot-notation shorthand)
193
- *
194
- * @param {string} resource - Resource key, or 'resource.action' dot notation
195
- * @param {string} [action] - Optional action (read | create | update | delete)
196
- * @returns {string[]}
197
- */
198
- export function getPermissionsFor(resource, action) {
199
- if (action) {
200
- return permissionsMap.get(`${resource}.${action}`) ?? [];
201
- }
202
- if (resource.includes('.')) {
203
- return permissionsMap.get(resource) ?? [];
204
- }
205
- return permissionsMap.get(resource) ?? [];
206
- }
207
-
208
- /**
209
- * Return the raw permissions array for a role — used by the /api/auth/permissions endpoint.
210
- *
211
- * @param {string} roleName
212
- * @returns {string[]}
213
- */
214
- export function getPermissionsForRole(roleName) {
215
- return rawPermissionsMap.get(roleName) ?? [];
216
- }
217
-
218
- /**
219
- * Return role names ordered from most to least privileged.
220
- *
221
- * @returns {string[]}
222
- */
223
- export function getRoleHierarchy() {
224
- return [...roleMap.entries()]
225
- .sort((a, b) => a[1].level - b[1].level)
226
- .map(([key]) => key);
227
- }