@actuate-media/cms-core 0.57.0 → 0.58.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 (214) 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/__tests__/auth/oauth.test.js +3 -1
  4. package/dist/__tests__/auth/oauth.test.js.map +1 -1
  5. package/dist/__tests__/auth/reset.test.js +1 -0
  6. package/dist/__tests__/auth/reset.test.js.map +1 -1
  7. package/dist/__tests__/auth/user-admin.test.js +14 -12
  8. package/dist/__tests__/auth/user-admin.test.js.map +1 -1
  9. package/dist/__tests__/db/model-types-freshness.test.d.ts +2 -0
  10. package/dist/__tests__/db/model-types-freshness.test.d.ts.map +1 -0
  11. package/dist/__tests__/db/model-types-freshness.test.js +46 -0
  12. package/dist/__tests__/db/model-types-freshness.test.js.map +1 -0
  13. package/dist/__tests__/scheduling/scheduling.test.js +8 -6
  14. package/dist/__tests__/scheduling/scheduling.test.js.map +1 -1
  15. package/dist/__tests__/server-site.test.js +3 -1
  16. package/dist/__tests__/server-site.test.js.map +1 -1
  17. package/dist/__tests__/setup/index.test.js +21 -10
  18. package/dist/__tests__/setup/index.test.js.map +1 -1
  19. package/dist/actions.d.ts +2 -1
  20. package/dist/actions.d.ts.map +1 -1
  21. package/dist/actions.js +15 -8
  22. package/dist/actions.js.map +1 -1
  23. package/dist/api/guarded-db.d.ts +14 -0
  24. package/dist/api/guarded-db.d.ts.map +1 -0
  25. package/dist/api/guarded-db.js +58 -0
  26. package/dist/api/guarded-db.js.map +1 -0
  27. package/dist/api/handler-factory.d.ts.map +1 -1
  28. package/dist/api/handler-factory.js +2 -1
  29. package/dist/api/handler-factory.js.map +1 -1
  30. package/dist/api/handlers.d.ts +5 -3
  31. package/dist/api/handlers.d.ts.map +1 -1
  32. package/dist/api/handlers.js +54 -13068
  33. package/dist/api/handlers.js.map +1 -1
  34. package/dist/api/route-helpers.d.ts +321 -0
  35. package/dist/api/route-helpers.d.ts.map +1 -0
  36. package/dist/api/route-helpers.js +1307 -0
  37. package/dist/api/route-helpers.js.map +1 -0
  38. package/dist/api/routes/ai-seo.d.ts +3 -0
  39. package/dist/api/routes/ai-seo.d.ts.map +1 -0
  40. package/dist/api/routes/ai-seo.js +377 -0
  41. package/dist/api/routes/ai-seo.js.map +1 -0
  42. package/dist/api/routes/ai-settings.d.ts +3 -0
  43. package/dist/api/routes/ai-settings.d.ts.map +1 -0
  44. package/dist/api/routes/ai-settings.js +764 -0
  45. package/dist/api/routes/ai-settings.js.map +1 -0
  46. package/dist/api/routes/auth.d.ts +3 -0
  47. package/dist/api/routes/auth.d.ts.map +1 -0
  48. package/dist/api/routes/auth.js +787 -0
  49. package/dist/api/routes/auth.js.map +1 -0
  50. package/dist/api/routes/collab.d.ts +3 -0
  51. package/dist/api/routes/collab.d.ts.map +1 -0
  52. package/dist/api/routes/collab.js +426 -0
  53. package/dist/api/routes/collab.js.map +1 -0
  54. package/dist/api/routes/dashboard.d.ts +7 -0
  55. package/dist/api/routes/dashboard.d.ts.map +1 -0
  56. package/dist/api/routes/dashboard.js +540 -0
  57. package/dist/api/routes/dashboard.js.map +1 -0
  58. package/dist/api/routes/documents.d.ts +3 -0
  59. package/dist/api/routes/documents.d.ts.map +1 -0
  60. package/dist/api/routes/documents.js +180 -0
  61. package/dist/api/routes/documents.js.map +1 -0
  62. package/dist/api/routes/folders.d.ts +3 -0
  63. package/dist/api/routes/folders.d.ts.map +1 -0
  64. package/dist/api/routes/folders.js +195 -0
  65. package/dist/api/routes/folders.js.map +1 -0
  66. package/dist/api/routes/forms.d.ts +3 -0
  67. package/dist/api/routes/forms.d.ts.map +1 -0
  68. package/dist/api/routes/forms.js +661 -0
  69. package/dist/api/routes/forms.js.map +1 -0
  70. package/dist/api/routes/globals.d.ts +3 -0
  71. package/dist/api/routes/globals.d.ts.map +1 -0
  72. package/dist/api/routes/globals.js +133 -0
  73. package/dist/api/routes/globals.js.map +1 -0
  74. package/dist/api/routes/media.d.ts +3 -0
  75. package/dist/api/routes/media.d.ts.map +1 -0
  76. package/dist/api/routes/media.js +521 -0
  77. package/dist/api/routes/media.js.map +1 -0
  78. package/dist/api/routes/meta.d.ts +3 -0
  79. package/dist/api/routes/meta.d.ts.map +1 -0
  80. package/dist/api/routes/meta.js +96 -0
  81. package/dist/api/routes/meta.js.map +1 -0
  82. package/dist/api/routes/page-templates.d.ts +3 -0
  83. package/dist/api/routes/page-templates.d.ts.map +1 -0
  84. package/dist/api/routes/page-templates.js +265 -0
  85. package/dist/api/routes/page-templates.js.map +1 -0
  86. package/dist/api/routes/presence.d.ts +3 -0
  87. package/dist/api/routes/presence.d.ts.map +1 -0
  88. package/dist/api/routes/presence.js +35 -0
  89. package/dist/api/routes/presence.js.map +1 -0
  90. package/dist/api/routes/preview.d.ts +3 -0
  91. package/dist/api/routes/preview.d.ts.map +1 -0
  92. package/dist/api/routes/preview.js +111 -0
  93. package/dist/api/routes/preview.js.map +1 -0
  94. package/dist/api/routes/redirects.d.ts +3 -0
  95. package/dist/api/routes/redirects.d.ts.map +1 -0
  96. package/dist/api/routes/redirects.js +790 -0
  97. package/dist/api/routes/redirects.js.map +1 -0
  98. package/dist/api/routes/saved-sections.d.ts +3 -0
  99. package/dist/api/routes/saved-sections.d.ts.map +1 -0
  100. package/dist/api/routes/saved-sections.js +1036 -0
  101. package/dist/api/routes/saved-sections.js.map +1 -0
  102. package/dist/api/routes/scheduling.d.ts +3 -0
  103. package/dist/api/routes/scheduling.d.ts.map +1 -0
  104. package/dist/api/routes/scheduling.js +373 -0
  105. package/dist/api/routes/scheduling.js.map +1 -0
  106. package/dist/api/routes/sections.d.ts +3 -0
  107. package/dist/api/routes/sections.d.ts.map +1 -0
  108. package/dist/api/routes/sections.js +500 -0
  109. package/dist/api/routes/sections.js.map +1 -0
  110. package/dist/api/routes/security-center.d.ts +3 -0
  111. package/dist/api/routes/security-center.d.ts.map +1 -0
  112. package/dist/api/routes/security-center.js +71 -0
  113. package/dist/api/routes/security-center.js.map +1 -0
  114. package/dist/api/routes/seo-public.d.ts +14 -0
  115. package/dist/api/routes/seo-public.d.ts.map +1 -0
  116. package/dist/api/routes/seo-public.js +930 -0
  117. package/dist/api/routes/seo-public.js.map +1 -0
  118. package/dist/api/routes/seo.d.ts +8 -0
  119. package/dist/api/routes/seo.d.ts.map +1 -0
  120. package/dist/api/routes/seo.js +848 -0
  121. package/dist/api/routes/seo.js.map +1 -0
  122. package/dist/api/routes/system.d.ts +3 -0
  123. package/dist/api/routes/system.d.ts.map +1 -0
  124. package/dist/api/routes/system.js +340 -0
  125. package/dist/api/routes/system.js.map +1 -0
  126. package/dist/api/routes/url-resolution.d.ts +3 -0
  127. package/dist/api/routes/url-resolution.d.ts.map +1 -0
  128. package/dist/api/routes/url-resolution.js +457 -0
  129. package/dist/api/routes/url-resolution.js.map +1 -0
  130. package/dist/api/routes/users.d.ts +3 -0
  131. package/dist/api/routes/users.d.ts.map +1 -0
  132. package/dist/api/routes/users.js +1162 -0
  133. package/dist/api/routes/users.js.map +1 -0
  134. package/dist/api/routes/webhooks.d.ts +3 -0
  135. package/dist/api/routes/webhooks.d.ts.map +1 -0
  136. package/dist/api/routes/webhooks.js +338 -0
  137. package/dist/api/routes/webhooks.js.map +1 -0
  138. package/dist/auth/invites.d.ts +9 -8
  139. package/dist/auth/invites.d.ts.map +1 -1
  140. package/dist/auth/invites.js.map +1 -1
  141. package/dist/auth/oauth.d.ts +2 -1
  142. package/dist/auth/oauth.d.ts.map +1 -1
  143. package/dist/auth/oauth.js.map +1 -1
  144. package/dist/auth/reset.d.ts +3 -2
  145. package/dist/auth/reset.d.ts.map +1 -1
  146. package/dist/auth/reset.js +5 -2
  147. package/dist/auth/reset.js.map +1 -1
  148. package/dist/auth/session.d.ts +3 -2
  149. package/dist/auth/session.d.ts.map +1 -1
  150. package/dist/auth/session.js.map +1 -1
  151. package/dist/auth/team.d.ts +4 -3
  152. package/dist/auth/team.d.ts.map +1 -1
  153. package/dist/auth/team.js.map +1 -1
  154. package/dist/auth/user-admin.d.ts +6 -5
  155. package/dist/auth/user-admin.d.ts.map +1 -1
  156. package/dist/auth/user-admin.js.map +1 -1
  157. package/dist/content-health/cron.d.ts +2 -1
  158. package/dist/content-health/cron.d.ts.map +1 -1
  159. package/dist/content-health/cron.js.map +1 -1
  160. package/dist/cron/index.d.ts +2 -1
  161. package/dist/cron/index.d.ts.map +1 -1
  162. package/dist/cron/index.js +2 -0
  163. package/dist/cron/index.js.map +1 -1
  164. package/dist/db/client-types.d.ts +75 -0
  165. package/dist/db/client-types.d.ts.map +1 -0
  166. package/dist/db/client-types.js +2 -0
  167. package/dist/db/client-types.js.map +1 -0
  168. package/dist/db/model-types.d.ts +564 -0
  169. package/dist/db/model-types.d.ts.map +1 -0
  170. package/dist/db/model-types.js +14 -0
  171. package/dist/db/model-types.js.map +1 -0
  172. package/dist/diagnostics/api-metrics.d.ts.map +1 -1
  173. package/dist/diagnostics/api-metrics.js.map +1 -1
  174. package/dist/forms/entries.d.ts.map +1 -1
  175. package/dist/forms/entries.js.map +1 -1
  176. package/dist/forms/files.d.ts.map +1 -1
  177. package/dist/forms/files.js.map +1 -1
  178. package/dist/forms/service.d.ts.map +1 -1
  179. package/dist/forms/service.js.map +1 -1
  180. package/dist/forms/submission.d.ts.map +1 -1
  181. package/dist/forms/submission.js.map +1 -1
  182. package/dist/forms/webhooks.d.ts.map +1 -1
  183. package/dist/forms/webhooks.js.map +1 -1
  184. package/dist/graphql/index.d.ts.map +1 -1
  185. package/dist/graphql/index.js.map +1 -1
  186. package/dist/index.d.ts +1 -0
  187. package/dist/index.d.ts.map +1 -1
  188. package/dist/index.js.map +1 -1
  189. package/dist/scheduling/index.d.ts +2 -1
  190. package/dist/scheduling/index.d.ts.map +1 -1
  191. package/dist/scheduling/index.js.map +1 -1
  192. package/dist/search/index.d.ts.map +1 -1
  193. package/dist/search/index.js.map +1 -1
  194. package/dist/security/audit.d.ts.map +1 -1
  195. package/dist/security/audit.js.map +1 -1
  196. package/dist/security/reauth.d.ts +2 -1
  197. package/dist/security/reauth.d.ts.map +1 -1
  198. package/dist/security/reauth.js.map +1 -1
  199. package/dist/seo/audit-runner.d.ts +3 -2
  200. package/dist/seo/audit-runner.d.ts.map +1 -1
  201. package/dist/seo/audit-runner.js.map +1 -1
  202. package/dist/server-site.d.ts +2 -1
  203. package/dist/server-site.d.ts.map +1 -1
  204. package/dist/server-site.js.map +1 -1
  205. package/dist/setup/index.d.ts +4 -3
  206. package/dist/setup/index.d.ts.map +1 -1
  207. package/dist/setup/index.js.map +1 -1
  208. package/dist/webhooks/index.d.ts +5 -5
  209. package/dist/webhooks/index.d.ts.map +1 -1
  210. package/dist/webhooks/index.js +8 -1
  211. package/dist/webhooks/index.js.map +1 -1
  212. package/dist/workflow/index.d.ts.map +1 -1
  213. package/dist/workflow/index.js.map +1 -1
  214. package/package.json +1 -1
