Das Vorschau-System von EmDash ermöglicht es Redakteuren, unveröffentlichte Inhalte über sichere, zeitlich begrenzte URLs anzuzeigen. Vorschau-Links verwenden HMAC-SHA256-signierte Tokens, die Sie mit Prüfern teilen können, ohne Ihren gesamten Entwurfsinhalt offenzulegen.
Wie es funktioniert
- Der Administrator generiert eine Vorschau-URL für einen Entwurfsbeitrag
- Die URL enthält einen signierten
_previewQuery-Parameter mit einer Ablaufzeit - Die Middleware von EmDash verifiziert automatisch das Token und richtet den Request-Kontext ein
- Ihr Template-Code ruft
getEmDashEntry()wie gewohnt auf — Entwurfsinhalte werden automatisch bereitgestellt
Die Vorschau ist implizit. Die Middleware verifiziert das Token und die Query-Funktionen lesen es über AsyncLocalStorage, sodass derselbe Template-Code während einer Vorschau Entwurfsinhalte und ansonsten veröffentlichte Inhalte bereitstellt.
Einrichtung der Vorschau
Die Vorschau funktioniert, sobald EmDash installiert ist. Bei der ersten Verwendung generiert EmDash ein standortspezifisches Vorschau-Secret und speichert es in der Datenbank, sodass im Normalfall keine Konfiguration erforderlich ist.
Setzen Sie EMDASH_PREVIEW_SECRET in Ihrer Umgebung nur, wenn Sie:
- Das Secret über mehrere Prozesse hinweg teilen müssen (z.B. einen separaten Vorschau-Worker, der URLs signiert und sie zur Verifizierung an Ihre Hauptseite sendet)
- Das Secret aus Compliance-/Audit-Gründen auf einen von Ihnen kontrollierten Wert festlegen möchten
- Bei der Wiederherstellung aus einem Backup zu einem bekannten Wert migrieren möchten
# Optional: Überschreibt das automatisch generierte Secret
EMDASH_PREVIEW_SECRET="your-random-secret-key-here"
Wenn gesetzt, hat der Umgebungswert Vorrang vor dem in der Datenbank gespeicherten Wert.
Bestehende Templates funktionieren automatisch mit der Vorschau, wie auf der folgenden Seite:
---
import { getEmDashEntry } from "emdash";
const { slug } = Astro.params;
// Keine spezielle Vorschau-Behandlung erforderlich — die Middleware
// erkennt _preview Tokens und stellt Entwurfsinhalte automatisch bereit
const { entry, isPreview, error } = await getEmDashEntry("posts", slug);
if (error) {
return new Response("Server error", { status: 500 });
}
if (!entry) {
return Astro.redirect("/404");
}
---
{isPreview && (
<div class="preview-banner">
Sie sehen eine Vorschau. Dieser Inhalt ist nicht veröffentlicht.
</div>
)}
<article>
<h1>{entry.data.title}</h1>
</article>
Das isPreview Flag ist true, wenn Entwurfsinhalte über ein gültiges Vorschau-Token bereitgestellt werden.
Generieren von Vorschau-URLs
Verwenden Sie getPreviewUrl(), um Vorschau-Links zu erstellen. Die Funktion nimmt das Secret als explizites Argument:
import { getPreviewUrl } from "emdash";
const previewUrl = await getPreviewUrl({
collection: "posts",
id: "my-draft-post",
secret: import.meta.env.EMDASH_PREVIEW_SECRET,
expiresIn: "1h",
});
// Gibt zurück: /posts/my-draft-post?_preview=eyJjaWQ...
Wenn EMDASH_PREVIEW_SECRET nicht gesetzt ist, generiert und speichert EmDash automatisch ein standortspezifisches Secret in der Datenbank zur Token-Verifizierung. Der getPreviewUrl() Template-Helper erfordert dennoch, dass Sie das Secret explizit übergeben — fixieren Sie Ihre Umgebungsvariable, wenn Sie es von Seiten-Templates aus aufrufen. Die meisten Websites verwenden stattdessen die Schaltfläche “Vorschau-Link generieren” der Admin-Oberfläche, die über die API geht und das aufgelöste Secret automatisch verwendet.
Übergeben Sie baseUrl, um eine absolute URL zu generieren:
const fullUrl = await getPreviewUrl({
collection: "posts",
id: "my-draft-post",
secret: import.meta.env.EMDASH_PREVIEW_SECRET,
baseUrl: "https://example.com",
});
// Gibt zurück: https://example.com/posts/my-draft-post?_preview=eyJjaWQ...
Übergeben Sie pathPattern, um eine URL mit einem benutzerdefinierten Pfad zu generieren:
const blogUrl = await getPreviewUrl({
collection: "posts",
id: "my-draft-post",
secret: import.meta.env.EMDASH_PREVIEW_SECRET,
pathPattern: "/blog/{id}",
});
// Gibt zurück: /blog/my-draft-post?_preview=eyJjaWQ...
Locale-bewusste Pfade
pathPattern unterstützt auch einen {locale} Platzhalter. Übergeben Sie ein leeres locale, wenn der Eintrag in der Standardsprache ist und prefixDefaultLocale false ist; benachbarte Schrägstriche, die durch den leeren Wert übrig bleiben, werden automatisch zusammengeführt.
Das folgende Beispiel erstellt eine locale-präfixierte Vorschau-URL:
await getPreviewUrl({
collection: "posts",
id: "hello",
secret,
pathPattern: "/{locale}/{id}",
locale: "pt-br",
});
// Gibt zurück: /pt-br/hello?_preview=...
await getPreviewUrl({
collection: "posts",
id: "hello",
secret,
pathPattern: "/{locale}/{id}",
locale: "", // Standardsprache, kein Präfix
});
// Gibt zurück: /hello?_preview=...
Der “Auf Website ansehen”-Link des Administrators geht über POST /_emdash/api/content/{collection}/{id}/preview-url, der die Locale des Eintrags liest, die i18n-Konfiguration der Website nachschlägt und die locale automatisch bereitstellt. Um das von diesem Endpunkt verwendete Standardmuster zu ändern, setzen Sie EMDASH_PREVIEW_PATH_PATTERN (z.B. /{locale}/{id}) — Request-Bodies haben Vorrang, wenn sie ihr eigenes pathPattern enthalten.
Token-Ablauf
Steuern Sie, wie lange Vorschau-Links gültig bleiben:
// Gültig für 1 Stunde (Standard)
await getPreviewUrl({ ..., expiresIn: "1h" });
// Gültig für 30 Minuten
await getPreviewUrl({ ..., expiresIn: "30m" });
// Gültig für 1 Tag
await getPreviewUrl({ ..., expiresIn: "1d" });
// Gültig für 2 Wochen
await getPreviewUrl({ ..., expiresIn: "2w" });
// Gültig für 3600 Sekunden
await getPreviewUrl({ ..., expiresIn: 3600 });
Unterstützte Einheiten: s (Sekunden), m (Minuten), h (Stunden), d (Tage), w (Wochen).
Verifizieren von Tokens
Verwenden Sie verifyPreviewToken(), um eingehende Vorschau-Anfragen zu validieren:
import { verifyPreviewToken } from "emdash";
// Von einer URL (extrahiert den _preview Query-Parameter)
const result = await verifyPreviewToken({
url: Astro.url,
secret: import.meta.env.EMDASH_PREVIEW_SECRET,
});
// Oder mit einem Token direkt
const result = await verifyPreviewToken({
token: someTokenString,
secret: import.meta.env.EMDASH_PREVIEW_SECRET,
});
Das Ergebnis zeigt an, ob das Token gültig ist:
if (result.valid) {
// Token ist gültig
console.log(result.payload.cid); // "posts:my-draft-post"
console.log(result.payload.exp); // Ablauf-Zeitstempel
console.log(result.payload.iat); // Ausstellungs-Zeitstempel
} else {
// Token ist ungültig
console.log(result.error);
// "none" - kein Token vorhanden
// "malformed" - Token-Struktur ist ungültig
// "invalid" - Signaturverifizierung fehlgeschlagen
// "expired" - Token ist abgelaufen
}
Vorschau-Indikator
Sie können einen visuellen Indikator anzeigen, wenn Inhalte in der Vorschau angezeigt werden. Das isPreview Flag, das von getEmDashEntry zurückgegeben wird, teilt Ihnen mit, wann Entwurfsinhalte bereitgestellt werden:
{isPreview && (
<div class="preview-banner" role="alert">
<strong>Vorschau</strong> — Sie sehen unveröffentlichte Inhalte.
<a href={Astro.url.pathname}>Vorschau beenden</a>
</div>
)}
Hilfsfunktionen
isPreviewRequest(url)
Überprüfen Sie, ob eine URL ein Vorschau-Token enthält:
import { isPreviewRequest } from "emdash";
if (isPreviewRequest(Astro.url)) {
// Vorschau-Anfrage behandeln
}
getPreviewToken(url)
Extrahieren Sie den Token-String aus einer URL:
import { getPreviewToken } from "emdash";
const token = getPreviewToken(Astro.url);
// Gibt den Token-String oder null zurück
parseContentId(contentId)
Parsen Sie eine Content-ID in Collection und ID:
import { parseContentId } from "emdash";
const { collection, id } = parseContentId("posts:my-draft-post");
// { collection: "posts", id: "my-draft-post" }
Token-Sicherheit
Vorschau-Tokens sind signiert und zeitlich begrenzt. Die CLI und die Hilfsfunktionen generieren und verifizieren sie für Sie; Sie konstruieren oder parsen sie nicht von Hand. Ein Token identifiziert einen Eintrag und funktioniert nicht mehr, nachdem es abgelaufen ist.
Vollständiges Beispiel
Die folgende Seite kombiniert Vorschau- und visuelle Bearbeitungsunterstützung in einem vollständigen Blog-Post-Template:
---
import { getEmDashEntry } from "emdash";
import BaseLayout from "../../layouts/Base.astro";
import { PortableText } from "emdash/ui";
const { slug } = Astro.params;
// Vorschau ist automatisch — Middleware behandelt die Token-Verifizierung
const { entry, isPreview, error } = await getEmDashEntry("posts", slug);
if (error) {
return new Response("Server error", { status: 500 });
}
if (!entry) {
return Astro.redirect("/404");
}
---
<BaseLayout title={entry.data.title}>
{isPreview && (
<div class="preview-banner" role="alert">
<strong>Vorschau</strong> — Dieser Inhalt ist nicht veröffentlicht.
</div>
)}
<article {...entry.edit}>
<header>
<h1 {...entry.edit.title}>{entry.data.title}</h1>
{entry.data.publishedAt && (
<time datetime={entry.data.publishedAt.toISOString()}>
{entry.data.publishedAt.toLocaleDateString()}
</time>
)}
{isPreview && !entry.data.publishedAt && (
<span class="draft-indicator">Entwurf</span>
)}
</header>
<div class="content" {...entry.edit.content}>
<PortableText value={entry.data.content} />
</div>
</article>
</BaseLayout>
Beachten Sie die {...entry.edit} und {...entry.edit.title} Spreads — diese fügen data-emdash-ref Attribute hinzu, die die visuelle Bearbeitung für authentifizierte Redakteure ermöglichen. In der Produktion erzeugen sie keine Ausgabe.
API-Referenz
getPreviewUrl(options)
Generieren Sie eine Vorschau-URL mit einem signierten Token.
Optionen:
collection— Collection-Slug (string)id— Content-ID oder Slug (string)secret— Signatur-Secret (string)expiresIn— Token-Gültigkeitsdauer (Standard:"1h")baseUrl— Optionale Basis-URL für absolute LinkspathPattern— URL-Muster mit{collection},{id}und{locale}Platzhaltern (Standard:"/{collection}/{id}")locale— Wert, der für{locale}eingesetzt wird. Leerer String lässt das Locale-Segment weg (Schrägstriche werden zusammengeführt).
Gibt zurück: Promise<string>
verifyPreviewToken(options)
Verifizieren Sie ein Vorschau-Token.
Optionen:
secret— Verifizierungs-Secret (string)url— URL, aus der das Token extrahiert werden soll, ODERtoken— Token-String direkt
Gibt zurück: Promise<VerifyPreviewTokenResult>
type VerifyPreviewTokenResult =
| { valid: true; payload: PreviewTokenPayload }
| { valid: false; error: "invalid" | "expired" | "malformed" | "none" };
generatePreviewToken(options)
Generieren Sie ein Token ohne URL-Erstellung.
Optionen:
contentId— Content-ID im Formatcollection:idexpiresIn— Token-Gültigkeitsdauer (Standard:"1h")secret— Signatur-Secret
Gibt zurück: Promise<string>