@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.
- package/dist/__tests__/api/admin-contracts.test.d.ts +2 -0
- package/dist/__tests__/api/admin-contracts.test.d.ts.map +1 -0
- package/dist/__tests__/api/admin-contracts.test.js +172 -0
- package/dist/__tests__/api/admin-contracts.test.js.map +1 -0
- package/dist/__tests__/next.test.js +20 -0
- package/dist/__tests__/next.test.js.map +1 -1
- package/dist/api/handlers.d.ts.map +1 -1
- package/dist/api/handlers.js +124 -10
- package/dist/api/handlers.js.map +1 -1
- package/dist/next.d.ts.map +1 -1
- package/dist/next.js +16 -3
- package/dist/next.js.map +1 -1
- package/package.json +1 -1
- package/prisma/cms-schema.prisma +69 -0
|
@@ -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":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAG5C,SAAS,gBAAgB;IACvB,OAAO;QACL,QAAQ,EAAE;YACR,QAAQ,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE;YAClC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE;SAC/B;QACD,IAAI,EAAE;YACJ,SAAS,EAAE,EAAE;YACb,WAAW,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;SAC/B;QACD,KAAK,EAAE;YACL,IAAI,EAAE,YAAY;SACnB;QACD,WAAW,EAAE;YACX,KAAK,EAAE;gBACL,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE;gBAC7C,MAAM,EAAE;oBACN,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE;iBACxC;aACF;SACF;QACD,OAAO,EAAE;YACP,eAAe,EAAE;gBACf,IAAI,EAAE,eAAe;gBACrB,KAAK,EAAE,eAAe;gBACtB,MAAM,EAAE;oBACN,QAAQ,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE;iBAC/C;aACF;SACF;QACD,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,oBAAoB;gBAC1B,IAAI,CAAC,MAAM;oBACT,OAAO,MAAM,CAAC;gBAChB,CAAC;aACF;SACF;KACF,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,MAAM,GAAG,cAAc,CAAC,gBAAgB,EAAE,EAAE;YAChD,GAAG,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE;SAC1B,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC;YACzB,QAAQ,EAAE,MAAM;YAChB,kBAAkB,EAAE,YAAY;YAChC,mBAAmB,EAAE,OAAO;YAC5B,eAAe,EAAE,eAAe;SACjC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,MAAM,GAAG,cAAc,CAAC,gBAAgB,EAAE,EAAE;YAChD,sBAAsB,EAAE,CAAC,iBAAiB,CAAC;YAC3C,iBAAiB,EAAE,CAAC,kBAAkB,EAAE,yBAAyB,CAAC;SACnE,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;QACnE,MAAM,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QAClE,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;QAC/D,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,MAAM,GAAG,cAAc,CAAC,gBAAgB,EAAE,EAAE;YAChD,YAAY,EAAE;gBACZ,aAAa,EAAE;oBACb,aAAa,EAAE,KAAK;oBACpB,cAAc,EAAE,CAAC,iBAAiB,CAAC;iBACpC;aACF;SACF,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC;YAClC,aAAa,EAAE;gBACb,aAAa,EAAE,KAAK;gBACpB,cAAc,EAAE,CAAC,iBAAiB,CAAC;aACpC;SACF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
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;
|
|
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"}
|
package/dist/api/handlers.js
CHANGED
|
@@ -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: {
|
|
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
|
|
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
|
-
|
|
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: {
|
|
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(
|
|
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);
|