feat(preview): consume rustycms live-preview drafts
Deploy / verify (push) Successful in 45s
Deploy / deploy (push) Successful in 1m1s

`?preview_draft=<sid>` on any page/post route forwards the signed
session id to the CMS, which renders the in-memory draft instead of
the persisted entry. Layout listens for `rustycms:reload` postMessages
from the admin iframe parent and re-runs `invalidateAll()` to pick up
fresh draft state. CSP `frame-ancestors` is set per-request when
`PUBLIC_PREVIEW_PARENT_ORIGIN` is configured so the admin iframe can
embed the response.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Peter Meier
2026-05-01 09:24:05 +02:00
parent 3bc7273e71
commit 00d42ebb42
5 changed files with 59 additions and 7 deletions
+4 -1
View File
@@ -27,8 +27,9 @@ export const load: PageServerLoad = async ({ params, locals, fetch, url, setHead
const { slug } = params;
const translations = locals.translations ?? {};
const preview = url.searchParams.get('preview') ?? undefined;
const previewDraft = url.searchParams.get('preview_draft') ?? undefined;
if (preview) {
if (preview || previewDraft) {
setHeaders({
'cache-control': 'private, no-store, max-age=0',
'x-robots-tag': 'noindex, nofollow',
@@ -42,6 +43,7 @@ export const load: PageServerLoad = async ({ params, locals, fetch, url, setHead
// Levels). Kein Body-Stripping hier — Page-Detail braucht alle Felder.
depth: 3,
preview,
previewDraft,
});
if (!page) {
@@ -91,6 +93,7 @@ export const load: PageServerLoad = async ({ params, locals, fetch, url, setHead
page,
translations,
preview: !!preview,
previewDraft: !!previewDraft,
seoTitle: page.seoTitle ?? page.headline ?? page.linkName ?? slug,
seoDescription: page.seoDescription ?? page.subheadline ?? '',
robots: (page as { seoMetaRobots?: string }).seoMetaRobots ?? undefined,