Modo de Visualização
O sistema de pré-visualização do EmDash permite que editores visualizem conteúdo não publicado por meio de URLs seguras e com tempo limitado. Os links de pré-visualização usam tokens assinados com HMAC-SHA256 que você pode compartilhar com revisores sem expor todo o seu conteúdo de rascunho.
Como Funciona
Seção intitulada “Como Funciona”- O administrador gera uma URL de pré-visualização para um post em rascunho
- A URL contém um parâmetro de consulta
_previewassinado com um tempo de expiração - O middleware do EmDash verifica automaticamente o token e configura o contexto da requisição
- Seu código de template chama
getEmDashEntry()normalmente — o conteúdo de rascunho é servido automaticamente
A pré-visualização é implícita. Seu código de template não precisa lidar com tokens ou passar opções de pré-visualização — o middleware e as funções de consulta tratam de tudo via AsyncLocalStorage.
Configurando a Pré-visualização
Seção intitulada “Configurando a Pré-visualização”Adicione um segredo de pré-visualização ao seu ambiente:
EMDASH_PREVIEW_SECRET="your-random-secret-key-here"Gere uma string aleatória segura. Este segredo assina e verifica os tokens de pré-visualização.
É isso. Seus templates existentes funcionam com pré-visualização automaticamente:
astro title="src/pages/posts/[...slug].astro"---import { getEmDashEntry } from "emdash";
const { slug } = Astro.params;
// Nenhum tratamento especial de pré-visualização necessário — o middleware// detecta tokens _preview e serve conteúdo de rascunho automaticamenteconst { 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"> You are viewing a preview. This content is not published. </div>)}
<article> <h1>{entry.data.title}</h1></article>O sinalizador isPreview é true quando o conteúdo de rascunho está sendo servido por meio de um token de pré-visualização válido.
Gerando URLs de Pré-visualização
Seção intitulada “Gerando URLs de Pré-visualização”Use getPreviewUrl() para criar links de pré-visualização:
import { getPreviewUrl } from "emdash";
const previewUrl = await getPreviewUrl({ collection: "posts", id: "my-draft-post", secret: import.meta.env.EMDASH_PREVIEW_SECRET, expiresIn: "1h",});// Retorna: /posts/my-draft-post?_preview=eyJjaWQ...Com uma URL base para links absolutos:
const fullUrl = await getPreviewUrl({ collection: "posts", id: "my-draft-post", secret: import.meta.env.EMDASH_PREVIEW_SECRET, baseUrl: "https://example.com",});// Returns: https://example.com/posts/my-draft-post?_preview=eyJjaWQ...Com um padrão de caminho personalizado:
const blogUrl = await getPreviewUrl({ collection: "posts", id: "my-draft-post", secret: import.meta.env.EMDASH_PREVIEW_SECRET, pathPattern: "/blog/{id}",});// Returns: /blog/my-draft-post?_preview=eyJjaWQ...Expiração do Token
Seção intitulada “Expiração do Token”Controle por quanto tempo os links de pré-visualização permanecem válidos:
// Valid for 1 hour (default)await getPreviewUrl({ ..., expiresIn: "1h" });
// Válido por 30 minutosawait getPreviewUrl({ ..., expiresIn: "30m" });
// Válido por 1 diaawait getPreviewUrl({ ..., expiresIn: "1d" });
// Válido por 2 semanasawait getPreviewUrl({ ..., expiresIn: "2w" });
// Válido por 3600 segundosawait getPreviewUrl({ ..., expiresIn: 3600 });Unidades suportadas: s (segundos), m (minutos), h (horas), d (dias), w (semanas).
Verificando Tokens
Seção intitulada “Verificando Tokens”Use verifyPreviewToken() para validar requisições de pré-visualização recebidas:
import { verifyPreviewToken } from "emdash";
// De uma URL (extrai o parâmetro de consulta _preview)const result = await verifyPreviewToken({ url: Astro.url, secret: import.meta.env.EMDASH_PREVIEW_SECRET,});
// Ou com um token diretamenteconst result = await verifyPreviewToken({ token: someTokenString, secret: import.meta.env.EMDASH_PREVIEW_SECRET,});O resultado indica se o token é válido:
if (result.valid) { // Token is valid console.log(result.payload.cid); // "posts:my-draft-post" console.log(result.payload.exp); // Expiry timestamp console.log(result.payload.iat); // Issued-at timestamp} else { // Token is invalid console.log(result.error); // "none" - no token present // "malformed" - token structure is invalid // "invalid" - signature verification failed // "expired" - token has expired}Indicador de Pré-visualização
Seção intitulada “Indicador de Pré-visualização”Você pode mostrar um indicador visual quando o conteúdo está sendo pré-visualizado. O sinalizador isPreview retornado por getEmDashEntry informa quando o conteúdo de rascunho está sendo servido:
{isPreview && ( <div class="preview-banner" role="alert"> <strong>Preview</strong> — You are viewing unpublished content. <a href={Astro.url.pathname}>Exit preview</a> </div>)}Funções Auxiliares
Seção intitulada “Funções Auxiliares”isPreviewRequest(url)
Seção intitulada “isPreviewRequest(url)”Verifica se uma URL contém um token de pré-visualização:
import { isPreviewRequest } from "emdash";
if (isPreviewRequest(Astro.url)) { // Handle preview request}getPreviewToken(url)
Seção intitulada “getPreviewToken(url)”Extrai a string do token de uma URL:
import { getPreviewToken } from "emdash";
const token = getPreviewToken(Astro.url);// Retorna a string do token ou nullparseContentId(contentId)
Seção intitulada “parseContentId(contentId)”Analisa um ID de conteúdo em coleção e ID:
import { parseContentId } from "emdash";
const { collection, id } = parseContentId("posts:my-draft-post");// { collection: "posts", id: "my-draft-post" }Formato do Token
Seção intitulada “Formato do Token”Os tokens de pré-visualização usam um formato compacto: base64url(payload).base64url(signature)
O payload contém:
cid— ID do conteúdo no formatocollection:idexp— Timestamp de expiração (segundos desde a época)iat— Timestamp de emissão (segundos desde a época)
Os tokens são assinados com HMAC-SHA256 usando seu segredo de pré-visualização.
Exemplo Completo
Seção intitulada “Exemplo Completo”Uma página completa de postagem de blog com suporte a pré-visualização e edição visual:
astro title="src/pages/posts/[...slug].astro"---import { getEmDashEntry } from "emdash";import BaseLayout from "../../../layouts/Base.astro";import { PortableText } from "emdash/ui";
const { slug } = Astro.params;
// A pré-visualização é automática — o middleware lida com a verificação do tokenconst { 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>Preview</strong> — This content is not published. </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">Draft</span> )} </header>
<div class="content" {...entry.edit.content}> <PortableText value={entry.data.content} /> </div> </article></BaseLayout>Observe os spreads {...entry.edit} e {...entry.edit.title} — eles adicionam atributos data-emdash-ref que habilitam a edição visual para editores autenticados. Em produção, eles não produzem saída.
Referência da API
Seção intitulada “Referência da API”getPreviewUrl(options)
Seção intitulada “getPreviewUrl(options)”Gera uma URL de pré-visualização com um token assinado.
Opções:
collection— Slug da coleção (string)id— ID ou slug do conteúdo (string)secret— Segredo de assinatura (string)expiresIn— Duração de validade do token (padrão:"1h")baseUrl— URL base opcional para links absolutospathPattern— Padrão de URL com placeholders{collection}e{id}(padrão:"/{collection}/{id}")
Retorna: Promise<string>
verifyPreviewToken(options)
Seção intitulada “verifyPreviewToken(options)”Verifica um token de pré-visualização.
Opções:
secret— Segredo de verificação (string)url— URL para extrair o token, OUtoken— String do token diretamente
Retorna: Promise<VerifyPreviewTokenResult>
type VerifyPreviewTokenResult = | { valid: true; payload: PreviewTokenPayload } | { valid: false; error: "invalid" | "expired" | "malformed" | "none" };generatePreviewToken(options)
Seção intitulada “generatePreviewToken(options)”Gera um token sem construir uma URL.
Opções:
contentId— ID do conteúdo no formatocollection:idexpiresIn— Duração de validade do token (padrão:"1h")secret— Segredo de assinatura
Retorna: Promise<string>