@actuate-media/cms-core 0.10.1 → 0.10.3

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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=admin-contracts.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-contracts.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/api/admin-contracts.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,172 @@
1
+ import { beforeEach, describe, expect, it } from 'vitest';
2
+ import { handleActuateAPI } from '../../api/index.js';
3
+ import { createSession } from '../../auth/session.js';
4
+ import { initDB } from '../../db.js';
5
+ const SECRET = 'test-secret-for-admin-contracts-1234567890';
6
+ async function authHeaders() {
7
+ const token = await createSession({ userId: 'admin-1', role: 'ADMIN', sessionId: 'session-1' }, { secret: SECRET });
8
+ return { cookie: `actuate_session=${token}` };
9
+ }
10
+ function createMockDB() {
11
+ return {
12
+ session: {
13
+ findUnique: async () => ({ revokedAt: null }),
14
+ },
15
+ user: {},
16
+ media: {
17
+ findMany: async () => [
18
+ {
19
+ id: 'media-1',
20
+ filename: 'hero.jpg',
21
+ mimeType: 'image/jpeg',
22
+ fileSize: 2048,
23
+ storageKey: 'uploads/hero.jpg',
24
+ altText: 'Hero image',
25
+ title: 'Hero',
26
+ width: 1200,
27
+ height: 800,
28
+ createdAt: new Date('2026-01-01T00:00:00.000Z'),
29
+ updatedAt: new Date('2026-01-02T00:00:00.000Z'),
30
+ },
31
+ ],
32
+ count: async () => 1,
33
+ },
34
+ document: {
35
+ findMany: async (args) => {
36
+ if (args?.where?.collection === 'forms') {
37
+ return [
38
+ {
39
+ id: 'form-1',
40
+ title: 'Legacy Title',
41
+ data: {
42
+ name: 'Contact Form',
43
+ description: 'Contact us',
44
+ fields: [{ name: 'email' }, { name: 'message' }],
45
+ },
46
+ createdAt: new Date('2026-01-03T00:00:00.000Z'),
47
+ updatedAt: new Date('2026-01-04T00:00:00.000Z'),
48
+ },
49
+ ];
50
+ }
51
+ return [
52
+ {
53
+ id: 'page-1',
54
+ collection: 'pages',
55
+ title: 'Home',
56
+ slug: 'home',
57
+ data: {
58
+ title: 'Home',
59
+ slug: 'home',
60
+ metaTitle: 'Home Meta',
61
+ metaDescription: 'Home description',
62
+ canonical: 'https://example.com/',
63
+ schemaType: 'WebPage',
64
+ },
65
+ structuredData: { score: 82, issues: ['missing-og'] },
66
+ updatedAt: new Date('2026-01-05T00:00:00.000Z'),
67
+ },
68
+ ];
69
+ },
70
+ },
71
+ formSubmission: {
72
+ findMany: async () => [
73
+ {
74
+ id: 'submission-1',
75
+ data: { name: 'Ada', email: 'ada@example.com', message: 'Hello' },
76
+ attribution: { source: 'google', medium: 'cpc' },
77
+ status: 'new',
78
+ submittedAt: new Date('2026-01-06T00:00:00.000Z'),
79
+ },
80
+ ],
81
+ count: async () => 1,
82
+ },
83
+ redirect: {
84
+ findMany: async () => [
85
+ {
86
+ id: 'redirect-1',
87
+ source: '/old',
88
+ destination: '/new',
89
+ statusCode: 301,
90
+ createdAt: new Date('2026-01-07T00:00:00.000Z'),
91
+ updatedAt: new Date('2026-01-08T00:00:00.000Z'),
92
+ },
93
+ ],
94
+ },
95
+ };
96
+ }
97
+ describe('admin API response contracts', () => {
98
+ beforeEach(() => {
99
+ process.env.CMS_SECRET = SECRET;
100
+ const db = createMockDB();
101
+ initDB(db);
102
+ });
103
+ it('normalizes media list items to the shape consumed by cms-admin', async () => {
104
+ const handler = handleActuateAPI({ prismaClient: createMockDB() });
105
+ const response = await handler(new Request('https://example.com/api/cms/media', {
106
+ headers: await authHeaders(),
107
+ }));
108
+ await expect(response.json()).resolves.toMatchObject({
109
+ data: {
110
+ data: [
111
+ {
112
+ id: 'media-1',
113
+ name: 'hero.jpg',
114
+ type: 'image/jpeg',
115
+ size: '2.0 KB',
116
+ sizeBytes: 2048,
117
+ url: '',
118
+ altTag: 'Hero image',
119
+ title: 'Hero',
120
+ dimensions: '1200x800',
121
+ },
122
+ ],
123
+ total: 1,
124
+ },
125
+ });
126
+ });
127
+ it('normalizes forms, submissions, redirects, and SEO pages for cms-admin', async () => {
128
+ const handler = handleActuateAPI({ prismaClient: createMockDB() });
129
+ const headers = await authHeaders();
130
+ const [forms, submissions, redirects, seoPages] = await Promise.all([
131
+ handler(new Request('https://example.com/api/cms/forms', { headers })),
132
+ handler(new Request('https://example.com/api/cms/forms/form-1/submissions', { headers })),
133
+ handler(new Request('https://example.com/api/cms/redirects', { headers })),
134
+ handler(new Request('https://example.com/api/cms/seo/pages', { headers })),
135
+ ]);
136
+ await expect(forms.json()).resolves.toMatchObject({
137
+ data: [{ id: 'form-1', name: 'Contact Form', fields: 2, submissions: 1 }],
138
+ });
139
+ await expect(submissions.json()).resolves.toMatchObject({
140
+ data: {
141
+ submissions: [
142
+ {
143
+ id: 'submission-1',
144
+ name: 'Ada',
145
+ email: 'ada@example.com',
146
+ message: 'Hello',
147
+ status: 'new',
148
+ attribution: { source: 'google', medium: 'cpc' },
149
+ },
150
+ ],
151
+ total: 1,
152
+ },
153
+ });
154
+ await expect(redirects.json()).resolves.toMatchObject({
155
+ data: [{ id: 'redirect-1', from: '/old', to: '/new', type: '301' }],
156
+ });
157
+ await expect(seoPages.json()).resolves.toMatchObject({
158
+ data: [
159
+ {
160
+ id: 'page-1',
161
+ url: '/',
162
+ title: 'Home',
163
+ score: 82,
164
+ issues: 1,
165
+ metaTitle: 'Home Meta',
166
+ metaDescription: 'Home description',
167
+ },
168
+ ],
169
+ });
170
+ });
171
+ });
172
+ //# sourceMappingURL=admin-contracts.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-contracts.test.js","sourceRoot":"","sources":["../../../src/__tests__/api/admin-contracts.test.ts"],"names":[],"mappings":"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"}
@@ -79,5 +79,25 @@ describe('withActuateCMS', () => {
79
79
  },
80
80
  });
