@actuate-media/cms-core 0.57.0 → 0.57.1

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 (118) hide show
  1. package/dist/__tests__/api/stats-seo-cache.test.js +1 -1
  2. package/dist/__tests__/api/stats-seo-cache.test.js.map +1 -1
  3. package/dist/api/guarded-db.d.ts +13 -0
  4. package/dist/api/guarded-db.d.ts.map +1 -0
  5. package/dist/api/guarded-db.js +58 -0
  6. package/dist/api/guarded-db.js.map +1 -0
  7. package/dist/api/handler-factory.d.ts.map +1 -1
  8. package/dist/api/handler-factory.js +2 -1
  9. package/dist/api/handler-factory.js.map +1 -1
  10. package/dist/api/handlers.d.ts +5 -3
  11. package/dist/api/handlers.d.ts.map +1 -1
  12. package/dist/api/handlers.js +54 -13068
  13. package/dist/api/handlers.js.map +1 -1
  14. package/dist/api/route-helpers.d.ts +320 -0
  15. package/dist/api/route-helpers.d.ts.map +1 -0
  16. package/dist/api/route-helpers.js +1307 -0
  17. package/dist/api/route-helpers.js.map +1 -0
  18. package/dist/api/routes/ai-seo.d.ts +3 -0
  19. package/dist/api/routes/ai-seo.d.ts.map +1 -0
  20. package/dist/api/routes/ai-seo.js +377 -0
  21. package/dist/api/routes/ai-seo.js.map +1 -0
  22. package/dist/api/routes/ai-settings.d.ts +3 -0
  23. package/dist/api/routes/ai-settings.d.ts.map +1 -0
  24. package/dist/api/routes/ai-settings.js +764 -0
  25. package/dist/api/routes/ai-settings.js.map +1 -0
  26. package/dist/api/routes/auth.d.ts +3 -0
  27. package/dist/api/routes/auth.d.ts.map +1 -0
  28. package/dist/api/routes/auth.js +787 -0
  29. package/dist/api/routes/auth.js.map +1 -0
  30. package/dist/api/routes/collab.d.ts +3 -0
  31. package/dist/api/routes/collab.d.ts.map +1 -0
  32. package/dist/api/routes/collab.js +426 -0
  33. package/dist/api/routes/collab.js.map +1 -0
  34. package/dist/api/routes/dashboard.d.ts +7 -0
  35. package/dist/api/routes/dashboard.d.ts.map +1 -0
  36. package/dist/api/routes/dashboard.js +540 -0
  37. package/dist/api/routes/dashboard.js.map +1 -0
  38. package/dist/api/routes/documents.d.ts +3 -0
  39. package/dist/api/routes/documents.d.ts.map +1 -0
  40. package/dist/api/routes/documents.js +180 -0
  41. package/dist/api/routes/documents.js.map +1 -0
  42. package/dist/api/routes/folders.d.ts +3 -0
  43. package/dist/api/routes/folders.d.ts.map +1 -0
  44. package/dist/api/routes/folders.js +195 -0
  45. package/dist/api/routes/folders.js.map +1 -0
  46. package/dist/api/routes/forms.d.ts +3 -0
  47. package/dist/api/routes/forms.d.ts.map +1 -0
  48. package/dist/api/routes/forms.js +661 -0
  49. package/dist/api/routes/forms.js.map +1 -0
  50. package/dist/api/routes/globals.d.ts +3 -0
  51. package/dist/api/routes/globals.d.ts.map +1 -0
  52. package/dist/api/routes/globals.js +131 -0
  53. package/dist/api/routes/globals.js.map +1 -0
  54. package/dist/api/routes/media.d.ts +3 -0
  55. package/dist/api/routes/media.d.ts.map +1 -0
  56. package/dist/api/routes/media.js +521 -0
  57. package/dist/api/routes/media.js.map +1 -0
  58. package/dist/api/routes/meta.d.ts +3 -0
  59. package/dist/api/routes/meta.d.ts.map +1 -0
  60. package/dist/api/routes/meta.js +96 -0
  61. package/dist/api/routes/meta.js.map +1 -0
  62. package/dist/api/routes/page-templates.d.ts +3 -0
  63. package/dist/api/routes/page-templates.d.ts.map +1 -0
  64. package/dist/api/routes/page-templates.js +265 -0
  65. package/dist/api/routes/page-templates.js.map +1 -0
  66. package/dist/api/routes/presence.d.ts +3 -0
  67. package/dist/api/routes/presence.d.ts.map +1 -0
  68. package/dist/api/routes/presence.js +35 -0
  69. package/dist/api/routes/presence.js.map +1 -0
  70. package/dist/api/routes/preview.d.ts +3 -0
  71. package/dist/api/routes/preview.d.ts.map +1 -0
  72. package/dist/api/routes/preview.js +111 -0
  73. package/dist/api/routes/preview.js.map +1 -0
  74. package/dist/api/routes/redirects.d.ts +3 -0
  75. package/dist/api/routes/redirects.d.ts.map +1 -0
  76. package/dist/api/routes/redirects.js +790 -0
  77. package/dist/api/routes/redirects.js.map +1 -0
  78. package/dist/api/routes/saved-sections.d.ts +3 -0
  79. package/dist/api/routes/saved-sections.d.ts.map +1 -0
  80. package/dist/api/routes/saved-sections.js +1036 -0
  81. package/dist/api/routes/saved-sections.js.map +1 -0
  82. package/dist/api/routes/scheduling.d.ts +3 -0
  83. package/dist/api/routes/scheduling.d.ts.map +1 -0
  84. package/dist/api/routes/scheduling.js +373 -0
  85. package/dist/api/routes/scheduling.js.map +1 -0
  86. package/dist/api/routes/sections.d.ts +3 -0
  87. package/dist/api/routes/sections.d.ts.map +1 -0
  88. package/dist/api/routes/sections.js +500 -0
  89. package/dist/api/routes/sections.js.map +1 -0
  90. package/dist/api/routes/security-center.d.ts +3 -0
  91. package/dist/api/routes/security-center.d.ts.map +1 -0
  92. package/dist/api/routes/security-center.js +71 -0
  93. package/dist/api/routes/security-center.js.map +1 -0
  94. package/dist/api/routes/seo-public.d.ts +14 -0
  95. package/dist/api/routes/seo-public.d.ts.map +1 -0
  96. package/dist/api/routes/seo-public.js +930 -0
  97. package/dist/api/routes/seo-public.js.map +1 -0
  98. package/dist/api/routes/seo.d.ts +8 -0
  99. package/dist/api/routes/seo.d.ts.map +1 -0
  100. package/dist/api/routes/seo.js +848 -0
  101. package/dist/api/routes/seo.js.map +1 -0
  102. package/dist/api/routes/system.d.ts +3 -0
  103. package/dist/api/routes/system.d.ts.map +1 -0
  104. package/dist/api/routes/system.js +340 -0
  105. package/dist/api/routes/system.js.map +1 -0
  106. package/dist/api/routes/url-resolution.d.ts +3 -0
  107. package/dist/api/routes/url-resolution.d.ts.map +1 -0
  108. package/dist/api/routes/url-resolution.js +457 -0
  109. package/dist/api/routes/url-resolution.js.map +1 -0
  110. package/dist/api/routes/users.d.ts +3 -0
  111. package/dist/api/routes/users.d.ts.map +1 -0
  112. package/dist/api/routes/users.js +1162 -0
  113. package/dist/api/routes/users.js.map +1 -0
  114. package/dist/api/routes/webhooks.d.ts +3 -0
  115. package/dist/api/routes/webhooks.d.ts.map +1 -0
  116. package/dist/api/routes/webhooks.js +338 -0
  117. package/dist/api/routes/webhooks.js.map +1 -0
  118. package/package.json +1 -1
