domma-cms 0.24.0 → 0.25.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/CLAUDE.md +5 -0
- package/admin/js/api.js +1 -1
- package/admin/js/app.js +2 -2
- package/admin/js/lib/crud-tutorial.js +1 -1
- package/admin/js/templates/api-endpoint-editor.html +120 -0
- package/admin/js/templates/api-endpoints.html +13 -0
- package/admin/js/views/api-endpoint-editor.js +1 -0
- package/admin/js/views/api-endpoints.js +7 -0
- package/admin/js/views/index.js +1 -1
- package/admin/js/views/project-detail.js +1 -1
- package/config/menus/admin-sidebar.json +7 -1
- package/package.json +1 -1
- package/server/routes/api/api-endpoints.js +96 -0
- package/server/routes/api/collections.js +2 -2
- package/server/routes/api/endpoints-public.js +88 -0
- package/server/server.js +8 -0
- package/server/services/apiEndpoints.js +402 -0
- package/server/services/apiTokens.js +15 -1
- package/server/services/permissionRegistry.js +13 -0
- package/server/services/presetCollections.js +29 -0
- package/server/services/projects.js +18 -2
- package/server/services/scaffolder.js +24 -1
- package/server/services/sidebar-migration.js +1 -0
|
@@ -337,7 +337,7 @@ export async function getProjectForPage(urlPath, explicitProject) {
|
|
|
337
337
|
export async function getArtefactsForProject(projectSlug) {
|
|
338
338
|
const out = {
|
|
339
339
|
pages: [], collections: [], forms: [], actions: [],
|
|
340
|
-
menus: [], blocks: [], views: [], roles: [], users: []
|
|
340
|
+
menus: [], blocks: [], views: [], roles: [], users: [], apis: []
|
|
341
341
|
};
|
|
342
342
|
|
|
343
343
|
try {
|
|
@@ -411,6 +411,17 @@ export async function getArtefactsForProject(projectSlug) {
|
|
|
411
411
|
}
|
|
412
412
|
} catch { /* skip */ }
|
|
413
413
|
|
|
414
|
+
try {
|
|
415
|
+
// Custom API endpoints store their project in data.project (required
|
|
416
|
+
// field — no untagged-→core fallback applies, so match directly).
|
|
417
|
+
const {entries} = await listEntries('api-endpoints', {limit: 0});
|
|
418
|
+
for (const e of entries) {
|
|
419
|
+
if (e.data?.project === projectSlug) {
|
|
420
|
+
out.apis.push({id: e.id, name: e.data.name, path: e.data.path, collection: e.data.collection});
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
} catch { /* skip */ }
|
|
424
|
+
|
|
414
425
|
return out;
|
|
415
426
|
}
|
|
416
427
|
|
|
@@ -436,7 +447,7 @@ export async function untagAllForProject(projectSlug) {
|
|
|
436
447
|
}
|
|
437
448
|
const counts = {
|
|
438
449
|
pages: 0, collections: 0, forms: 0, actions: 0,
|
|
439
|
-
menus: 0, blocks: 0, views: 0, roles: 0, users: 0
|
|
450
|
+
menus: 0, blocks: 0, views: 0, roles: 0, users: 0, apis: 0
|
|
440
451
|
};
|
|
441
452
|
const grouped = await getArtefactsForProject(projectSlug);
|
|
442
453
|
|
|
@@ -516,6 +527,11 @@ export async function untagAllForProject(projectSlug) {
|
|
|
516
527
|
// plumbing to land first; pages need frontmatter rewriting which is a
|
|
517
528
|
// separate concern. Counts remain 0 for those types in this task; later
|
|
518
529
|
// tasks may revisit.
|
|
530
|
+
//
|
|
531
|
+
// API endpoints: intentionally skipped. An endpoint's project IS its URL
|
|
532
|
+
// namespace (/api/x/<project>/...) — silently untagging would move live
|
|
533
|
+
// endpoints to /api/x/core/... and break external callers. Delete or
|
|
534
|
+
// recreate them explicitly instead.
|
|
519
535
|
|
|
520
536
|
return counts;
|
|
521
537
|
}
|
|
@@ -279,7 +279,7 @@ export async function applyRecipe(recipeSlug, opts = {}) {
|
|
|
279
279
|
throw err;
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
-
const created = { collection: null, form: null, actions: [], roles: [], users: [], menus: [], apiTokens: [] };
|
|
282
|
+
const created = { collection: null, form: null, actions: [], roles: [], users: [], menus: [], apiTokens: [], apiEndpoints: [] };
|
|
283
283
|
const skipped = [];
|
|
284
284
|
const warnings = [];
|
|
285
285
|
|
|
@@ -489,6 +489,29 @@ export async function applyRecipe(recipeSlug, opts = {}) {
|
|
|
489
489
|
}
|
|
490
490
|
}
|
|
491
491
|
|
|
492
|
+
// Custom API endpoints — declared as definition objects (path, collection,
|
|
493
|
+
// filter, ...). Idempotent: an existing (project, path-shape) match is
|
|
494
|
+
// skipped so re-applying a recipe never clobbers a tuned definition.
|
|
495
|
+
const endpointDecls = resolved.apiEndpoints ? [].concat(resolved.apiEndpoints) : [];
|
|
496
|
+
if (endpointDecls.length) {
|
|
497
|
+
const {createEndpoint, findEndpointByPath} = await import('./apiEndpoints.js');
|
|
498
|
+
const epProject = projectSlug || tokens.namespace || 'core';
|
|
499
|
+
for (const ep of endpointDecls) {
|
|
500
|
+
if (!ep.path) continue;
|
|
501
|
+
if (await findEndpointByPath(epProject, ep.path)) {
|
|
502
|
+
skipped.push(`apiEndpoint:${ep.path}`);
|
|
503
|
+
warnings.push(`API endpoint "${ep.path}" already exists for project "${epProject}" — left unchanged`);
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
try {
|
|
507
|
+
await createEndpoint({...ep, project: epProject, createdBy: opts.createdBy || null});
|
|
508
|
+
created.apiEndpoints.push(ep.path);
|
|
509
|
+
} catch (err) {
|
|
510
|
+
warnings.push(`API endpoint "${ep.path}" failed: ${err.message}`);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
492
515
|
const snippet = resolved.snippet || null;
|
|
493
516
|
|
|
494
517
|
return { created, skipped, warnings, snippet };
|
|
@@ -41,6 +41,7 @@ const SEED_ITEMS = [
|
|
|
41
41
|
{text: 'Forms', url: '#/forms', icon: 'layout', permission: 'collections'},
|
|
42
42
|
{text: 'Views', url: '#/views', icon: 'eye', permission: 'views'},
|
|
43
43
|
{text: 'Actions', url: '#/actions', icon: 'zap', permission: 'actions'},
|
|
44
|
+
{text: 'API Builder', url: '#/api-endpoints', icon: 'code', permission: 'api-endpoints'},
|
|
44
45
|
{text: 'Blocks', url: '#/blocks', icon: 'box', permission: 'pages'},
|
|
45
46
|
{text: 'Components', url: '#/components', icon: 'component', permission: 'components'}
|
|
46
47
|
]
|