@@ -0,0 +1,521 @@
1
+ import { optimizeImage, formatBytes } from '../../media/optimize.js';
2
+ import { logEvent } from '../../security/audit.js';
3
+ import { validateMimeType, checkMagicBytes } from '../../security/upload.js';
4
+ import { sanitizeHtml } from '../../security/sanitize.js';
5
+ import { guardedDb } from '../guarded-db.js';
6
+ import { importBlobStorage, json, errorResponse, internalError, clampPageSize, normalizeMediaItem, buildMediaUsedOn, isAllowedStorageUrl, requireAuth, requireMediaScope, WRITE_ROLES, requireRole, uploadLimiter, checkRateLimitAsync, } from '../route-helpers.js';
7
+ export function registerMediaRoutes(router) {
8
+ const db = guardedDb;
9
+ // ---------------------------------------------------------------------------
10
+ // Media routes
11
+ // ---------------------------------------------------------------------------
12
+ router.get('/media', async (request) => {
13
+ try {
14
+ const auth = await requireAuth(request);
15
+ if (auth.error)
16
+ return auth.error;
17
+ const url = new URL(request.url);
18
+ const page = Number(url.searchParams.get('page')) || 1;
19
+ const pageSize = clampPageSize(url.searchParams.get('pageSize'));
20
+ const skip = (page - 1) * pageSize;
21
+ const folderParam = url.searchParams.get('folderId');
22
+ const where = {};
23
+ if (folderParam === 'none') {
24
+ where.folderId = null;
25
+ }
26
+ else if (folderParam) {
27
+ where.folderId = folderParam;
28
+ }
29
+ const [items, total] = await Promise.all([
30
+ db().media.findMany({
31
+ where,
32
+ skip,
33
+ take: pageSize,
34
+ orderBy: { createdAt: 'desc' },
35
+ include: {
36
+ mediaUsages: {
37
+ include: {
38
+ document: { select: { id: true, collection: true, title: true, deletedAt: true } },
39
+ },
40
+ },
41
+ },
42
+ }),
43
+ db().media.count({ where }),
44
+ ]);
45
+ const normalized = items.map((item) => {
46
+ const { mediaUsages, ...rest } = normalizeMediaItem(item);
47
+ return { ...rest, usedOn: buildMediaUsedOn(mediaUsages) };
48
+ });
49
+ return json({
50
+ data: {
51
+ data: normalized,
52
+ items: normalized,
53
+ total,
54
+ page,
55
+ pageSize,
56
+ totalPages: Math.ceil(total / pageSize),
57
+ },
58
+ });
59
+ }
60
+ catch (err) {
61
+ return internalError(err);
62
+ }
63
+ });
64
+ // The /media/presign endpoint returns advisory upload metadata; the actual
65
+ // upload still goes through /media/upload (which performs validation).
66
+ // Returning a presigned URL pointing at our own non-presigned endpoint was
67
+ // misleading, so we deprecated this in favour of just using /media/upload
68
+ // directly. We keep the route for backward compatibility but it now just
69
+ // returns a hint to use the upload endpoint.
70
+ router.post('/media/presign', async (request) => {
71
+ try {
72
+ const auth = await requireAuth(request);
73
+ if (auth.error)
74
+ return auth.error;
75
+ const scopeErr = requireMediaScope(auth.session);
76
+ if (scopeErr)
77
+ return scopeErr;
78
+ const body = (await request.json());
79
+ if (!body.filename || !body.contentType) {
80
+ return errorResponse('filename and contentType are required', 400);
81
+ }
82
+ return json({
83
+ data: {
84
+ uploadUrl: '/api/cms/media/upload',
85
+ method: 'POST',
86
+ field: 'file',
87
+ deprecated: true,
88
+ message: 'Direct multipart upload to /media/upload is preferred; this endpoint will be removed in a future release.',
89
+ },
90
+ });
91
+ }
92
+ catch (err) {
93
+ return internalError(err);
94
+ }
95
+ });
96
+ const MAX_UPLOAD_BYTES = 50 * 1024 * 1024; // 50 MB
97
+ /**
98
+ * Allowlist of accepted upload mime types. Storing a file outside this set
99
+ * would let an attacker upload e.g. `.html`/`.js` and serve it from the same
100
+ * origin as the admin (cookie-leaking XSS), or `.svg` with embedded
101
+ * `<script>` (also XSS). Add to this list deliberately.
102
+ */
103
+ const ALLOWED_UPLOAD_MIME_TYPES = [
104
+ 'image/jpeg',
105
+ 'image/png',
106
+ 'image/gif',
107
+ 'image/webp',
108
+ 'image/avif',
109
+ 'image/svg+xml',
110
+ 'application/pdf',
111
+ 'video/mp4',
112
+ 'video/webm',
113
+ 'audio/mpeg',
114
+ 'audio/ogg',
115
+ 'audio/wav',
116
+ ];
117
+ router.post('/media/upload', async (request) => {
118
+ try {
119
+ const auth = await requireAuth(request);
120
+ if (auth.error)
121
+ return auth.error;
122
+ const scopeErr = requireMediaScope(auth.session);
123
+ if (scopeErr)
124
+ return scopeErr;
125
+ if (!(await checkRateLimitAsync(uploadLimiter, `upload:${auth.session.userId}`))) {
126
+ return errorResponse('Upload rate limit reached (20/hour). Try again later.', 429);
127
+ }
128
+ // Reject *before* buffering the body. We require a valid content-length
129
+ // header so that chunked / no-length requests (which would otherwise
130
+ // bypass this gate and allow `request.formData()` to buffer unbounded
131
+ // memory) are rejected up front. The multipart envelope is always
132
+ // larger than the underlying file, so checking against MAX_UPLOAD_BYTES
133
+ // here is a safe upper bound — a 50 MB file cannot fit inside a 50 MB
134
+ // request body.
135
+ const contentLengthHeader = request.headers.get('content-length');
136
+ if (!contentLengthHeader) {
137
+ return errorResponse('Content-Length header is required for uploads (chunked encoding is not supported)', 411);
138
+ }
139
+ const contentLength = parseInt(contentLengthHeader, 10);
140
+ if (!Number.isFinite(contentLength) || contentLength <= 0) {
141
+ return errorResponse('Invalid Content-Length header', 400);
142
+ }
143
+ if (contentLength > MAX_UPLOAD_BYTES) {
144
+ return errorResponse('File exceeds maximum size of 50MB', 413);
145
+ }
146
+ const formData = await request.formData();
147
+ const file = formData.get('file');
148
+ const skipOptimize = formData.get('skipOptimize') === 'true';
149
+ if (!file) {
150
+ return errorResponse('No file provided', 400);
151
+ }
152
+ const originalFilename = file.name;
153
+ const contentType = file.type;
154
+ const originalSize = file.size;
155
+ // Belt-and-braces: even with the header check above, re-validate the
156
+ // *actual* file size after parsing in case the multipart envelope
157
+ // disagreed with the header. This must come BEFORE any further
158
+ // processing (magic byte read, SVG sanitization, blob upload).
159
+ if (originalSize > MAX_UPLOAD_BYTES) {
160
+ return errorResponse('File exceeds maximum size of 50MB', 413);
161
+ }
162
+ // 1. Block file types that aren't on our allowlist outright.
163
+ if (!validateMimeType(contentType, ALLOWED_UPLOAD_MIME_TYPES)) {
164
+ return errorResponse(`Unsupported file type "${contentType || 'unknown'}". Allowed types: ${ALLOWED_UPLOAD_MIME_TYPES.join(', ')}`, 415);
165
+ }
166
+ const arrayBuffer = await file.arrayBuffer();
167
+ // 2. Verify the file's actual bytes match the claimed mime type. Without
168
+ // this, an attacker can upload a `.exe` with `Content-Type: image/png`
169
+ // and have it served from our origin.
170
+ const magicCheck = checkMagicBytes(arrayBuffer, contentType);
171
+ if (!magicCheck.valid) {
172
+ return errorResponse(`File contents do not match declared type "${contentType}".`, 415);
173
+ }
174
+ // 3. SVGs need an extra pass — even when the mime type is correct, the
175
+ // XML body can contain `<script>` or event-handler attributes that
176
+ // execute when an admin previews the file. Sanitize before storing.
177
+ let uploadBuffer;
178
+ let finalFilename = originalFilename;
179
+ let finalMimeType = contentType;
180
+ let finalSize = originalSize;
181
+ let width = null;
182
+ let height = null;
183
+ let blurHash = null;
184
+ let savings = 0;
185
+ if (contentType === 'image/svg+xml') {
186
+ // Strip <script>, on*, javascript: URLs, foreignObject, etc.
187
+ const xml = new TextDecoder('utf-8', { fatal: false }).decode(arrayBuffer);
188
+ const sanitized = sanitizeHtml(xml, {
189
+ allowedTags: [
190
+ 'svg',
191
+ 'g',
192
+ 'path',
193
+ 'circle',
194
+ 'ellipse',
195
+ 'line',
196
+ 'polygon',
197
+ 'polyline',
198
+ 'rect',
199
+ 'text',
200
+ 'tspan',
201
+ 'defs',
202
+ 'use',
203
+ 'symbol',
204
+ 'title',
205
+ 'desc',
206
+ 'style',
207
+ 'linearGradient',
208
+ 'radialGradient',
209
+ 'stop',
210
+ 'mask',
211
+ 'clipPath',
212
+ 'pattern',
213
+ 'filter',
214
+ 'feGaussianBlur',
215
+ 'feColorMatrix',
216
+ 'feOffset',
217
+ 'feBlend',
218
+ 'feFlood',
219
+ 'feComposite',
220
+ 'feMerge',
221
+ 'feMergeNode',
222
+ ],
223
+ allowedAttributes: {
224
+ '*': [
225
+ 'id',
226
+ 'class',
227
+ 'fill',
228
+ 'stroke',
229
+ 'stroke-width',
230
+ 'stroke-linecap',
231
+ 'stroke-linejoin',
232
+ 'opacity',
233
+ 'transform',
234
+ 'd',
235
+ 'cx',
236
+ 'cy',
237
+ 'r',
238
+ 'rx',
239
+ 'ry',
240
+ 'x',
241
+ 'y',
242
+ 'x1',
243
+ 'y1',
244
+ 'x2',
245
+ 'y2',
246
+ 'width',
247
+ 'height',
248
+ 'viewBox',
249
+ 'xmlns',
250
+ 'xmlns:xlink',
251
+ 'preserveAspectRatio',
252
+ 'points',
253
+ 'points',
254
+ 'offset',
255
+ 'stop-color',
256
+ 'stop-opacity',
257
+ 'gradientUnits',
258
+ 'gradientTransform',
259
+ 'href',
260
+ 'xlink:href',
261
+ 'fill-rule',
262
+ 'clip-rule',
263
+ 'mask',
264
+ 'clip-path',
265
+ 'filter',
266
+ 'patternUnits',
267
+ 'patternContentUnits',
268
+ 'in',
269
+ 'in2',
270
+ 'result',
271
+ 'mode',
272
+ 'values',
273
+ 'type',
274
+ 'stdDeviation',
275
+ 'dx',
276
+ 'dy',
277
+ ],
278
+ },
279
+ });
280
+ uploadBuffer = Buffer.from(sanitized, 'utf-8');
281
+ finalSize = uploadBuffer.byteLength;
282
+ }
283
+ else if (!skipOptimize && contentType.startsWith('image/')) {
284
+ const result = await optimizeImage(arrayBuffer, originalFilename, contentType);
285
+ uploadBuffer = result.buffer;
286
+ finalFilename = result.filename;
287
+ finalMimeType = result.mimeType;
288
+ finalSize = result.optimizedSize;
289
+ width = result.width || null;
290
+ height = result.height || null;
291
+ blurHash = result.blurHash ?? null;
292
+ savings = result.savings;
293
+ }
294
+ else {
295
+ uploadBuffer = Buffer.from(arrayBuffer);
296
+ }
297
+ const sanitizedName = finalFilename.replace(/[^a-zA-Z0-9._-]/g, '_');
298
+ const storageKey = `actuate/media/${Date.now()}-${sanitizedName}`;
299
+ let publicUrl = '';
300
+ // Prefer the configured platform storage adapter (e.g. platform-vercel,
301
+ // platform-aws, or a consumer-provided one). Falling through to
302
+ // @vercel/blob via dynamic import preserves the legacy behavior for
303
+ // installs that haven't wired up a platform package yet.
304
+ const { getStorageAdapter } = await import('../../storage/index.js');
305
+ const storage = getStorageAdapter();
306
+ if (storage) {
307
+ try {
308
+ publicUrl = await storage.upload(storageKey, uploadBuffer, finalMimeType);
309
+ }
310
+ catch (err) {
311
+ if (process.env.NODE_ENV !== 'test') {
312
+ console.error('[Actuate CMS] Storage adapter upload failed:', err);
313
+ }
314
+ return errorResponse('Storage upload failed', 500);
315
+ }
316
+ }
317
+ else {
318
+ try {
319
+ const blob = await importBlobStorage();
320
+ const result = await blob.put(storageKey, uploadBuffer, {
321
+ access: 'public',
322
+ contentType: finalMimeType,
323
+ });
324
+ publicUrl = result.url;
325
+ }
326
+ catch {
327
+ publicUrl = `/api/cms/media/file/${storageKey}`;
328
+ }
329
+ }
330
+ const media = await db().media.create({
331
+ data: {
332
+ filename: finalFilename,
333
+ storageKey: publicUrl || storageKey,
334
+ mimeType: finalMimeType,
335
+ fileSize: finalSize,
336
+ width,
337
+ height,
338
+ blurHash,
339
+ uploadedById: auth.session.userId,
340
+ },
341
+ });
342
+ await logEvent({
343
+ event: 'media_uploaded',
344
+ userId: auth.session.userId,
345
+ details: { mediaId: media.id, filename: finalFilename },
346
+ });
347
+ return json({
348
+ data: {
349
+ ...media,
350
+ optimization: {
351
+ originalSize,
352
+ optimizedSize: finalSize,
353
+ savings,
354
+ originalFormat: contentType,
355
+ outputFormat: finalMimeType,
356
+ originalSizeFormatted: formatBytes(originalSize),
357
+ optimizedSizeFormatted: formatBytes(finalSize),
358
+ },
359
+ },
360
+ }, 201);
361
+ }
362
+ catch (err) {
363
+ return internalError(err);
364
+ }
365
+ });
366
+ router.post('/media/:id/optimize', async (request, params) => {
367
+ try {
368
+ const auth = await requireAuth(request);
369
+ if (auth.error)
370
+ return auth.error;
371
+ const media = await db().media.findUnique({ where: { id: params.id } });
372
+ if (!media)
373
+ return errorResponse('Media not found', 404);
374
+ if (!media.mimeType.startsWith('image/')) {
375
+ return errorResponse('Only images can be optimized', 400);
376
+ }
377
+ if (media.mimeType === 'image/webp') {
378
+ return json({
379
+ data: { ...media, optimization: { alreadyOptimized: true, savings: 0 } },
380
+ });
381
+ }
382
+ if (!isAllowedStorageUrl(media.storageKey)) {
383
+ return errorResponse('Invalid media storage URL', 400);
384
+ }
385
+ let fileBuffer;
386
+ try {
387
+ const response = await fetch(media.storageKey);
388
+ if (!response.ok)
389
+ throw new Error('Failed to fetch media file');
390
+ fileBuffer = Buffer.from(await response.arrayBuffer());
391
+ }
392
+ catch {
393
+ return errorResponse('Could not retrieve the original file for optimization', 500);
394
+ }
395
+ const result = await optimizeImage(fileBuffer, media.filename, media.mimeType);
396
+ if (result.savings <= 0) {
397
+ return json({
398
+ data: { ...media, optimization: { alreadyOptimized: true, savings: 0 } },
399
+ });
400
+ }
401
+ // Upload the optimized version
402
+ const sanitizedName = result.filename.replace(/[^a-zA-Z0-9._-]/g, '_');
403
+ const newStorageKey = `actuate/media/${Date.now()}-${sanitizedName}`;
404
+ let newPublicUrl = '';
405
+ try {
406
+ const blob = await importBlobStorage();
407
+ const uploadResult = await blob.put(newStorageKey, result.buffer, {
408
+ access: 'public',
409
+ contentType: result.mimeType,
410
+ });
411
+ newPublicUrl = uploadResult.url;
412
+ // Delete the old blob
413
+ try {
414
+ await blob.del(media.storageKey);
415
+ }
416
+ catch {
417
+ /* best-effort */
418
+ }
419
+ }
420
+ catch {
421
+ newPublicUrl = `/api/cms/media/file/${newStorageKey}`;
422
+ }
423
+ const updated = await db().media.update({
424
+ where: { id: params.id },
425
+ data: {
426
+ filename: result.filename,
427
+ storageKey: newPublicUrl || newStorageKey,
428
+ mimeType: result.mimeType,
429
+ fileSize: result.optimizedSize,
430
+ width: result.width || undefined,
431
+ height: result.height || undefined,
432
+ blurHash: result.blurHash ?? undefined,
433
+ },
434
+ });
435
+ return json({
436
+ data: {
437
+ ...updated,
438
+ optimization: {
439
+ originalSize: result.originalSize,
440
+ optimizedSize: result.optimizedSize,
441
+ savings: result.savings,
442
+ originalFormat: media.mimeType,
443
+ outputFormat: result.mimeType,
444
+ originalSizeFormatted: formatBytes(result.originalSize),
445
+ optimizedSizeFormatted: formatBytes(result.optimizedSize),
446
+ },
447
+ },
448
+ });
449
+ }
450
+ catch (err) {
451
+ return internalError(err);
452
+ }
453
+ });
454
+ router.put('/media/:id', async (request, params) => {
455
+ try {
456
+ const auth = await requireAuth(request);
457
+ if (auth.error)
458
+ return auth.error;
459
+ const scopeErr = requireMediaScope(auth.session);
460
+ if (scopeErr)
461
+ return scopeErr;
462
+ if (!auth.session.apiKey) {
463
+ const roleErr = requireRole(auth.session.role, WRITE_ROLES);
464
+ if (roleErr)
465
+ return roleErr;
466
+ }
467
+ const body = (await request.json());
468
+ const updated = await db().media.update({
469
+ where: { id: params.id },
470
+ data: {
471
+ ...(body.alt !== undefined ? { altText: body.alt } : {}),
472
+ ...(body.title !== undefined ? { title: body.title } : {}),
473
+ ...(body.filename !== undefined ? { filename: body.filename } : {}),
474
+ ...(body.focalX !== undefined ? { focalPointX: body.focalX } : {}),
475
+ ...(body.focalY !== undefined ? { focalPointY: body.focalY } : {}),
476
+ },
477
+ });
478
+ return json({ data: updated });
479
+ }
480
+ catch (err) {
481
+ return internalError(err);
482
+ }
483
+ });
484
+ router.delete('/media/:id', async (request, params) => {
485
+ try {
486
+ const auth = await requireAuth(request);
487
+ if (auth.error)
488
+ return auth.error;
489
+ const scopeErr = requireMediaScope(auth.session);
490
+ if (scopeErr)
491
+ return scopeErr;
492
+ if (!auth.session.apiKey) {
493
+ const roleErr = requireRole(auth.session.role, WRITE_ROLES);
494
+ if (roleErr)
495
+ return roleErr;
496
+ }
497
+ const media = await db().media.findUnique({ where: { id: params.id } });
498
+ if (!media) {
499
+ return errorResponse('Media not found', 404);
500
+ }
501
+ try {
502
+ const blob = await importBlobStorage();
503
+ await blob.del(media.storageKey);
504
+ }
505
+ catch {
506
+ // Storage delete failed or not available
507
+ }
508
+ await db().media.delete({ where: { id: params.id } });
509
+ await logEvent({
510
+ event: 'media_deleted',
511
+ userId: auth.session.userId,
512
+ details: { mediaId: params.id, filename: media.filename },
513
+ });
514
+ return json({ data: { success: true } });
515
+ }
516
+ catch (err) {
517
+ return internalError(err);
518
+ }
519
+ });
520
+ }
521
+ //# sourceMappingURL=media.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media.js","sourceRoot":"","sources":["../../../src/api/routes/media.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ApiRouter } from '../router.js';
2
+ export declare function registerMetaRoutes(router: ApiRouter): void;
3
+ //# sourceMappingURL=meta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meta.d.ts","sourceRoot":"","sources":["../../../src/api/routes/meta.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAO7C,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA4F1D"}
@@ -0,0 +1,96 @@
1
+ import { summarizeCollectionFields } from '../collection-introspection.js';
2
+ import { generateToken as generateCsrfToken } from '../../security/csrf.js';
3
+ import { generateOpenAPISpec } from '../openapi.js';
4
+ import { getActuateConfig } from '../../config/runtime.js';
5
+ import { json, errorResponse } from '../route-helpers.js';
6
+ export function registerMetaRoutes(router) {
7
+ // ---------------------------------------------------------------------------
8
+ // CSRF token endpoint
9
+ // ---------------------------------------------------------------------------
10
+ router.get('/auth/csrf', async () => {
11
+ const token = await generateCsrfToken();
12
+ const isProduction = process.env.NODE_ENV === 'production';
13
+ const cookieFlags = [`actuate_csrf=${token}`, 'Path=/', 'SameSite=Lax', 'Max-Age=86400'];
14
+ if (isProduction)
15
+ cookieFlags.push('Secure');
16
+ const response = json({ data: { token } });
17
+ response.headers.set('Set-Cookie', cookieFlags.join('; '));
18
+ return response;
19
+ });
20
+ // ---------------------------------------------------------------------------
21
+ // OpenAPI spec
22
+ // ---------------------------------------------------------------------------
23
+ router.get('/openapi.json', async () => {
24
+ const config = getActuateConfig();
25
+ if (!config)
26
+ return errorResponse('CMS not configured', 500);
27
+ const spec = generateOpenAPISpec(config);
28
+ return new Response(JSON.stringify(spec, null, 2), {
29
+ status: 200,
30
+ headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' },
31
+ });
32
+ });
33
+ // ---------------------------------------------------------------------------
34
+ // Collection discovery (Phase 7 PR F)
35
+ //
36
+ // Lightweight enumeration sourced directly from `getActuateConfig()` so
37
+ // agents and the MCP server's `list_collections` tool don't depend on
38
+ // OpenAPI spec generation succeeding. OpenAPI is still the source of
39
+ // truth for full per-field JSON Schemas; this endpoint trades depth
40
+ // for reliability and surfaces just enough metadata for the agent to
41
+ // plan a follow-up call (create_document, list_documents, etc.).
42
+ //
43
+ // Auth: unauthenticated, like /openapi.json — the response contains
44
+ // only schema-shape metadata that is already public via OpenAPI.
45
+ // ---------------------------------------------------------------------------
46
+ router.get('/collections', async () => {
47
+ const config = getActuateConfig();
48
+ if (!config)
49
+ return errorResponse('CMS not configured', 500);
50
+ const collections = Object.entries(config.collections ?? {}).map(([slug, def]) => {
51
+ // Reduce each field to `{ name, type, required }` plus, where present,
52
+ // the relationship target, select options, and (for `array`/`blocks`)
53
+ // the nested subfield schema — enough for the agent to plan a deep
54
+ // create/update AND resolve relationship fields (WS-B4 + WS-C C2)
55
+ // without sending the full validator. See collection-introspection.ts.
56
+ const fieldSummary = summarizeCollectionFields(def?.fields);
57
+ return {
58
+ slug,
59
+ labels: def?.labels ?? { singular: slug, plural: slug },
60
+ type: def?.type ?? 'page',
61
+ urlPrefix: def?.urlPrefix ?? slug,
62
+ hidden: def?.admin?.hidden === true,
63
+ // Surface the admin metadata the Posts area needs to render
64
+ // type tabs / cards (description, icon, accent color, group).
65
+ // None are sensitive — they all already live on the public
66
+ // collection definition the admin client receives via props.
67
+ description: def?.admin?.description ?? null,
68
+ icon: def?.admin?.icon ?? null,
69
+ color: def?.admin?.color ?? null,
70
+ group: def?.admin?.group ?? null,
71
+ fieldCount: fieldSummary.length,
72
+ fields: fieldSummary,
73
+ };
74
+ });
75
+ return json({ data: collections });
76
+ });
77
+ router.get('/docs', async () => {
78
+ const html = `<!DOCTYPE html>
79
+ <html>
80
+ <head>
81
+ <title>Actuate CMS API Reference</title>
82
+ <meta charset="utf-8" />
83
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
84
+ </head>
85
+ <body>
86
+ <script id="api-reference" data-url="/api/cms/openapi.json"></script>
87
+ <script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
88
+ </body>
89
+ </html>`;
90
+ return new Response(html, {
91
+ status: 200,
92
+ headers: { 'Content-Type': 'text/html; charset=utf-8' },
93
+ });
94
+ });
95
+ }
96
+ //# sourceMappingURL=meta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meta.js","sourceRoot":"","sources":["../../../src/api/routes/meta.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { ApiRouter } from '../router.js';
2
+ export declare function registerPageTemplateRoutes(router: ApiRouter): void;
3
+ //# sourceMappingURL=page-templates.d.ts.map