@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.
- package/dist/__tests__/api/stats-seo-cache.test.js +1 -1
- package/dist/__tests__/api/stats-seo-cache.test.js.map +1 -1
- package/dist/api/guarded-db.d.ts +13 -0
- package/dist/api/guarded-db.d.ts.map +1 -0
- package/dist/api/guarded-db.js +58 -0
- package/dist/api/guarded-db.js.map +1 -0
- package/dist/api/handler-factory.d.ts.map +1 -1
- package/dist/api/handler-factory.js +2 -1
- package/dist/api/handler-factory.js.map +1 -1
- package/dist/api/handlers.d.ts +5 -3
- package/dist/api/handlers.d.ts.map +1 -1
- package/dist/api/handlers.js +54 -13068
- package/dist/api/handlers.js.map +1 -1
- package/dist/api/route-helpers.d.ts +320 -0
- package/dist/api/route-helpers.d.ts.map +1 -0
- package/dist/api/route-helpers.js +1307 -0
- package/dist/api/route-helpers.js.map +1 -0
- package/dist/api/routes/ai-seo.d.ts +3 -0
- package/dist/api/routes/ai-seo.d.ts.map +1 -0
- package/dist/api/routes/ai-seo.js +377 -0
- package/dist/api/routes/ai-seo.js.map +1 -0
- package/dist/api/routes/ai-settings.d.ts +3 -0
- package/dist/api/routes/ai-settings.d.ts.map +1 -0
- package/dist/api/routes/ai-settings.js +764 -0
- package/dist/api/routes/ai-settings.js.map +1 -0
- package/dist/api/routes/auth.d.ts +3 -0
- package/dist/api/routes/auth.d.ts.map +1 -0
- package/dist/api/routes/auth.js +787 -0
- package/dist/api/routes/auth.js.map +1 -0
- package/dist/api/routes/collab.d.ts +3 -0
- package/dist/api/routes/collab.d.ts.map +1 -0
- package/dist/api/routes/collab.js +426 -0
- package/dist/api/routes/collab.js.map +1 -0
- package/dist/api/routes/dashboard.d.ts +7 -0
- package/dist/api/routes/dashboard.d.ts.map +1 -0
- package/dist/api/routes/dashboard.js +540 -0
- package/dist/api/routes/dashboard.js.map +1 -0
- package/dist/api/routes/documents.d.ts +3 -0
- package/dist/api/routes/documents.d.ts.map +1 -0
- package/dist/api/routes/documents.js +180 -0
- package/dist/api/routes/documents.js.map +1 -0
- package/dist/api/routes/folders.d.ts +3 -0
- package/dist/api/routes/folders.d.ts.map +1 -0
- package/dist/api/routes/folders.js +195 -0
- package/dist/api/routes/folders.js.map +1 -0
- package/dist/api/routes/forms.d.ts +3 -0
- package/dist/api/routes/forms.d.ts.map +1 -0
- package/dist/api/routes/forms.js +661 -0
- package/dist/api/routes/forms.js.map +1 -0
- package/dist/api/routes/globals.d.ts +3 -0
- package/dist/api/routes/globals.d.ts.map +1 -0
- package/dist/api/routes/globals.js +131 -0
- package/dist/api/routes/globals.js.map +1 -0
- package/dist/api/routes/media.d.ts +3 -0
- package/dist/api/routes/media.d.ts.map +1 -0
- package/dist/api/routes/media.js +521 -0
- package/dist/api/routes/media.js.map +1 -0
- package/dist/api/routes/meta.d.ts +3 -0
- package/dist/api/routes/meta.d.ts.map +1 -0
- package/dist/api/routes/meta.js +96 -0
- package/dist/api/routes/meta.js.map +1 -0
- package/dist/api/routes/page-templates.d.ts +3 -0
- package/dist/api/routes/page-templates.d.ts.map +1 -0
- package/dist/api/routes/page-templates.js +265 -0
- package/dist/api/routes/page-templates.js.map +1 -0
- package/dist/api/routes/presence.d.ts +3 -0
- package/dist/api/routes/presence.d.ts.map +1 -0
- package/dist/api/routes/presence.js +35 -0
- package/dist/api/routes/presence.js.map +1 -0
- package/dist/api/routes/preview.d.ts +3 -0
- package/dist/api/routes/preview.d.ts.map +1 -0
- package/dist/api/routes/preview.js +111 -0
- package/dist/api/routes/preview.js.map +1 -0
- package/dist/api/routes/redirects.d.ts +3 -0
- package/dist/api/routes/redirects.d.ts.map +1 -0
- package/dist/api/routes/redirects.js +790 -0
- package/dist/api/routes/redirects.js.map +1 -0
- package/dist/api/routes/saved-sections.d.ts +3 -0
- package/dist/api/routes/saved-sections.d.ts.map +1 -0
- package/dist/api/routes/saved-sections.js +1036 -0
- package/dist/api/routes/saved-sections.js.map +1 -0
- package/dist/api/routes/scheduling.d.ts +3 -0
- package/dist/api/routes/scheduling.d.ts.map +1 -0
- package/dist/api/routes/scheduling.js +373 -0
- package/dist/api/routes/scheduling.js.map +1 -0
- package/dist/api/routes/sections.d.ts +3 -0
- package/dist/api/routes/sections.d.ts.map +1 -0
- package/dist/api/routes/sections.js +500 -0
- package/dist/api/routes/sections.js.map +1 -0
- package/dist/api/routes/security-center.d.ts +3 -0
- package/dist/api/routes/security-center.d.ts.map +1 -0
- package/dist/api/routes/security-center.js +71 -0
- package/dist/api/routes/security-center.js.map +1 -0
- package/dist/api/routes/seo-public.d.ts +14 -0
- package/dist/api/routes/seo-public.d.ts.map +1 -0
- package/dist/api/routes/seo-public.js +930 -0
- package/dist/api/routes/seo-public.js.map +1 -0
- package/dist/api/routes/seo.d.ts +8 -0
- package/dist/api/routes/seo.d.ts.map +1 -0
- package/dist/api/routes/seo.js +848 -0
- package/dist/api/routes/seo.js.map +1 -0
- package/dist/api/routes/system.d.ts +3 -0
- package/dist/api/routes/system.d.ts.map +1 -0
- package/dist/api/routes/system.js +340 -0
- package/dist/api/routes/system.js.map +1 -0
- package/dist/api/routes/url-resolution.d.ts +3 -0
- package/dist/api/routes/url-resolution.d.ts.map +1 -0
- package/dist/api/routes/url-resolution.js +457 -0
- package/dist/api/routes/url-resolution.js.map +1 -0
- package/dist/api/routes/users.d.ts +3 -0
- package/dist/api/routes/users.d.ts.map +1 -0
- package/dist/api/routes/users.js +1162 -0
- package/dist/api/routes/users.js.map +1 -0
- package/dist/api/routes/webhooks.d.ts +3 -0
- package/dist/api/routes/webhooks.d.ts.map +1 -0
- package/dist/api/routes/webhooks.js +338 -0
- package/dist/api/routes/webhooks.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-templates.d.ts","sourceRoot":"","sources":["../../../src/api/routes/page-templates.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAiB7C,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAwQlE"}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { logEvent } from '../../security/audit.js';
|
|
2
|
+
import { BUILT_IN_TEMPLATES } from '../../page-builder/templates.js';
|
|
3
|
+
import { validateTree } from '../../page-builder/validate.js';
|
|
4
|
+
import { guardedDb } from '../guarded-db.js';
|
|
5
|
+
import { json, errorResponse, internalError, hasModel, modelNotAvailable, safeCount, requireAuth, ADMIN_ROLES, requireRole, } from '../route-helpers.js';
|
|
6
|
+
export function registerPageTemplateRoutes(router) {
|
|
7
|
+
const db = guardedDb;
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Page Templates
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
async function seedBuiltInTemplates(d) {
|
|
12
|
+
for (const [key, template] of Object.entries(BUILT_IN_TEMPLATES)) {
|
|
13
|
+
await d.pageTemplate.upsert({
|
|
14
|
+
where: { id: `builtin-${key}` },
|
|
15
|
+
create: {
|
|
16
|
+
id: `builtin-${key}`,
|
|
17
|
+
name: template.name,
|
|
18
|
+
description: template.description,
|
|
19
|
+
category: template.category,
|
|
20
|
+
tree: template.tree,
|
|
21
|
+
builtIn: true,
|
|
22
|
+
},
|
|
23
|
+
update: {
|
|
24
|
+
name: template.name,
|
|
25
|
+
description: template.description,
|
|
26
|
+
category: template.category,
|
|
27
|
+
tree: template.tree,
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
router.get('/page-templates', async (request) => {
|
|
33
|
+
try {
|
|
34
|
+
const auth = await requireAuth(request);
|
|
35
|
+
if (auth.error)
|
|
36
|
+
return auth.error;
|
|
37
|
+
const roleErr = requireRole(auth.session.role, ADMIN_ROLES);
|
|
38
|
+
if (roleErr)
|
|
39
|
+
return roleErr;
|
|
40
|
+
const d = db();
|
|
41
|
+
if (!hasModel(d, 'pageTemplate'))
|
|
42
|
+
return modelNotAvailable('PageTemplate');
|
|
43
|
+
const url = new URL(request.url, 'http://localhost');
|
|
44
|
+
const category = url.searchParams.get('category');
|
|
45
|
+
const collection = url.searchParams.get('collection');
|
|
46
|
+
const builtInCount = await safeCount(d.pageTemplate, { builtIn: true });
|
|
47
|
+
if (builtInCount === 0) {
|
|
48
|
+
await seedBuiltInTemplates(d);
|
|
49
|
+
}
|
|
50
|
+
const where = {};
|
|
51
|
+
if (category)
|
|
52
|
+
where.category = category;
|
|
53
|
+
const templates = await d.pageTemplate.findMany({
|
|
54
|
+
where,
|
|
55
|
+
orderBy: [{ builtIn: 'desc' }, { updatedAt: 'desc' }],
|
|
56
|
+
});
|
|
57
|
+
// When a `collection` filter is supplied, reorder the response so the
|
|
58
|
+
// suggested templates for that collection bubble to the top. The admin
|
|
59
|
+
// "Create new" picker uses this so blog posts default to `blog-post`,
|
|
60
|
+
// case studies to `case-study`, etc., without forcing the user to scan
|
|
61
|
+
// every template alphabetically.
|
|
62
|
+
if (collection) {
|
|
63
|
+
const { getTemplatesForCollection } = await import('../../collections/presets.js');
|
|
64
|
+
const { primary, alternates } = getTemplatesForCollection(collection);
|
|
65
|
+
const suggestionOrder = new Map();
|
|
66
|
+
if (primary)
|
|
67
|
+
suggestionOrder.set(`builtin-${primary}`, 0);
|
|
68
|
+
alternates.forEach((alt, i) => suggestionOrder.set(`builtin-${alt}`, i + 1));
|
|
69
|
+
templates.sort((a, b) => {
|
|
70
|
+
const ai = suggestionOrder.has(a.id) ? suggestionOrder.get(a.id) : 100;
|
|
71
|
+
const bi = suggestionOrder.has(b.id) ? suggestionOrder.get(b.id) : 100;
|
|
72
|
+
return ai - bi;
|
|
73
|
+
});
|
|
74
|
+
const annotated = templates.map((t) => ({
|
|
75
|
+
...t,
|
|
76
|
+
suggested: suggestionOrder.has(t.id),
|
|
77
|
+
suggestionRank: suggestionOrder.get(t.id) ?? null,
|
|
78
|
+
}));
|
|
79
|
+
return json({
|
|
80
|
+
data: annotated,
|
|
81
|
+
suggestion: {
|
|
82
|
+
collection,
|
|
83
|
+
primary: primary ? `builtin-${primary}` : null,
|
|
84
|
+
alternates: alternates.map((a) => `builtin-${a}`),
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return json({ data: templates });
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
return internalError(err, 'page-templates list');
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
router.get('/page-templates/:id', async (request, params) => {
|
|
95
|
+
try {
|
|
96
|
+
const auth = await requireAuth(request);
|
|
97
|
+
if (auth.error)
|
|
98
|
+
return auth.error;
|
|
99
|
+
const roleErr = requireRole(auth.session.role, ADMIN_ROLES);
|
|
100
|
+
if (roleErr)
|
|
101
|
+
return roleErr;
|
|
102
|
+
const d = db();
|
|
103
|
+
if (!hasModel(d, 'pageTemplate'))
|
|
104
|
+
return modelNotAvailable('PageTemplate');
|
|
105
|
+
const template = await d.pageTemplate.findUnique({ where: { id: params.id } });
|
|
106
|
+
if (!template)
|
|
107
|
+
return errorResponse('Template not found', 404);
|
|
108
|
+
return json({ data: template });
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
return internalError(err, 'page-templates get');
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
router.post('/page-templates', async (request) => {
|
|
115
|
+
try {
|
|
116
|
+
const auth = await requireAuth(request);
|
|
117
|
+
if (auth.error)
|
|
118
|
+
return auth.error;
|
|
119
|
+
const roleErr = requireRole(auth.session.role, ADMIN_ROLES);
|
|
120
|
+
if (roleErr)
|
|
121
|
+
return roleErr;
|
|
122
|
+
const d = db();
|
|
123
|
+
if (!hasModel(d, 'pageTemplate'))
|
|
124
|
+
return modelNotAvailable('PageTemplate');
|
|
125
|
+
const body = (await request.json());
|
|
126
|
+
if (!body.name)
|
|
127
|
+
return errorResponse('name is required', 400);
|
|
128
|
+
if (!body.tree)
|
|
129
|
+
return errorResponse('tree is required', 400);
|
|
130
|
+
const validation = validateTree(body.tree);
|
|
131
|
+
if (!validation.valid) {
|
|
132
|
+
return errorResponse(`Invalid tree: ${validation.errors.join('; ')}`, 400);
|
|
133
|
+
}
|
|
134
|
+
const validCategories = ['landing', 'content', 'utility'];
|
|
135
|
+
const category = body.category || 'content';
|
|
136
|
+
if (!validCategories.includes(category)) {
|
|
137
|
+
return errorResponse('category must be landing, content, or utility', 400);
|
|
138
|
+
}
|
|
139
|
+
const template = await d.pageTemplate.create({
|
|
140
|
+
data: {
|
|
141
|
+
name: body.name,
|
|
142
|
+
description: body.description || null,
|
|
143
|
+
category,
|
|
144
|
+
tree: body.tree,
|
|
145
|
+
thumbnail: body.thumbnail || null,
|
|
146
|
+
builtIn: false,
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
await logEvent({
|
|
150
|
+
event: 'settings_changed',
|
|
151
|
+
userId: auth.session.userId,
|
|
152
|
+
details: { action: 'page_template_created', templateId: template.id, name: template.name },
|
|
153
|
+
});
|
|
154
|
+
return json({ data: template }, 201);
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
return internalError(err, 'page-templates create');
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
router.put('/page-templates/:id', async (request, params) => {
|
|
161
|
+
try {
|
|
162
|
+
const auth = await requireAuth(request);
|
|
163
|
+
if (auth.error)
|
|
164
|
+
return auth.error;
|
|
165
|
+
const roleErr = requireRole(auth.session.role, ADMIN_ROLES);
|
|
166
|
+
if (roleErr)
|
|
167
|
+
return roleErr;
|
|
168
|
+
const d = db();
|
|
169
|
+
if (!hasModel(d, 'pageTemplate'))
|
|
170
|
+
return modelNotAvailable('PageTemplate');
|
|
171
|
+
const existing = await d.pageTemplate.findUnique({ where: { id: params.id } });
|
|
172
|
+
if (!existing)
|
|
173
|
+
return errorResponse('Template not found', 404);
|
|
174
|
+
if (existing.builtIn)
|
|
175
|
+
return errorResponse('Cannot update built-in templates', 403);
|
|
176
|
+
const body = (await request.json());
|
|
177
|
+
const update = {};
|
|
178
|
+
if (body.name !== undefined)
|
|
179
|
+
update.name = body.name;
|
|
180
|
+
if (body.description !== undefined)
|
|
181
|
+
update.description = body.description;
|
|
182
|
+
if (body.thumbnail !== undefined)
|
|
183
|
+
update.thumbnail = body.thumbnail;
|
|
184
|
+
if (body.category !== undefined) {
|
|
185
|
+
const validCategories = ['landing', 'content', 'utility'];
|
|
186
|
+
if (!validCategories.includes(body.category)) {
|
|
187
|
+
return errorResponse('category must be landing, content, or utility', 400);
|
|
188
|
+
}
|
|
189
|
+
update.category = body.category;
|
|
190
|
+
}
|
|
191
|
+
if (body.tree !== undefined) {
|
|
192
|
+
const validation = validateTree(body.tree);
|
|
193
|
+
if (!validation.valid) {
|
|
194
|
+
return errorResponse(`Invalid tree: ${validation.errors.join('; ')}`, 400);
|
|
195
|
+
}
|
|
196
|
+
update.tree = body.tree;
|
|
197
|
+
}
|
|
198
|
+
const template = await d.pageTemplate.update({
|
|
199
|
+
where: { id: params.id },
|
|
200
|
+
data: update,
|
|
201
|
+
});
|
|
202
|
+
await logEvent({
|
|
203
|
+
event: 'settings_changed',
|
|
204
|
+
userId: auth.session.userId,
|
|
205
|
+
details: { action: 'page_template_updated', templateId: template.id, name: template.name },
|
|
206
|
+
});
|
|
207
|
+
return json({ data: template });
|
|
208
|
+
}
|
|
209
|
+
catch (err) {
|
|
210
|
+
return internalError(err, 'page-templates update');
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
router.delete('/page-templates/:id', async (request, params) => {
|
|
214
|
+
try {
|
|
215
|
+
const auth = await requireAuth(request);
|
|
216
|
+
if (auth.error)
|
|
217
|
+
return auth.error;
|
|
218
|
+
const roleErr = requireRole(auth.session.role, ADMIN_ROLES);
|
|
219
|
+
if (roleErr)
|
|
220
|
+
return roleErr;
|
|
221
|
+
const d = db();
|
|
222
|
+
if (!hasModel(d, 'pageTemplate'))
|
|
223
|
+
return modelNotAvailable('PageTemplate');
|
|
224
|
+
const existing = await d.pageTemplate.findUnique({ where: { id: params.id } });
|
|
225
|
+
if (!existing)
|
|
226
|
+
return errorResponse('Template not found', 404);
|
|
227
|
+
if (existing.builtIn)
|
|
228
|
+
return errorResponse('Cannot delete built-in templates', 403);
|
|
229
|
+
await d.pageTemplate.delete({ where: { id: params.id } });
|
|
230
|
+
await logEvent({
|
|
231
|
+
event: 'settings_changed',
|
|
232
|
+
userId: auth.session.userId,
|
|
233
|
+
details: { action: 'page_template_deleted', templateId: existing.id, name: existing.name },
|
|
234
|
+
});
|
|
235
|
+
return json({ data: { deleted: true } });
|
|
236
|
+
}
|
|
237
|
+
catch (err) {
|
|
238
|
+
return internalError(err, 'page-templates delete');
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
router.post('/page-templates/seed', async (request) => {
|
|
242
|
+
try {
|
|
243
|
+
const auth = await requireAuth(request);
|
|
244
|
+
if (auth.error)
|
|
245
|
+
return auth.error;
|
|
246
|
+
const roleErr = requireRole(auth.session.role, ADMIN_ROLES);
|
|
247
|
+
if (roleErr)
|
|
248
|
+
return roleErr;
|
|
249
|
+
const d = db();
|
|
250
|
+
if (!hasModel(d, 'pageTemplate'))
|
|
251
|
+
return modelNotAvailable('PageTemplate');
|
|
252
|
+
await seedBuiltInTemplates(d);
|
|
253
|
+
await logEvent({
|
|
254
|
+
event: 'settings_changed',
|
|
255
|
+
userId: auth.session.userId,
|
|
256
|
+
details: { action: 'page_templates_seeded' },
|
|
257
|
+
});
|
|
258
|
+
return json({ data: { seeded: Object.keys(BUILT_IN_TEMPLATES).length } });
|
|
259
|
+
}
|
|
260
|
+
catch (err) {
|
|
261
|
+
return internalError(err, 'page-templates seed');
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
//# sourceMappingURL=page-templates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page-templates.js","sourceRoot":"","sources":["../../../src/api/routes/page-templates.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presence.d.ts","sourceRoot":"","sources":["../../../src/api/routes/presence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAI7C,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA6B9D"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createSSEPresenceAdapter } from '../../presence/index.js';
|
|
2
|
+
import { json, internalError, requireAuth } from '../route-helpers.js';
|
|
3
|
+
export function registerPresenceRoutes(router) {
|
|
4
|
+
const presenceAdapter = createSSEPresenceAdapter();
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Presence SSE
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
router.get('/presence/:documentId', async (request, params) => {
|
|
9
|
+
try {
|
|
10
|
+
const auth = await requireAuth(request);
|
|
11
|
+
if (auth.error)
|
|
12
|
+
return auth.error;
|
|
13
|
+
return presenceAdapter.handleSSE(request, params.documentId, {
|
|
14
|
+
userId: auth.session.userId,
|
|
15
|
+
name: auth.session.role,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
return internalError(err);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
router.post('/presence/:documentId/heartbeat', async (request, params) => {
|
|
23
|
+
try {
|
|
24
|
+
const auth = await requireAuth(request);
|
|
25
|
+
if (auth.error)
|
|
26
|
+
return auth.error;
|
|
27
|
+
await presenceAdapter.provider.heartbeat(params.documentId, auth.session.userId);
|
|
28
|
+
return json({ data: { ok: true } });
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
return internalError(err);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=presence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presence.js","sourceRoot":"","sources":["../../../src/api/routes/presence.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAA;AAClE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAEtE,MAAM,UAAU,sBAAsB,CAAC,MAAiB;IACtD,MAAM,eAAe,GAAG,wBAAwB,EAAE,CAAA;IAClD,8EAA8E;IAC9E,eAAe;IACf,8EAA8E;IAE9E,MAAM,CAAC,GAAG,CAAC,uBAAuB,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;QAC5D,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,CAAA;YACvC,IAAI,IAAI,CAAC,KAAK;gBAAE,OAAO,IAAI,CAAC,KAAK,CAAA;YACjC,OAAO,eAAe,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,UAAW,EAAE;gBAC5D,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;gBAC3B,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI;aACxB,CAAC,CAAA;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,aAAa,CAAC,GAAG,CAAC,CAAA;QAC3B,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,MAAM,CAAC,IAAI,CAAC,iCAAiC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;QACvE,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,eAAe,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,UAAW,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;YACjF,OAAO,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,CAAA;QACrC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,aAAa,CAAC,GAAG,CAAC,CAAA;QAC3B,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../../src/api/routes/preview.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAa7C,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAgH7D"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { logEvent } from '../../security/audit.js';
|
|
2
|
+
import { createPreviewAdapter } from '../../preview/index.js';
|
|
3
|
+
import { createPreviewStreamResponse } from '../../preview/sse-stream.js';
|
|
4
|
+
import { guardedDb } from '../guarded-db.js';
|
|
5
|
+
import { json, errorResponse, internalError, getSessionSecret, requireAuth, } from '../route-helpers.js';
|
|
6
|
+
export function registerPreviewRoutes(router) {
|
|
7
|
+
const db = guardedDb;
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Preview routes
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
router.post('/preview/token', async (request) => {
|
|
12
|
+
try {
|
|
13
|
+
const auth = await requireAuth(request);
|
|
14
|
+
if (auth.error)
|
|
15
|
+
return auth.error;
|
|
16
|
+
const body = (await request.json());
|
|
17
|
+
if (!body.collection || !body.documentId) {
|
|
18
|
+
return errorResponse('collection and documentId are required', 400);
|
|
19
|
+
}
|
|
20
|
+
// Verify the document actually exists so we don't issue tokens for
|
|
21
|
+
// hypothetical ids — a leaked token would otherwise silently work
|
|
22
|
+
// once a doc with that id appeared.
|
|
23
|
+
const doc = await db().document.findFirst({
|
|
24
|
+
where: { id: body.documentId, collection: body.collection, deletedAt: null },
|
|
25
|
+
select: { id: true, slug: true },
|
|
26
|
+
});
|
|
27
|
+
if (!doc)
|
|
28
|
+
return errorResponse('Document not found', 404);
|
|
29
|
+
const preview = createPreviewAdapter(getSessionSecret(), db());
|
|
30
|
+
const session = await preview.createPreviewSession(body.collection, body.documentId, {
|
|
31
|
+
ttlSeconds: body.ttlSeconds,
|
|
32
|
+
issuedBy: auth.session.userId,
|
|
33
|
+
});
|
|
34
|
+
await logEvent({
|
|
35
|
+
event: 'preview_token_issued',
|
|
36
|
+
userId: auth.session.userId,
|
|
37
|
+
details: {
|
|
38
|
+
collection: body.collection,
|
|
39
|
+
documentId: body.documentId,
|
|
40
|
+
expiresAt: session.expiresAt.toISOString(),
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
return json({
|
|
44
|
+
data: {
|
|
45
|
+
token: session.token,
|
|
46
|
+
expiresAt: session.expiresAt,
|
|
47
|
+
slug: doc.slug,
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
return internalError(err, 'create preview token');
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
router.get('/preview/:collection/:id', async (request, params) => {
|
|
56
|
+
try {
|
|
57
|
+
const url = new URL(request.url);
|
|
58
|
+
const token = url.searchParams.get('token');
|
|
59
|
+
if (!token)
|
|
60
|
+
return errorResponse('Preview token required', 401);
|
|
61
|
+
const preview = createPreviewAdapter(getSessionSecret(), db());
|
|
62
|
+
const session = await preview.validatePreviewToken(token);
|
|
63
|
+
if (!session)
|
|
64
|
+
return errorResponse('Invalid or expired preview token', 401);
|
|
65
|
+
if (session.collection !== params.collection || session.documentId !== params.id) {
|
|
66
|
+
return errorResponse('Token does not match requested resource', 403);
|
|
67
|
+
}
|
|
68
|
+
const data = await preview.getPreviewData(session);
|
|
69
|
+
if (!data)
|
|
70
|
+
return errorResponse('Document not found', 404);
|
|
71
|
+
return json({ data });
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
return internalError(err, 'fetch preview data');
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
/**
|
|
78
|
+
* Server-Sent Events stream for live preview. The admin's `LivePreview`
|
|
79
|
+
* panel opens an `EventSource('/api/cms/preview/stream?token=...')` and
|
|
80
|
+
* relays every `event: update` frame to the previewed iframe through
|
|
81
|
+
* `postMessage`. Token-based auth (instead of cookies) because
|
|
82
|
+
* `EventSource` cannot send arbitrary headers.
|
|
83
|
+
*
|
|
84
|
+
* Emits:
|
|
85
|
+
* - `event: ready` once on connection (handshake).
|
|
86
|
+
* - `event: update` whenever the underlying draft document is updated
|
|
87
|
+
* (see `PUT /collections/:slug/:id` for the emit site).
|
|
88
|
+
* - `: heartbeat\n\n` keepalive comments every 25 s so proxies don't
|
|
89
|
+
* reap idle connections.
|
|
90
|
+
*
|
|
91
|
+
* Closes cleanly after 4 minutes so platform idle caps don't terminate
|
|
92
|
+
* the connection mid-event; EventSource will auto-reconnect.
|
|
93
|
+
*/
|
|
94
|
+
router.get('/preview/stream', async (request) => {
|
|
95
|
+
try {
|
|
96
|
+
const url = new URL(request.url);
|
|
97
|
+
const token = url.searchParams.get('token');
|
|
98
|
+
if (!token)
|
|
99
|
+
return errorResponse('Preview token required', 401);
|
|
100
|
+
const preview = createPreviewAdapter(getSessionSecret(), db());
|
|
101
|
+
const session = await preview.validatePreviewToken(token);
|
|
102
|
+
if (!session)
|
|
103
|
+
return errorResponse('Invalid or expired preview token', 401);
|
|
104
|
+
return createPreviewStreamResponse(session);
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
return internalError(err, 'open preview stream');
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=preview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview.js","sourceRoot":"","sources":["../../../src/api/routes/preview.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redirects.d.ts","sourceRoot":"","sources":["../../../src/api/routes/redirects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAiB7C,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAmyB9D"}
|