81
81
  });
82
+ it('adds the Vercel Blob image remote pattern once', () => {
83
+ const config = withActuateCMS(createFullConfig(), {
84
+ images: {
85
+ remotePatterns: [
86
+ { protocol: 'https', hostname: 'images.example.com' },
87
+ ],
88
+ },
89
+ });
90
+ expect(config.images).toEqual({
91
+ remotePatterns: [
92
+ { protocol: 'https', hostname: 'images.example.com' },
93
+ { protocol: 'https', hostname: '**.blob.vercel-storage.com' },
94
+ ],
95
+ });
96
+ const secondPass = withActuateCMS(createFullConfig(), config);
97
+ expect(secondPass.images.remotePatterns).toEqual([
98
+ { protocol: 'https', hostname: 'images.example.com' },
99
+ { protocol: 'https', hostname: '**.blob.vercel-storage.com' },
100
+ ]);
101
+ });
82
102
  });
83
103
  //# sourceMappingURL=next.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"next.test.js","sourceRoot":"","sources":["../../src/__tests__/next.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"next.test.js","sourceRoot":"","sources":["../../src/__tests__/next.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/api/handlers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AA4N7C,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAS9E;AAiED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAmiHzD"}
1
+ {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/api/handlers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AA8T7C,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAS9E;AAiED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA0jHzD"}
@@ -58,6 +58,97 @@ function clampPageSize(raw, max = 100, fallback = 20) {
58
58
  const n = Number(raw) || fallback;
59
59
  return Math.min(Math.max(1, n), max);
60
60
  }
61
+ function mediaUrl(storageKey) {
62
+ const value = String(storageKey ?? '');
63
+ if (!value)
64
+ return '';
65
+ return value.startsWith('http://') || value.startsWith('https://') || value.startsWith('/') ? value : '';
66
+ }
67
+ function normalizeMediaItem(media) {
68
+ const width = typeof media.width === 'number' ? media.width : null;
69
+ const height = typeof media.height === 'number' ? media.height : null;
70
+ return {
71
+ ...media,
72
+ name: media.filename ?? media.name ?? '',
73
+ type: media.mimeType ?? media.type ?? '',
74
+ size: formatBytes(Number(media.fileSize ?? media.sizeBytes ?? 0)),
75
+ sizeBytes: Number(media.fileSize ?? media.sizeBytes ?? 0),
76
+ date: media.createdAt ?? media.updatedAt ?? null,
77
+ url: mediaUrl(media.storageKey ?? media.url),
78
+ dimensions: width && height ? `${width}x${height}` : undefined,
79
+ format: typeof media.mimeType === 'string' ? media.mimeType.split('/')[1] : undefined,
80
+ altTag: media.altText ?? media.altTag ?? '',
81
+ title: media.title ?? '',
82
+ };
83
+ }
84
+ function asRecord(value) {
85
+ return value && typeof value === 'object' && !Array.isArray(value)
86
+ ? value
87
+ : {};
88
+ }
89
+ function normalizeFormDocument(form, submissions = 0) {
90
+ const data = asRecord(form.data);
91
+ const fields = Array.isArray(data.fields) ? data.fields.length : Number(data.fields ?? 0);
92
+ return {
93
+ ...form,
94
+ name: data.name ?? form.name ?? form.title ?? 'Untitled form',
95
+ description: data.description ?? form.description ?? '',
96
+ fields,
97
+ submissions,
98
+ };
99
+ }
100
+ function normalizeSubmission(submission) {
101
+ const data = asRecord(submission.data);
102
+ const attribution = asRecord(submission.attribution);
103
+ return {
104
+ ...submission,
105
+ name: String(data.name ?? submission.name ?? ''),
106
+ email: String(data.email ?? submission.email ?? ''),
107
+ phone: data.phone ?? submission.phone,
108
+ message: String(data.message ?? submission.message ?? ''),
109
+ status: submission.status ?? 'new',
110
+ attribution: {
111
+ source: String(attribution.source ?? '(direct)'),
112
+ medium: String(attribution.medium ?? '(none)'),
113
+ campaign: String(attribution.campaign ?? ''),
114
+ term: String(attribution.term ?? ''),
115
+ content: String(attribution.content ?? ''),
116
+ landingPage: String(attribution.landingPage ?? ''),
117
+ referrer: String(attribution.referrer ?? ''),
118
+ deviceType: attribution.deviceType ?? 'Desktop',
119
+ clickIds: asRecord(attribution.clickIds),
120
+ capturedAt: attribution.capturedAt ?? submission.submittedAt ?? submission.createdAt ?? null,
121
+ },
122
+ };
123
+ }
124
+ function normalizeRedirect(redirect) {
125
+ return {
126
+ ...redirect,
127
+ from: redirect.from ?? redirect.source ?? '',
128
+ to: redirect.to ?? redirect.destination ?? '',
129
+ type: String(redirect.type ?? redirect.statusCode ?? 301),
130
+ hits: Number(redirect.hits ?? 0),
131
+ active: redirect.active ?? true,
132
+ };
133
+ }
134
+ function normalizeSeoPage(page) {
135
+ const data = asRecord(page.data);
136
+ const structuredData = asRecord(page.structuredData);
137
+ const issueValue = structuredData.issues ?? data.issues;
138
+ const issueCount = Array.isArray(issueValue) ? issueValue.length : Number(issueValue ?? 0);
139
+ const slug = String(data.slug ?? page.slug ?? page.id ?? '');
140
+ return {
141
+ ...page,
142
+ url: data.url ?? (slug === 'home' ? '/' : `/${slug}`),
143
+ title: data.title ?? page.title ?? 'Untitled',
144
+ score: Number(structuredData.score ?? data.score ?? 0),
145
+ issues: issueCount,
146
+ metaTitle: data.metaTitle ?? '',
147
+ metaDescription: data.metaDescription ?? '',
148
+ canonical: data.canonical ?? '',
149
+ schemaType: data.schemaType ?? structuredData.schemaType ?? '',
150
+ };
151
+ }
61
152
  function hasModel(d, name) {
62
153
  try {
63
154
  return d[name] && typeof d[name].findMany === 'function';
@@ -68,6 +159,7 @@ function hasModel(d, name) {
68
159
  }
69
160
  function modelNotAvailable(name) {
70
161
  return errorResponse(`The "${name}" model is not available in your Prisma schema. `
162
+ + 'Run `actuate db:init` for new schemas, or carefully update the existing Actuate block, create/apply a Prisma migration, then regenerate Prisma Client. '
71
163
  + 'See https://actuatecms.dev/docs/database-setup for required models.', 501);
72
164
  }
73
165
  async function safeCount(model, where) {
@@ -876,8 +968,16 @@ export function registerCMSRoutes(router) {
876
968
  db().media.findMany({ where, skip, take: pageSize, orderBy: { createdAt: 'desc' } }),
877
969
  db().media.count({ where }),
878
970
  ]);
971
+ const normalized = items.map(normalizeMediaItem);
879
972
  return json({
880
- data: { items, total, page, pageSize, totalPages: Math.ceil(total / pageSize) },
973
+ data: {
974
+ data: normalized,
975
+ items: normalized,
976
+ total,
977
+ page,
978
+ pageSize,
979
+ totalPages: Math.ceil(total / pageSize),
980
+ },
881
981
  });
882
982
  }
883
983
  catch (err) {
@@ -1629,11 +1729,18 @@ export function registerCMSRoutes(router) {
1629
1729
  const auth = await requireAuth(request);
1630
1730
  if (auth.error)
1631
1731
  return auth.error;
1632
- const forms = await db().document.findMany({
1732
+ const d = db();
1733
+ const forms = await d.document.findMany({
1633
1734
  where: { collection: 'forms', deletedAt: null },
1634
1735
  orderBy: { createdAt: 'desc' },
1635
1736
  });
1636
- return json({ data: forms });
1737
+ const normalized = await Promise.all(forms.map(async (form) => {
1738
+ const submissions = hasModel(d, 'formSubmission')
1739
+ ? await d.formSubmission.count({ where: { formId: form.id } })
1740
+ : 0;
1741
+ return normalizeFormDocument(form, submissions);
1742
+ }));
1743
+ return json({ data: normalized });
1637
1744
  }
1638
1745
  catch (err) {
1639
1746
  return internalError(err);
@@ -1658,7 +1765,13 @@ export function registerCMSRoutes(router) {
1658
1765
  db().formSubmission.count({ where: { formId: params.id } }),
1659
1766
  ]);
1660
1767
  return json({
1661
- data: { submissions, total, page, pageSize, totalPages: Math.ceil(total / pageSize) },
1768
+ data: {
1769
+ submissions: submissions.map(normalizeSubmission),
1770
+ total,
1771
+ page,
1772
+ pageSize,
1773
+ totalPages: Math.ceil(total / pageSize),
1774
+ },
1662
1775
  });
1663
1776
  }
1664
1777
  catch (err) {
@@ -1743,7 +1856,7 @@ export function registerCMSRoutes(router) {
1743
1856
  if (auth.error)
1744
1857
  return auth.error;
1745
1858
  const redirects = await db().redirect.findMany({ orderBy: { createdAt: 'desc' } });
1746
- return json({ data: redirects });
1859
+ return json({ data: redirects.map(normalizeRedirect) });
1747
1860
  }
1748
1861
  catch (err) {
1749
1862
  return internalError(err);
@@ -1757,8 +1870,9 @@ export function registerCMSRoutes(router) {
1757
1870
  if (auth.session.role !== 'ADMIN')
1758
1871
  return errorResponse('Admin access required', 403);
1759
1872
  const body = await request.json();
1760
- const source = String(body.source ?? '').trim();
1761
- const destination = String(body.destination ?? '').trim();
1873
+ const source = String(body.source ?? body.from ?? '').trim();
1874
+ const destination = String(body.destination ?? body.to ?? '').trim();
1875
+ const requestedStatus = Number(body.statusCode ?? body.type);
1762
1876
  if (!source || !destination) {
1763
1877
  return errorResponse('source and destination are required', 400);
1764
1878
  }
@@ -1777,12 +1891,12 @@ export function registerCMSRoutes(router) {
1777
1891
  data: {
1778
1892
  source,
1779
1893
  destination,
1780
- statusCode: [301, 302, 307, 308].includes(Number(body.statusCode)) ? Number(body.statusCode) : 301,
1894
+ statusCode: [301, 302, 307, 308].includes(requestedStatus) ? requestedStatus : 301,
1781
1895
  isRegex: body.isRegex === true,
1782
1896
  notes: typeof body.notes === 'string' ? body.notes : null,
1783
1897
  },
1784
1898
  });
1785
- return json({ data: redirect }, 201);
1899
+ return json({ data: normalizeRedirect(redirect) }, 201);
1786
1900
  }
1787
1901
  catch (err) {
1788
1902
  return internalError(err, 'create redirect');
@@ -1821,7 +1935,7 @@ export function registerCMSRoutes(router) {
1821
1935
  },
1822
1936
  orderBy: { updatedAt: 'desc' },
1823
1937
  });
1824
- return json({ data: pages });
1938
+ return json({ data: pages.map(normalizeSeoPage) });
1825
1939
  }
1826
1940
  catch (err) {
1827
1941
  return internalError(err);