domma-cms 0.3.0 → 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.
- package/README.md +3 -3
- package/admin/css/admin.css +1 -1
- package/admin/dist/domma/domma-tools.css +2313 -0
- package/admin/dist/domma/domma-tools.min.js +10 -0
- package/admin/index.html +4 -0
- package/admin/js/api.js +1 -1
- package/admin/js/app.js +8 -4
- package/admin/js/config/sidebar-config.js +1 -1
- package/admin/js/lib/markdown-toolbar.js +18 -10
- package/admin/js/templates/action-editor.html +171 -0
- package/admin/js/templates/actions-list.html +19 -0
- package/admin/js/templates/api-reference.html +1411 -0
- package/admin/js/templates/block-editor.html +158 -0
- package/admin/js/templates/blocks.html +8 -0
- package/admin/js/templates/collection-editor.html +47 -0
- package/admin/js/templates/collection-entries.html +3 -0
- package/admin/js/templates/collections.html +51 -4
- package/admin/js/templates/documentation.html +258 -0
- package/{plugins/form-builder/admin → admin/js}/templates/form-editor.html +238 -199
- package/{plugins/form-builder/admin → admin/js}/templates/form-submissions.html +30 -30
- package/{plugins/form-builder/admin/templates/forms-list.html → admin/js/templates/forms.html} +17 -17
- package/admin/js/templates/login.html +29 -4
- package/admin/js/templates/my-profile.html +17 -0
- package/admin/js/templates/page-editor.html +39 -0
- package/admin/js/templates/pages.html +6 -1
- package/admin/js/templates/pro-docs.html +259 -0
- package/admin/js/templates/role-editor.html +59 -0
- package/admin/js/templates/roles.html +10 -0
- package/admin/js/templates/settings.html +123 -21
- package/admin/js/templates/tutorials.html +81 -0
- package/admin/js/templates/user-editor.html +7 -0
- package/admin/js/templates/users.html +3 -26
- package/admin/js/templates/view-editor.html +201 -0
- package/admin/js/templates/view-preview.html +51 -0
- package/admin/js/templates/views-list.html +19 -0
- package/admin/js/views/action-editor.js +1 -0
- package/admin/js/views/actions-list.js +1 -0
- package/admin/js/views/api-reference.js +1 -0
- package/admin/js/views/block-editor.js +8 -0
- package/admin/js/views/blocks.js +4 -0
- package/admin/js/views/collection-editor.js +3 -3
- package/admin/js/views/collection-entries.js +1 -1
- package/admin/js/views/collections.js +1 -1
- package/admin/js/views/dashboard.js +1 -1
- package/admin/js/views/form-editor.js +8 -0
- package/admin/js/views/form-submissions.js +1 -0
- package/admin/js/views/forms.js +1 -0
- package/admin/js/views/index.js +1 -1
- package/admin/js/views/login.js +2 -2
- package/admin/js/views/media.js +1 -1
- package/admin/js/views/my-profile.js +1 -0
- package/admin/js/views/page-editor.js +34 -15
- package/admin/js/views/pages.js +5 -5
- package/admin/js/views/plugins.js +10 -10
- package/admin/js/views/pro-docs.js +1 -0
- package/admin/js/views/role-editor.js +1 -0
- package/admin/js/views/roles.js +4 -0
- package/admin/js/views/settings.js +3 -1
- package/admin/js/views/user-editor.js +1 -1
- package/admin/js/views/users.js +4 -7
- package/admin/js/views/view-editor.js +1 -0
- package/admin/js/views/view-preview.js +1 -0
- package/admin/js/views/views-list.js +1 -0
- package/bin/cli.js +1 -1
- package/config/auth.json +1 -0
- package/config/connections.json.bak +9 -0
- package/config/connections.json.example +9 -0
- package/config/plugins.json +19 -29
- package/config/server.json +6 -6
- package/config/site.json +12 -2
- package/package.json +24 -10
- package/plugins/example-analytics/stats.json +17 -12
- package/plugins/theme-roller/admin/templates/theme-roller.html +71 -0
- package/plugins/theme-roller/admin/views/theme-roller-view.js +403 -0
- package/plugins/theme-roller/config.js +1 -0
- package/plugins/theme-roller/plugin.js +233 -0
- package/plugins/theme-roller/plugin.json +31 -0
- package/plugins/theme-roller/public/active-theme.css +0 -0
- package/plugins/theme-roller/public/inject-head-late.html +1 -0
- package/public/css/forms.css +1 -0
- package/public/css/site.css +1 -1
- package/public/js/forms.js +1 -0
- package/public/js/site.js +1 -1
- package/scripts/build.js +194 -129
- package/scripts/pro.js +254 -0
- package/scripts/reset.js +33 -8
- package/scripts/seed.js +343 -78
- package/scripts/setup.js +1 -0
- package/server/middleware/auth.js +136 -120
- package/server/routes/api/actions.js +200 -0
- package/server/routes/api/auth.js +292 -146
- package/server/routes/api/blocks.js +84 -0
- package/server/routes/api/collections.js +79 -27
- package/{plugins/form-builder/plugin.js → server/routes/api/forms.js} +483 -505
- package/server/routes/api/layouts.js +49 -39
- package/server/routes/api/media.js +118 -92
- package/server/routes/api/navigation.js +40 -36
- package/server/routes/api/pages.js +132 -118
- package/server/routes/api/plugins.js +6 -3
- package/server/routes/api/settings.js +104 -88
- package/server/routes/api/users.js +27 -19
- package/server/routes/api/views.js +148 -0
- package/server/routes/public.js +124 -108
- package/server/server.js +269 -181
- package/server/services/actions.js +387 -0
- package/server/services/adapterRegistry.js +98 -0
- package/server/services/adapters/FileAdapter.js +192 -0
- package/server/services/adapters/MongoAdapter.js +220 -0
- package/server/services/blocks.js +162 -0
- package/server/services/collections.js +74 -86
- package/server/services/connectionManager.js +102 -0
- package/server/services/content.js +312 -307
- package/server/services/email.js +126 -0
- package/server/services/forms.js +173 -0
- package/server/services/markdown.js +1378 -747
- package/server/services/permissionRegistry.js +173 -0
- package/server/services/presetCollections.js +251 -0
- package/server/services/renderer.js +75 -1
- package/server/services/roles.js +227 -0
- package/server/services/rowAccess.js +104 -0
- package/server/services/userProfiles.js +199 -0
- package/server/services/users.js +281 -212
- package/server/services/views.js +280 -0
- package/server/templates/page.html +119 -113
- package/plugins/form-builder/admin/templates/form-settings.html +0 -29
- package/plugins/form-builder/admin/views/form-editor.js +0 -1444
- package/plugins/form-builder/admin/views/form-settings.js +0 -38
- package/plugins/form-builder/admin/views/form-submissions.js +0 -295
- package/plugins/form-builder/admin/views/forms-list.js +0 -164
- package/plugins/form-builder/config.js +0 -9
- package/plugins/form-builder/data/forms/consent.json +0 -104
- package/plugins/form-builder/data/forms/contact-details.json +0 -99
- package/plugins/form-builder/data/forms/contacts.json +0 -66
- package/plugins/form-builder/data/forms/feedback.json +0 -130
- package/plugins/form-builder/data/submissions/consent.json +0 -13
- package/plugins/form-builder/data/submissions/contact-details.json +0 -1
- package/plugins/form-builder/data/submissions/contacts.json +0 -26
- package/plugins/form-builder/data/submissions/feedback.json +0 -1
- package/plugins/form-builder/plugin.json +0 -52
- package/plugins/form-builder/public/inject-body.html +0 -352
- package/plugins/form-builder/public/inject-head.html +0 -58
- package/plugins/form-builder/public/package.json +0 -1
- package/scripts/copy-domma.js +0 -48
- package/server/services/userTypes.js +0 -167
- /package/{plugins/form-builder/public → public/js}/form-logic-engine.js +0 -0
|
@@ -24,12 +24,27 @@
|
|
|
24
24
|
* DELETE /collections/:slug/public/:id - Delete entry (if api.delete enabled)
|
|
25
25
|
*/
|
|
26
26
|
import {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
clearEntries,
|
|
28
|
+
createCollection,
|
|
29
|
+
createEntry,
|
|
30
|
+
deleteCollection,
|
|
31
|
+
deleteEntry,
|
|
32
|
+
exportEntries,
|
|
33
|
+
getCollection,
|
|
34
|
+
getEntry,
|
|
35
|
+
importEntries,
|
|
36
|
+
listCollections,
|
|
37
|
+
listEntries,
|
|
38
|
+
updateCollection,
|
|
39
|
+
updateEntry
|
|
30
40
|
} from '../../services/collections.js';
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
41
|
+
import {authenticate, requireAdmin, requirePermission} from '../../middleware/auth.js';
|
|
42
|
+
import {getRoleLevel, invalidate as invalidateRoles} from '../../services/roles.js';
|
|
43
|
+
import {getConfig, saveConfig} from '../../config.js';
|
|
44
|
+
import {PRESET_COLLECTION_SLUGS} from '../../services/presetCollections.js';
|
|
45
|
+
import {ensureFormForCollection} from '../../services/forms.js';
|
|
46
|
+
|
|
47
|
+
const ALL_PRESET_SLUGS = new Set(['roles', 'user-profiles', ...PRESET_COLLECTION_SLUGS]);
|
|
33
48
|
|
|
34
49
|
/**
|
|
35
50
|
* Resolve the role level number for a named role.
|
|
@@ -76,34 +91,71 @@ async function checkPublicAccess(schema, operation, request, reply) {
|
|
|
76
91
|
}
|
|
77
92
|
|
|
78
93
|
export async function collectionsRoutes(fastify) {
|
|
79
|
-
const
|
|
94
|
+
const canRead = {preHandler: [authenticate, requirePermission('collections', 'read')]};
|
|
95
|
+
const canCreate = {preHandler: [authenticate, requirePermission('collections', 'create')]};
|
|
96
|
+
const canUpdate = {preHandler: [authenticate, requirePermission('collections', 'update')]};
|
|
97
|
+
const canDelete = {preHandler: [authenticate, requirePermission('collections', 'delete')]};
|
|
80
98
|
|
|
81
99
|
// -------------------------------------------------------------------------
|
|
82
100
|
// Collection CRUD (schema management)
|
|
83
101
|
// -------------------------------------------------------------------------
|
|
84
102
|
|
|
85
|
-
fastify.get('/collections',
|
|
103
|
+
fastify.get('/collections', canRead, async () => {
|
|
86
104
|
return listCollections();
|
|
87
105
|
});
|
|
88
106
|
|
|
89
|
-
fastify.
|
|
90
|
-
|
|
107
|
+
fastify.get('/collections/pro-status', canRead, async () => {
|
|
108
|
+
try {
|
|
109
|
+
const connections = getConfig('connections');
|
|
110
|
+
const names = Object.keys(connections).filter(k => k !== '_comment');
|
|
111
|
+
return {pro: names.length > 0, connections: names};
|
|
112
|
+
} catch {
|
|
113
|
+
return {pro: false, connections: []};
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
fastify.get('/collections/connections', {preHandler: [authenticate, requireAdmin]}, async () => {
|
|
118
|
+
try {
|
|
119
|
+
const {_comment, ...rest} = getConfig('connections');
|
|
120
|
+
return rest;
|
|
121
|
+
} catch {
|
|
122
|
+
return {};
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
fastify.put('/collections/connections', {preHandler: [authenticate, requireAdmin]}, async (request, reply) => {
|
|
127
|
+
const data = request.body;
|
|
128
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
|
129
|
+
return reply.status(400).send({error: 'Invalid connections data'});
|
|
130
|
+
}
|
|
131
|
+
for (const [name, conn] of Object.entries(data)) {
|
|
132
|
+
if (!conn.type || !conn.uri || !conn.database) {
|
|
133
|
+
return reply.status(400).send({error: `Connection "${name}" requires type, uri, and database`});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
saveConfig('connections', data);
|
|
137
|
+
return {success: true};
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
fastify.post('/collections', canCreate, async (request, reply) => {
|
|
141
|
+
const {title, slug, description, fields, api, storage} = request.body || {};
|
|
91
142
|
if (!title) return reply.status(400).send({ error: 'title is required' });
|
|
92
143
|
try {
|
|
93
|
-
|
|
144
|
+
const schema = await createCollection({title, slug, description, fields, api, storage});
|
|
145
|
+
await ensureFormForCollection(schema);
|
|
94
146
|
return reply.status(201).send(schema);
|
|
95
147
|
} catch (err) {
|
|
96
148
|
return reply.status(409).send({ error: err.message });
|
|
97
149
|
}
|
|
98
150
|
});
|
|
99
151
|
|
|
100
|
-
fastify.get('/collections/:slug',
|
|
152
|
+
fastify.get('/collections/:slug', canRead, async (request, reply) => {
|
|
101
153
|
const schema = await getCollection(request.params.slug);
|
|
102
154
|
if (!schema) return reply.status(404).send({ error: 'Collection not found' });
|
|
103
155
|
return schema;
|
|
104
156
|
});
|
|
105
157
|
|
|
106
|
-
fastify.put('/collections/:slug',
|
|
158
|
+
fastify.put('/collections/:slug', canUpdate, async (request, reply) => {
|
|
107
159
|
try {
|
|
108
160
|
return await updateCollection(request.params.slug, request.body || {});
|
|
109
161
|
} catch (err) {
|
|
@@ -111,8 +163,8 @@ export async function collectionsRoutes(fastify) {
|
|
|
111
163
|
}
|
|
112
164
|
});
|
|
113
165
|
|
|
114
|
-
fastify.delete('/collections/:slug',
|
|
115
|
-
if (request.params.slug
|
|
166
|
+
fastify.delete('/collections/:slug', canDelete, async (request, reply) => {
|
|
167
|
+
if (ALL_PRESET_SLUGS.has(request.params.slug)) {
|
|
116
168
|
return reply.status(403).send({ error: 'Cannot delete a preset collection' });
|
|
117
169
|
}
|
|
118
170
|
try {
|
|
@@ -127,7 +179,7 @@ export async function collectionsRoutes(fastify) {
|
|
|
127
179
|
// Entry CRUD
|
|
128
180
|
// -------------------------------------------------------------------------
|
|
129
181
|
|
|
130
|
-
fastify.get('/collections/:slug/entries',
|
|
182
|
+
fastify.get('/collections/:slug/entries', canRead, async (request, reply) => {
|
|
131
183
|
const schema = await getCollection(request.params.slug);
|
|
132
184
|
if (!schema) return reply.status(404).send({ error: 'Collection not found' });
|
|
133
185
|
const { page, limit, sort, order, search } = request.query;
|
|
@@ -140,30 +192,30 @@ export async function collectionsRoutes(fastify) {
|
|
|
140
192
|
});
|
|
141
193
|
});
|
|
142
194
|
|
|
143
|
-
fastify.get('/collections/:slug/entries/:id',
|
|
195
|
+
fastify.get('/collections/:slug/entries/:id', canRead, async (request, reply) => {
|
|
144
196
|
const entry = await getEntry(request.params.slug, request.params.id);
|
|
145
197
|
if (!entry) return reply.status(404).send({ error: 'Entry not found' });
|
|
146
198
|
return entry;
|
|
147
199
|
});
|
|
148
200
|
|
|
149
|
-
fastify.post('/collections/:slug/entries',
|
|
201
|
+
fastify.post('/collections/:slug/entries', canCreate, async (request, reply) => {
|
|
150
202
|
const user = request.user;
|
|
151
203
|
try {
|
|
152
204
|
const entry = await createEntry(request.params.slug, request.body?.data || {}, {
|
|
153
205
|
createdBy: user?.id || null,
|
|
154
206
|
source: 'admin'
|
|
155
207
|
});
|
|
156
|
-
if (request.params.slug === '
|
|
208
|
+
if (request.params.slug === 'roles') await invalidateRoles();
|
|
157
209
|
return reply.status(201).send(entry);
|
|
158
210
|
} catch (err) {
|
|
159
211
|
return reply.status(400).send({ error: err.message });
|
|
160
212
|
}
|
|
161
213
|
});
|
|
162
214
|
|
|
163
|
-
fastify.put('/collections/:slug/entries/:id',
|
|
215
|
+
fastify.put('/collections/:slug/entries/:id', canUpdate, async (request, reply) => {
|
|
164
216
|
try {
|
|
165
217
|
const entry = await updateEntry(request.params.slug, request.params.id, request.body?.data || {});
|
|
166
|
-
if (request.params.slug === '
|
|
218
|
+
if (request.params.slug === 'roles') await invalidateRoles();
|
|
167
219
|
return entry;
|
|
168
220
|
} catch (err) {
|
|
169
221
|
const status = err.message === 'Entry not found' ? 404 : 400;
|
|
@@ -171,16 +223,16 @@ export async function collectionsRoutes(fastify) {
|
|
|
171
223
|
}
|
|
172
224
|
});
|
|
173
225
|
|
|
174
|
-
fastify.delete('/collections/:slug/entries/:id',
|
|
175
|
-
if (request.params.slug === '
|
|
176
|
-
const entry = await getEntry('
|
|
226
|
+
fastify.delete('/collections/:slug/entries/:id', canDelete, async (request, reply) => {
|
|
227
|
+
if (request.params.slug === 'roles') {
|
|
228
|
+
const entry = await getEntry('roles', request.params.id);
|
|
177
229
|
if (entry?.data?.level === 0) {
|
|
178
230
|
return reply.status(403).send({ error: 'Cannot delete the root admin role' });
|
|
179
231
|
}
|
|
180
232
|
}
|
|
181
233
|
try {
|
|
182
234
|
await deleteEntry(request.params.slug, request.params.id);
|
|
183
|
-
if (request.params.slug === '
|
|
235
|
+
if (request.params.slug === 'roles') await invalidateRoles();
|
|
184
236
|
return { success: true };
|
|
185
237
|
} catch (err) {
|
|
186
238
|
return reply.status(404).send({ error: err.message });
|
|
@@ -188,7 +240,7 @@ export async function collectionsRoutes(fastify) {
|
|
|
188
240
|
});
|
|
189
241
|
|
|
190
242
|
// Clear all entries — DELETE /collections/:slug/entries (no :id)
|
|
191
|
-
fastify.delete('/collections/:slug/entries',
|
|
243
|
+
fastify.delete('/collections/:slug/entries', canDelete, async (request, reply) => {
|
|
192
244
|
try {
|
|
193
245
|
await clearEntries(request.params.slug);
|
|
194
246
|
return { success: true };
|
|
@@ -201,7 +253,7 @@ export async function collectionsRoutes(fastify) {
|
|
|
201
253
|
// Export / Import
|
|
202
254
|
// -------------------------------------------------------------------------
|
|
203
255
|
|
|
204
|
-
fastify.get('/collections/:slug/export',
|
|
256
|
+
fastify.get('/collections/:slug/export', canRead, async (request, reply) => {
|
|
205
257
|
const format = request.query.format === 'csv' ? 'csv' : 'json';
|
|
206
258
|
try {
|
|
207
259
|
const output = await exportEntries(request.params.slug, format);
|
|
@@ -218,7 +270,7 @@ export async function collectionsRoutes(fastify) {
|
|
|
218
270
|
}
|
|
219
271
|
});
|
|
220
272
|
|
|
221
|
-
fastify.post('/collections/:slug/import',
|
|
273
|
+
fastify.post('/collections/:slug/import', canCreate, async (request, reply) => {
|
|
222
274
|
const user = request.user;
|
|
223
275
|
const entries = request.body?.entries;
|
|
224
276
|
if (!Array.isArray(entries)) return reply.status(400).send({ error: 'entries must be an array' });
|