@@ -0,0 +1,500 @@
1
+ import { listDocuments, getDocument, createDocument, updateDocument } from '../../actions.js';
2
+ import { logEvent } from '../../security/audit.js';
3
+ import { notifyPreviewUpdate } from '../../preview/event-bus.js';
4
+ import { getSectionType, getSectionTypesForScope, getAllSectionTypes, buildSection, coerceSections, coercePostHeader, createPostTemplate, addSection as addSectionToList, removeSection as removeSectionFromList, reorderSections as reorderSectionList, setSectionVisibility, updateSectionContent as updateSectionContentInList, updateSectionSettings as updateSectionSettingsInList, updateSectionMeta as updateSectionMetaInList, validateSectionContent, } from '../../sections/index.js';
5
+ import { buildPostsOrderBy, buildPostsWhere, extractSeoScore as extractPostSeoScore, resolveQueryCollections, } from '../../posts/query.js';
6
+ import { getActuateConfig } from '../../config/runtime.js';
7
+ import { guardedDb } from '../guarded-db.js';
8
+ import { json, errorResponse, internalError, clampPageSize, requireAuth, requireCollectionScope, buildActionContext, collectionSupportsSections, } from '../route-helpers.js';
9
+ export function registerSectionRoutes(router) {
10
+ const db = guardedDb;
11
+ // ---------------------------------------------------------------------------
12
+ // Page section routes (flat Page Editor model)
13
+ //
14
+ // These operate on a page document's `data.sections` JSON array — the same
15
+ // model the visual Page Editor reads/writes — so an AI agent driving the
16
+ // CMS from Cursor/VS Code via MCP produces real, editable, publishable
17
+ // sections rather than disposable markup. All mutations go through the
18
+ // audited `updateDocument` path (versions, hooks, cache invalidation,
19
+ // live-preview push) and reuse collection scope checks.
20
+ //
21
+ // Discovery + validation are deterministic (no LLM) and only need auth.
22
+ // ---------------------------------------------------------------------------
23
+ // GET /page-sections/types — discover registered section types + schemas.
24
+ // Optional `?scope=page|post` filters to types usable on that surface so an
25
+ // agent only builds sections that have a working renderer in that context.
26
+ router.get('/page-sections/types', async (request) => {
27
+ try {
28
+ const auth = await requireAuth(request);
29
+ if (auth.error)
30
+ return auth.error;
31
+ const scope = new URL(request.url).searchParams.get('scope');
32
+ const data = scope === 'page' || scope === 'post'
33
+ ? getSectionTypesForScope(scope)
34
+ : getAllSectionTypes();
35
+ return json({ data });
36
+ }
37
+ catch (err) {
38
+ return internalError(err, 'list section types');
39
+ }
40
+ });
41
+ // POST /page-sections/validate — validate section content against its
42
+ // type schema. Body: { sectionType, content, settings? }.
43
+ router.post('/page-sections/validate', async (request) => {
44
+ try {
45
+ const auth = await requireAuth(request);
46
+ if (auth.error)
47
+ return auth.error;
48
+ const body = (await request.json().catch(() => null));
49
+ const sectionType = typeof body?.sectionType === 'string' ? body.sectionType : '';
50
+ if (!sectionType)
51
+ return errorResponse('sectionType is required', 400);
52
+ const content = body?.content && typeof body.content === 'object'
53
+ ? body.content
54
+ : {};
55
+ const settings = body?.settings && typeof body.settings === 'object'
56
+ ? body.settings
57
+ : undefined;
58
+ const result = validateSectionContent(sectionType, content, settings);
59
+ return json({ data: result });
60
+ }
61
+ catch (err) {
62
+ return internalError(err, 'validate section');
63
+ }
64
+ });
65
+ // Shared: load a page document's sections, or a Response on error.
66
+ //
67
+ // ADR 0002 ownership guard: a document authored in the visual page builder
68
+ // carries a `_layout` tree and its `sections` are *compiled* from that tree on
69
+ // every save. Editing those sections directly would be silently overwritten by
70
+ // the next builder save, so for WRITE callers we refuse a builder-owned doc
71
+ // with 409 instead. Read callers are unaffected.
72
+ const loadPageSections = async (collection, id, ctx, opts) => {
73
+ if (!collectionSupportsSections(collection)) {
74
+ return errorResponse(`Collection "${collection}" does not declare a "sections" JSON field, so it cannot store page sections.`, 400);
75
+ }
76
+ const doc = await getDocument(collection, id, ctx);
77
+ if (!doc)
78
+ return errorResponse('Page not found', 404);
79
+ // `getDocument` lifts the stored `_layout` out of `data` onto the wire
80
+ // envelope (`doc.layout`), so the builder-ownership signal lives there.
81
+ const layout = doc.layout;
82
+ if (opts?.forWrite && layout && typeof layout === 'object') {
83
+ return errorResponse(`Page "${id}" is owned by the visual page builder: its sections are compiled from the builder layout and would be overwritten on the next save. Edit it in the Page Builder instead (or clear its layout to convert it to a section-authored page).`, 409);
84
+ }
85
+ const data = doc.data ?? {};
86
+ // Preserve sections whose type isn't registered (seed/code-owned, e.g.
87
+ // `opal-*`) so a mutation through this API round-trips them instead of
88
+ // silently dropping externally-managed content on the next persist.
89
+ return {
90
+ sections: coerceSections(data.sections, {
91
+ onUnknown: 'preserve',
92
+ }),
93
+ };
94
+ };
95
+ // Shared: persist a mutated section array via the audited update path.
96
+ const persistPageSections = async (collection, id, sections, ctx, userId) => {
97
+ // Drop the derived `unmanaged` marker before persisting — it is recomputed
98
+ // from the registry on every read, so storing it would only let it go stale.
99
+ const toStore = sections.map(({ unmanaged: _unmanaged, ...rest }) => rest);
100
+ const doc = await updateDocument(collection, id, { sections: toStore }, ctx);
101
+ await logEvent({
102
+ event: 'document_updated',
103
+ userId,
104
+ details: { collection, documentId: id, action: 'page_sections_updated' },
105
+ });
106
+ notifyPreviewUpdate(collection, id, {
107
+ values: doc.data,
108
+ status: doc.status,
109
+ updatedAt: doc.updatedAt?.toISOString?.(),
110
+ });
111
+ const data = doc.data ?? {};
112
+ return coerceSections(data.sections, { onUnknown: 'preserve' });
113
+ };
114
+ // GET /page-sections/:collection/:id — list a page's sections.
115
+ router.get('/page-sections/:collection/:id', async (request, params) => {
116
+ try {
117
+ const auth = await requireAuth(request);
118
+ if (auth.error)
119
+ return auth.error;
120
+ const scopeErr = requireCollectionScope(auth.session, params.collection, 'read');
121
+ if (scopeErr)
122
+ return scopeErr;
123
+ const ctx = buildActionContext(auth.session, db());
124
+ const loaded = await loadPageSections(params.collection, params.id, ctx);
125
+ if (loaded instanceof Response)
126
+ return loaded;
127
+ return json({ data: loaded.sections });
128
+ }
129
+ catch (err) {
130
+ return internalError(err, 'list page sections');
131
+ }
132
+ });
133
+ // POST /page-sections/:collection/:id — add a section.
134
+ // Body: { sectionType, name?, internalLabel?, visible?, content?, settings?, position? }.
135
+ router.post('/page-sections/:collection/:id', async (request, params) => {
136
+ try {
137
+ const auth = await requireAuth(request);
138
+ if (auth.error)
139
+ return auth.error;
140
+ const scopeErr = requireCollectionScope(auth.session, params.collection, 'update');
141
+ if (scopeErr)
142
+ return scopeErr;
143
+ const body = (await request.json().catch(() => null));
144
+ const sectionType = typeof body?.sectionType === 'string' ? body.sectionType : '';
145
+ if (!getSectionType(sectionType)) {
146
+ return errorResponse(`Unknown section type "${sectionType}". Call GET /page-sections/types for valid ids.`, 400);
147
+ }
148
+ const ctx = buildActionContext(auth.session, db());
149
+ const loaded = await loadPageSections(params.collection, params.id, ctx, { forWrite: true });
150
+ if (loaded instanceof Response)
151
+ return loaded;
152
+ const section = buildSection(sectionType, {
153
+ name: typeof body?.name === 'string' ? body.name : undefined,
154
+ internalLabel: typeof body?.internalLabel === 'string' ? body.internalLabel : undefined,
155
+ visible: body?.visible !== false,
156
+ content: body?.content && typeof body.content === 'object'
157
+ ? body.content
158
+ : undefined,
159
+ settings: body?.settings && typeof body.settings === 'object'
160
+ ? body.settings
161
+ : undefined,
162
+ });
163
+ const position = typeof body?.position === 'number' ? body.position : undefined;
164
+ const next = addSectionToList(loaded.sections, section, position);
165
+ const saved = await persistPageSections(params.collection, params.id, next, ctx, auth.session.userId);
166
+ const validation = validateSectionContent(section.sectionType, section.content, section.settings);
167
+ return json({ data: { section, sections: saved, validation } }, 201);
168
+ }
169
+ catch (err) {
170
+ return internalError(err, 'add page section');
171
+ }
172
+ });
173
+ // POST /page-sections/:collection/:id/reorder — reorder by id list.
174
+ // Registered BEFORE the :sectionId routes so "reorder" isn't captured as a
175
+ // section id (the router matches in registration order).
176
+ router.post('/page-sections/:collection/:id/reorder', async (request, params) => {
177
+ try {
178
+ const auth = await requireAuth(request);
179
+ if (auth.error)
180
+ return auth.error;
181
+ const scopeErr = requireCollectionScope(auth.session, params.collection, 'update');
182
+ if (scopeErr)
183
+ return scopeErr;
184
+ const body = (await request.json().catch(() => null));
185
+ const orderedIds = Array.isArray(body?.orderedIds)
186
+ ? body.orderedIds.filter((x) => typeof x === 'string')
187
+ : null;
188
+ if (!orderedIds)
189
+ return errorResponse('orderedIds (string[]) is required', 400);
190
+ const ctx = buildActionContext(auth.session, db());
191
+ const loaded = await loadPageSections(params.collection, params.id, ctx, { forWrite: true });
192
+ if (loaded instanceof Response)
193
+ return loaded;
194
+ const next = reorderSectionList(loaded.sections, orderedIds);
195
+ const saved = await persistPageSections(params.collection, params.id, next, ctx, auth.session.userId);
196
+ return json({ data: saved });
197
+ }
198
+ catch (err) {
199
+ return internalError(err, 'reorder page sections');
200
+ }
201
+ });
202
+ // PATCH /page-sections/:collection/:id/:sectionId — update a section.
203
+ // Body: { content?, settings?, name?, internalLabel?, visible? }.
204
+ router.patch('/page-sections/:collection/:id/:sectionId', async (request, params) => {
205
+ try {
206
+ const auth = await requireAuth(request);
207
+ if (auth.error)
208
+ return auth.error;
209
+ const scopeErr = requireCollectionScope(auth.session, params.collection, 'update');
210
+ if (scopeErr)
211
+ return scopeErr;
212
+ const body = (await request.json().catch(() => null));
213
+ const ctx = buildActionContext(auth.session, db());
214
+ const loaded = await loadPageSections(params.collection, params.id, ctx, { forWrite: true });
215
+ if (loaded instanceof Response)
216
+ return loaded;
217
+ const sectionId = params.sectionId;
218
+ if (!loaded.sections.some((s) => s.id === sectionId)) {
219
+ return errorResponse(`Section "${sectionId}" not found on this page.`, 404);
220
+ }
221
+ let next = loaded.sections;
222
+ if (body?.content && typeof body.content === 'object') {
223
+ next = updateSectionContentInList(next, sectionId, body.content);
224
+ }
225
+ if (body?.settings && typeof body.settings === 'object') {
226
+ next = updateSectionSettingsInList(next, sectionId, body.settings);
227
+ }
228
+ const meta = {};
229
+ if (typeof body?.name === 'string')
230
+ meta.name = body.name;
231
+ if (typeof body?.internalLabel === 'string')
232
+ meta.internalLabel = body.internalLabel;
233
+ if (Object.keys(meta).length > 0)
234
+ next = updateSectionMetaInList(next, sectionId, meta);
235
+ if (typeof body?.visible === 'boolean') {
236
+ next = setSectionVisibility(next, sectionId, body.visible);
237
+ }
238
+ const saved = await persistPageSections(params.collection, params.id, next, ctx, auth.session.userId);
239
+ const updated = saved.find((s) => s.id === sectionId);
240
+ const validation = updated
241
+ ? validateSectionContent(updated.sectionType, updated.content, updated.settings)
242
+ : undefined;
243
+ return json({ data: { section: updated, sections: saved, validation } });
244
+ }
245
+ catch (err) {
246
+ return internalError(err, 'update page section');
247
+ }
248
+ });
249
+ // DELETE /page-sections/:collection/:id/:sectionId — remove a section.
250
+ router.delete('/page-sections/:collection/:id/:sectionId', async (request, params) => {
251
+ try {
252
+ const auth = await requireAuth(request);
253
+ if (auth.error)
254
+ return auth.error;
255
+ const scopeErr = requireCollectionScope(auth.session, params.collection, 'update');
256
+ if (scopeErr)
257
+ return scopeErr;
258
+ const ctx = buildActionContext(auth.session, db());
259
+ const loaded = await loadPageSections(params.collection, params.id, ctx, { forWrite: true });
260
+ if (loaded instanceof Response)
261
+ return loaded;
262
+ const sectionId = params.sectionId;
263
+ if (!loaded.sections.some((s) => s.id === sectionId)) {
264
+ return errorResponse(`Section "${sectionId}" not found on this page.`, 404);
265
+ }
266
+ const next = removeSectionFromList(loaded.sections, sectionId);
267
+ const saved = await persistPageSections(params.collection, params.id, next, ctx, auth.session.userId);
268
+ return json({ data: { sections: saved } });
269
+ }
270
+ catch (err) {
271
+ return internalError(err, 'remove page section');
272
+ }
273
+ });
274
+ // ---------------------------------------------------------------------------
275
+ // Post template routes
276
+ //
277
+ // A post type's template is a single document in the hidden
278
+ // `post-templates` collection, matched by `slug` == the post-type slug. It
279
+ // stores the design-driven header layout (`data.header`) and the default
280
+ // section list (`data.sections`) new posts inherit. Lets an external AI
281
+ // agent (Cursor/VS Code) read a type's template and build it from
282
+ // scope=post section types discovered via `/page-sections/types?scope=post`.
283
+ // ---------------------------------------------------------------------------
284
+ const POST_TEMPLATES_COLLECTION = 'post-templates';
285
+ const findPostTemplateDoc = async (postType, ctx) => {
286
+ const res = await listDocuments({ collection: POST_TEMPLATES_COLLECTION, pageSize: 200 }, ctx);
287
+ const docs = res.docs ?? [];
288
+ return (docs.find((d) => (d.slug ?? d.data?.slug) === postType) ?? null);
289
+ };
290
+ const templateResponse = (postType, doc) => {
291
+ if (!doc) {
292
+ // No saved template yet → hand back a usable starting template
293
+ // (centered header + article body + related posts) rather than a blank
294
+ // one, so new posts and the template editor are never empty. docId is
295
+ // null so callers can still tell it has not been persisted.
296
+ const seed = createPostTemplate(postType);
297
+ return { postType, docId: null, header: seed.header, sections: seed.sections };
298
+ }
299
+ const data = doc.data ?? {};
300
+ return {
301
+ postType,
302
+ docId: doc.id,
303
+ header: coercePostHeader(data.header),
304
+ sections: coerceSections(data.sections),
305
+ };
306
+ };
307
+ // GET /post-templates/:type — read a post type's template (header +
308
+ // default sections). Returns an unsaved default when none exists yet.
309
+ router.get('/post-templates/:type', async (request, params) => {
310
+ try {
311
+ const auth = await requireAuth(request);
312
+ if (auth.error)
313
+ return auth.error;
314
+ const scopeErr = requireCollectionScope(auth.session, POST_TEMPLATES_COLLECTION, 'read');
315
+ if (scopeErr)
316
+ return scopeErr;
317
+ const ctx = buildActionContext(auth.session, db());
318
+ const doc = await findPostTemplateDoc(params.type, ctx);
319
+ return json({ data: templateResponse(params.type, doc) });
320
+ }
321
+ catch (err) {
322
+ return internalError(err, 'get post template');
323
+ }
324
+ });
325
+ // PUT /post-templates/:type — create or replace a post type's template.
326
+ // Body: { header?, sections? }. Header is coerced to a full PostHeaderConfig
327
+ // and sections are coerced (unknown section types dropped) so the stored
328
+ // template always renders.
329
+ router.put('/post-templates/:type', async (request, params) => {
330
+ try {
331
+ const auth = await requireAuth(request);
332
+ if (auth.error)
333
+ return auth.error;
334
+ const scopeErr = requireCollectionScope(auth.session, POST_TEMPLATES_COLLECTION, 'update');
335
+ if (scopeErr)
336
+ return scopeErr;
337
+ const body = (await request.json().catch(() => null));
338
+ const header = coercePostHeader(body?.header);
339
+ const sections = coerceSections(body?.sections);
340
+ const ctx = buildActionContext(auth.session, db());
341
+ const existing = await findPostTemplateDoc(params.type, ctx);
342
+ const data = {
343
+ title: `${params.type} template`,
344
+ slug: params.type,
345
+ postType: params.type,
346
+ header,
347
+ sections,
348
+ status: 'PUBLISHED',
349
+ };
350
+ const doc = existing
351
+ ? await updateDocument(POST_TEMPLATES_COLLECTION, existing.id, data, ctx)
352
+ : await createDocument(POST_TEMPLATES_COLLECTION, data, ctx);
353
+ await logEvent({
354
+ event: existing ? 'document_updated' : 'document_created',
355
+ userId: auth.session.userId,
356
+ details: {
357
+ collection: POST_TEMPLATES_COLLECTION,
358
+ documentId: doc.id,
359
+ action: 'post_template_saved',
360
+ postType: params.type,
361
+ },
362
+ });
363
+ return json({ data: templateResponse(params.type, doc) });
364
+ }
365
+ catch (err) {
366
+ return internalError(err, 'save post template');
367
+ }
368
+ });
369
+ // ---------------------------------------------------------------------------
370
+ // GET /posts — aggregate list across post-type collections for the admin
371
+ // Posts area. Filtering, sorting and pagination all happen at the DB level
372
+ // (one query + one count) so the page scales past the old per-type client
373
+ // cap. Respects per-collection read scope: API keys only see collections
374
+ // their scope allows. Returns the post rows plus dataset-wide type counts
375
+ // and the contributor list the toolbar needs.
376
+ // ---------------------------------------------------------------------------
377
+ router.get('/posts', async (request) => {
378
+ try {
379
+ const auth = await requireAuth(request);
380
+ if (auth.error)
381
+ return auth.error;
382
+ const cfg = getActuateConfig();
383
+ const collections = cfg?.collections ?? {};
384
+ // Post-type collections the caller may read. Hidden collections and ones
385
+ // the API-key scope forbids are dropped so the aggregate never leaks rows
386
+ // a direct /collections/:slug call would refuse.
387
+ const postTypeMeta = [];
388
+ const allowedSlugs = [];
389
+ for (const [slug, col] of Object.entries(collections)) {
390
+ const isPost = col?.type === 'post' ||
391
+ (typeof col?.admin?.group === 'string' && col.admin.group.toLowerCase() === 'posts');
392
+ if (!isPost || col?.admin?.hidden)
393
+ continue;
394
+ if (requireCollectionScope(auth.session, slug, 'read'))
395
+ continue;
396
+ allowedSlugs.push(slug);
397
+ postTypeMeta.push({
398
+ slug,
399
+ singular: col?.labels?.singular ?? slug,
400
+ plural: col?.labels?.plural ?? slug,
401
+ description: col?.admin?.description ?? null,
402
+ urlPrefix: col?.urlPrefix ?? slug,
403
+ color: col?.admin?.color ?? null,
404
+ icon: col?.admin?.icon ?? null,
405
+ fieldCount: col?.fields ? Object.keys(col.fields).length : 0,
406
+ });
407
+ }
408
+ const url = new URL(request.url);
409
+ const page = Math.max(1, Number(url.searchParams.get('page')) || 1);
410
+ const pageSize = clampPageSize(url.searchParams.get('pageSize'), 100, 10);
411
+ if (allowedSlugs.length === 0) {
412
+ return json({
413
+ data: { posts: [], total: 0, page, pageSize, types: [], authors: [] },
414
+ });
415
+ }
416
+ const queryInput = {
417
+ allowedCollections: allowedSlugs,
418
+ postType: url.searchParams.get('postType') ?? undefined,
419
+ status: url.searchParams.get('status') ?? undefined,
420
+ author: url.searchParams.get('author') ?? undefined,
421
+ search: url.searchParams.get('search') ?? undefined,
422
+ sortBy: url.searchParams.get('sortBy') ?? undefined,
423
+ sortDirection: url.searchParams.get('sortDirection') ?? undefined,
424
+ };
425
+ const where = buildPostsWhere(queryInput);
426
+ const orderBy = buildPostsOrderBy(queryInput.sortBy, queryInput.sortDirection);
427
+ const queryCollections = resolveQueryCollections(queryInput);
428
+ // No collections to scan (e.g. a forbidden ?postType) → empty page, but
429
+ // still return the type/author facets for the toolbar.
430
+ const [docs, total] = queryCollections.length
431
+ ? await Promise.all([
432
+ db().document.findMany({
433
+ where,
434
+ orderBy,
435
+ skip: (page - 1) * pageSize,
436
+ take: pageSize,
437
+ include: { createdBy: { select: { id: true, name: true, email: true } } },
438
+ }),
439
+ db().document.count({ where }),
440
+ ])
441
+ : [[], 0];
442
+ // Dataset-wide facets (ignore the active filters) so the type tabs and
443
+ // author dropdown reflect everything the user can see, not the query.
444
+ const facetWhere = { collection: { in: allowedSlugs }, deletedAt: null };
445
+ const [typeCounts, authorGroups] = await Promise.all([
446
+ db().document.groupBy({
447
+ by: ['collection'],
448
+ where: facetWhere,
449
+ _count: { _all: true },
450
+ }),
451
+ db().document.groupBy({ by: ['createdById'], where: facetWhere }),
452
+ ]);
453
+ const countBySlug = new Map();
454
+ for (const row of typeCounts) {
455
+ countBySlug.set(row.collection, row._count._all);
456
+ }
457
+ const authorIds = authorGroups
458
+ .map((g) => g.createdById)
459
+ .filter((id) => Boolean(id));
460
+ const authorUsers = authorIds.length
461
+ ? await db().user.findMany({
462
+ where: { id: { in: authorIds } },
463
+ select: { id: true, name: true, email: true },
464
+ })
465
+ : [];
466
+ const posts = docs.map((d) => ({
467
+ id: d.id,
468
+ collection: d.collection,
469
+ title: d.title ?? null,
470
+ slug: d.slug ?? null,
471
+ status: d.status,
472
+ publishedAt: d.publishedAt ? d.publishedAt.toISOString() : null,
473
+ scheduledAt: d.scheduledAt ? d.scheduledAt.toISOString() : null,
474
+ createdAt: d.createdAt.toISOString(),
475
+ updatedAt: d.updatedAt.toISOString(),
476
+ seoScore: extractPostSeoScore(d.data),
477
+ author: d.createdBy
478
+ ? { id: d.createdBy.id, name: d.createdBy.name?.trim() || d.createdBy.email }
479
+ : null,
480
+ }));
481
+ return json({
482
+ data: {
483
+ posts,
484
+ total,
485
+ page,
486
+ pageSize,
487
+ types: postTypeMeta.map((t) => ({ ...t, count: countBySlug.get(t.slug) ?? 0 })),
488
+ authors: authorUsers.map((u) => ({
489
+ id: u.id,
490
+ name: u.name?.trim() || u.email,
491
+ })),
492
+ },
493
+ });
494
+ }
495
+ catch (err) {
496
+ return internalError(err, 'list posts');
497
+ }
498
+ });
499
+ }
500
+ //# sourceMappingURL=sections.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sections.js","sourceRoot":"","sources":["../../../src/api/routes/sections.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ApiRouter } from '../router.js';
2
+ export declare function registerSecurityCenterRoutes(router: ApiRouter): void;
3
+ //# sourceMappingURL=security-center.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security-center.d.ts","sourceRoot":"","sources":["../../../src/api/routes/security-center.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAW7C,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAiEpE"}
@@ -0,0 +1,71 @@
1
+ import { getClientIp } from '../../security/client-ip.js';
2
+ import { guardedDb } from '../guarded-db.js';
3
+ import { importAIPlugin, json, internalError, requireAuth, requireAdminScope, } from '../route-helpers.js';
4
+ export function registerSecurityCenterRoutes(router) {
5
+ const db = guardedDb;
6
+ // ---------------------------------------------------------------------------
7
+ // Security Center
8
+ // GET /security — computed posture + summaries
9
+ // PUT /security — persist enforced policy (lockout-safe)
10
+ // POST /security/sessions/revoke-all — revoke the caller's other sessions
11
+ // ---------------------------------------------------------------------------
12
+ router.get('/security', async (request) => {
13
+ try {
14
+ const auth = await requireAuth(request);
15
+ if (auth.error)
16
+ return auth.error;
17
+ const adminErr = requireAdminScope(auth.session);
18
+ if (adminErr)
19
+ return adminErr;
20
+ const { getSecurityCenterData } = await import('../../security/center/gather.js');
21
+ const data = await getSecurityCenterData({
22
+ db: db(),
23
+ userId: auth.session.userId,
24
+ currentIp: getClientIp(request),
25
+ });
26
+ return json({ data });
27
+ }
28
+ catch (err) {
29
+ return internalError(err, 'security GET');
30
+ }
31
+ });
32
+ // Read-only AI status. The dashboard cannot configure AI — it is wired up
33
+ // server-side via `aiPlugin({ provider, apiKey })` reading environment
34
+ // variables. This endpoint just reports whether the plugin is installed and
35
+ // a provider is configured so the admin UI can show an honest status instead
36
+ // of a decorative key field. Never returns secret values.
37
+ router.get('/ai/status', async (request) => {
38
+ try {
39
+ const auth = await requireAuth(request);
40
+ if (auth.error)
41
+ return auth.error;
42
+ const adminErr = requireAdminScope(auth.session);
43
+ if (adminErr)
44
+ return adminErr;
45
+ let installed = false;
46
+ let configured = false;
47
+ try {
48
+ const aiMod = await importAIPlugin();
49
+ installed = true;
50
+ if (typeof aiMod?.getAIProvider === 'function') {
51
+ configured = aiMod.getAIProvider() != null;
52
+ }
53
+ }
54
+ catch {
55
+ installed = false;
56
+ }
57
+ // Best-effort provider hint from the environment. We only expose which
58
+ // provider env var is present, never the key value itself.
59
+ const provider = process.env.ANTHROPIC_API_KEY
60
+ ? 'anthropic'
61
+ : process.env.OPENAI_API_KEY
62
+ ? 'openai'
63
+ : null;
64
+ return json({ data: { installed, configured, provider } });
65
+ }
66
+ catch (err) {
67
+ return internalError(err, 'ai status');
68
+ }
69
+ });
70
+ }
71
+ //# sourceMappingURL=security-center.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security-center.js","sourceRoot":"","sources":["../../../src/api/routes/security-center.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC5C,OAAO,EACL,cAAc,EACd,IAAI,EACJ,aAAa,EACb,WAAW,EACX,iBAAiB,GAClB,MAAM,qBAAqB,CAAA;AAE5B,MAAM,UAAU,4BAA4B,CAAC,MAAiB;IAC5D,MAAM,EAAE,GAAG,SAAS,CAAA;IACpB,8EAA8E;IAC9E,kBAAkB;IAClB,wEAAwE;IACxE,mFAAmF;IACnF,+EAA+E;IAC/E,8EAA8E;IAE9E,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QACxC,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,CAAA;YACvC,IAAI,IAAI,CAAC,KAAK;gBAAE,OAAO,IAAI,CAAC,KAAK,CAAA;YACjC,MAAM,QAAQ,GAAG,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YAChD,IAAI,QAAQ;gBAAE,OAAO,QAAQ,CAAA;YAE7B,MAAM,EAAE,qBAAqB,EAAE,GAAG,MAAM,MAAM,CAAC,iCAAiC,CAAC,CAAA;YACjF,MAAM,IAAI,GAAG,MAAM,qBAAqB,CAAC;gBACvC,EAAE,EAAE,EAAE,EAAE;gBACR,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;gBAC3B,SAAS,EAAE,WAAW,CAAC,OAAO,CAAC;aAChC,CAAC,CAAA;YACF,OAAO,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAA;QACvB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,aAAa,CAAC,GAAG,EAAE,cAAc,CAAC,CAAA;QAC3C,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,0EAA0E;IAC1E,uEAAuE;IACvE,4EAA4E;IAC5E,6EAA6E;IAC7E,0DAA0D;IAC1D,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QACzC,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,CAAA;YACvC,IAAI,IAAI,CAAC,KAAK;gBAAE,OAAO,IAAI,CAAC,KAAK,CAAA;YACjC,MAAM,QAAQ,GAAG,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YAChD,IAAI,QAAQ;gBAAE,OAAO,QAAQ,CAAA;YAE7B,IAAI,SAAS,GAAG,KAAK,CAAA;YACrB,IAAI,UAAU,GAAG,KAAK,CAAA;YACtB,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,MAAM,cAAc,EAAE,CAAA;gBACpC,SAAS,GAAG,IAAI,CAAA;gBAChB,IAAI,OAAO,KAAK,EAAE,aAAa,KAAK,UAAU,EAAE,CAAC;oBAC/C,UAAU,GAAG,KAAK,CAAC,aAAa,EAAE,IAAI,IAAI,CAAA;gBAC5C,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS,GAAG,KAAK,CAAA;YACnB,CAAC;YAED,uEAAuE;YACvE,2DAA2D;YAC3D,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB;gBAC5C,CAAC,CAAC,WAAW;gBACb,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc;oBAC1B,CAAC,CAAC,QAAQ;oBACV,CAAC,CAAC,IAAI,CAAA;YAEV,OAAO,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAA;QAC5D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,aAAa,CAAC,GAAG,EAAE,WAAW,CAAC,CAAA;QACxC,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { ApiRouter } from '../router.js';
2
+ import type { ActuateCMSConfig } from '../../config/types.js';
3
+ /**
4
+ * Load the runtime config with DB-stored SEO overrides applied. Used by every
5
+ * public SEO surface (sitemap, robots, og.png) and by /resolve so admin edits
6
+ * in the Settings → SEO panel take effect without a redeploy.
7
+ *
8
+ * Falls back to the static config when no override row exists yet — first
9
+ * boot of a freshly-installed CMS keeps working.
10
+ */
11
+ export declare function loadEffectiveConfig(): Promise<ActuateCMSConfig | null>;
12
+ export declare function siteUrlFromRequest(request: Request, cfg?: ActuateCMSConfig | null): string;
13
+ export declare function registerSeoPublicRoutes(router: ApiRouter): void;
14
+ //# sourceMappingURL=seo-public.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seo-public.d.ts","sourceRoot":"","sources":["../../../src/api/routes/seo-public.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE7C,OAAO,KAAK,EAAE,gBAAgB,EAAwB,MAAM,uBAAuB,CAAA;AAoBnF;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAU5E;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,gBAAgB,GAAG,IAAI,GAAG,MAAM,CAW1F;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAg7B/D"}