Desplegar en Cloudflare

En esta página

Esta guía despliega un sitio EmDash en Cloudflare Workers con D1 para su base de datos y R2 para medios. Comience con una plantilla EmDash Cloudflare o aplique la misma configuración a un sitio Astro existente.

Prerrequisitos

  • Una cuenta de Cloudflare
  • Las dependencias del proyecto instaladas
  • Wrangler autenticado con Cloudflare (pnpm wrangler login)

Configurar bindings

Las plantillas de Cloudflare incluyen el punto de entrada completo del Worker y bindings con nombre de D1 y R2. En el primer despliegue, Wrangler crea cualquiera de los dos recursos si el nombre configurado aún no existe. Conserve los nombres en wrangler.jsonc; Wrangler vuelve a conectar los despliegues posteriores con los mismos recursos.

La plantilla utiliza los siguientes bindings:

{
	"$schema": "node_modules/wrangler/config-schema.json",
	"name": "my-emdash-site",
	"main": "./src/worker.ts",
	"compatibility_date": "2026-02-24",
	"compatibility_flags": ["nodejs_compat"],

	"d1_databases": [
		{
			"binding": "DB",
			"database_name": "my-emdash-site",
		},
	],

	"r2_buckets": [
		{
			"binding": "MEDIA",
			"bucket_name": "my-emdash-media",
		},
	],
	"worker_loaders": [{ "binding": "LOADER" }],
	"triggers": { "crons": ["* * * * *"] },
}

Los nombres DB, MEDIA y LOADER deben coincidir con los adaptadores de EmDash. El Cron Trigger ejecuta la publicación programada, las tareas de plugins, las copias de seguridad y el mantenimiento. Consulte Plugin sandbox si el sitio usa plugins en sandbox.

Configurar EmDash

La siguiente configuración de Astro usa los bindings de D1 y R2.

import { defineConfig } from "astro/config";
import cloudflare from "@astrojs/cloudflare";
import react from "@astrojs/react";
import emdash from "emdash/astro";
import { d1, r2, sandbox } from "@emdash-cms/cloudflare";

export default defineConfig({
	output: "server",
	adapter: cloudflare(),
	integrations: [
		react(), // Required — the admin UI is a React app
		emdash({
			database: d1({ binding: "DB" }),
			storage: r2({ binding: "MEDIA" }),
			sandboxRunner: sandbox(),
		}),
	],
});

Si el sitio no usa plugins de marketplace, registry ni sandboxed, omita sandboxRunner y el binding LOADER.

Agregar el punto de entrada del Worker

El punto de entrada del Worker conecta Astro con el Cron Trigger y exporta el puente de plugins:

import handler, { createScheduledHandler, PluginBridge } from "@emdash-cms/cloudflare/worker";

export { PluginBridge };

export default {
	...handler,
	scheduled: createScheduledHandler(),
} satisfies ExportedHandler;

La exportación de PluginBridge es inofensiva cuando no hay ningún plugin en sandbox instalado. Consérvela si el mismo proyecto podría habilitar plugins más adelante.

Para ejecutar el mantenimiento general con un horario distinto a cada minuto, pase la misma expresión Cron a createScheduledHandler({ generalCron: "..." }) y a triggers.crons. Si difieren, el handler registra el disparador inesperado y lo ignora.

Construir y desplegar

Construya y despliegue el sitio una vez para que Wrangler aprovisione la base de datos D1 y el bucket R2 con nombre. Wrangler usa el inicio de sesión local creado por pnpm wrangler login.

pnpm build
pnpm wrangler deploy

Con el modo de migración auto predeterminado, EmDash aplica las migraciones del núcleo pendientes cuando el Worker desplegado recibe su primera solicitud. Use Manage core database migrations cuando una canalización de despliegue deba aplicar las migraciones antes de que el código nuevo reciba tráfico, o cuando necesite inspeccionar, comprobar o recuperar una migración.

Hasta que se complete el asistente de configuración, EmDash también aplica una sola vez el esquema y la estructura de un archivo seed en la primera solicitud. El contenido de ejemplo del seed solo se aplica cuando un administrador lo elige en el asistente de configuración. El seed se lee en tiempo de compilación desde .emdash/seed.json, la ruta en package.json#emdash.seed o seed/seed.json —el primero que se encuentre— y se incorpora al bundle. Si no hay ninguno, se usa un seed predeterminado integrado. Los despliegues posteriores sobre una base de datos existente no tocan su contenido.

Para cambiar el esquema o el modelo de contenido de un sitio que ya está desplegado, consulte Evolving a Deployed Site.

Publicación programada

En Cloudflare, EmDash publica las entradas programadas únicamente desde el handler scheduled del Worker, al que Cloudflare llama según el horario de un Cron Trigger. El Worker desplegado necesita ambas cosas:

Si falta cualquiera de los dos, las entradas programadas no se publican. En el desarrollo local con astro dev, EmDash publica las entradas programadas mediante un temporizador, de modo que una programación que funciona en local no confirma la configuración desplegada.

Ejecute emdash doctor desde la raíz del proyecto para comprobar el Cron Trigger y el handler scheduled en los archivos del proyecto local:

pnpm emdash doctor

Cuando emdash doctor encuentra ambos, la salida incluye una comprobación scheduler wiring superada. Una comprobación fallida del planificador describe el problema e imprime la corrección. La comprobación reconoce el punto de entrada del Worker mostrado en esta página y la reexportación de una línea que imprime como corrección, por lo que un punto de entrada escrito de otra forma puede fallar la comprobación aunque funcione.

Si no aparece ninguna comprobación del planificador, emdash doctor no encontró ninguna configuración de Wrangler en el directorio actual, o no encontró ni el Cron Trigger ni el handler scheduled. En un proyecto D1, la comprobación database falla porque busca un archivo SQLite local (./data.db de forma predeterminada). Ese fallo no afecta a las comprobaciones del planificador.

Vuelva a desplegar después de agregar o cambiar el Cron Trigger o el handler scheduled. Cloudflare puede tardar hasta 15 minutos en empezar a ejecutar un Cron Trigger nuevo o modificado. Una vez que el disparador empieza a ejecutarse, EmDash publica las entradas cuya hora programada ya ha pasado.

Para observar el disparador, ejecute pnpm wrangler tail. Cada ejecución imprime una línea como "* * * * *" @ 9/29/2026, 10:01:00 AM - Ok. Ok significa que la ejecución terminó sin un error no capturado. EmDash captura y registra los errores de publicación, así que Ok no confirma que se hayan publicado entradas. Una ejecución que publica entradas también registra un mensaje como [scheduled] Published 1 scheduled item(s), y los errores de la ejecución aparecen en la misma salida.

Tras el despliegue, el panel de administración muestra Scheduled publishing needs attention cuando una entrada programada ha superado su hora programada y no se ha completado ninguna ejecución del planificador en los últimos cinco minutos. Si el Cron Trigger se ejecuta con menos frecuencia que cada cinco minutos, el aviso también puede aparecer entre ejecuciones hasta que la siguiente publique la entrada.

Colocar el Worker cerca de D1

Cloudflare ejecuta un Worker cerca del visitante de forma predeterminada. Las solicitudes renderizadas en el servidor de EmDash hacen varios viajes de ida y vuelta a D1, así que use Targeted Placement para ejecutar el Worker cerca del primario de D1 y acelerar esas solicitudes.

Wrangler acepta placement.mode: "targeted" con exactamente un selector: region, host o hostname. Seleccione el valor que apunta a la ubicación del primario de D1 y agregue el objeto placement resultante a wrangler.jsonc. No habilite réplicas de lectura de D1 junto con Targeted Placement. Mantenga el ajuste session de EmDash en su valor predeterminado, "disabled", para que las lecturas y escrituras usen el primario cercano.

Caché de objetos

Para reducir la carga de lectura sobre D1, almacene en caché en Cloudflare KV los resultados de las consultas de contenido y configuración. Las lecturas se sirven desde KV en lugar de consultar la base de datos en cada solicitud:

import { d1, r2, kvCache } from "@emdash-cms/cloudflare";

emdash({
	database: d1({ binding: "DB" }),
	storage: r2({ binding: "MEDIA" }),
	objectCache: kvCache({ binding: "CACHE" }),
}),

Consulte Object Cache para la configuración de KV, las opciones y el comportamiento de invalidación.

Workers Cache

Workers Cache de Cloudflare coloca una caché de borde delante de su Worker: las solicitudes coincidentes se sirven sin ejecutar su Worker en absoluto.

Habilitarlo

  1. Use el proveedor de caché de Cloudflare de Astro para que las reglas de ruta y Astro.cache establezcan los encabezados de caché y la invalidación use cache.purge().

    import { cacheCloudflare } from "@astrojs/cloudflare/cache";
    
    export default defineConfig({
     adapter: cloudflare(),
     cache: {
       provider: cacheCloudflare(),
     },
     routeRules: {
       "/": { maxAge: 300, swr: 86400 },
       // Other public routes can use different cache lifetimes.
     },
    });

    El adaptador @astrojs/cloudflare detecta cacheCloudflare() y habilita Workers Cache en la configuración de despliegue generada.

  2. Purgue las respuestas en caché desde el código del Worker con la API de la plataforma. Esta llamada no necesita credenciales de la API REST de Cloudflare.

    import { cache } from "cloudflare:workers";
    
    await cache.purge({ purgeEverything: true });
    // Or purge selected tags:
    await cache.purge({ tags: ["posts"] });

Las respuestas de administración y de la API de EmDash ya envían Cache-Control: private, no-store y nunca se almacenan. Las páginas públicas controlan su propio almacenamiento en caché mediante Cache-Control / routeRules / Astro.cache.

Hay dos cosas que debe saber antes de habilitarlo:

  1. Las respuestas sin encabezado Cache-Control se almacenan en caché igualmente. Workers Cache aplica la frescura heurística de RFC 9111: un 200 sin ningún encabezado se almacena en caché durante 2 horas. Dé a cada ruta personalizada un Cache-Control explícito (use private, no-store para todo lo que dependa de la sesión).
  2. Las páginas en caché se comparten con los editores que han iniciado sesión. La caché se ejecuta antes de su Worker, así que no puede omitirse según las cookies de la solicitud. Un editor con sesión iniciada puede recibir la variante anónima en caché de una página pública —sin la barra de herramientas de edición visual— hasta que la entrada caduque. Las respuestas renderizadas para el editor nunca se almacenan (llevan private, no-store), por lo que no se filtra nada en la otra dirección.

Workers Caching es independiente de la caché de objetos (objectCache: kvCache({ binding: "CACHE" })), que almacena en caché los resultados de las consultas a la base de datos en KV por debajo del Worker.

Dominios personalizados

El primer despliegue recibe una URL workers.dev. El dominio personalizado debe ser ya un dominio activo gestionado por Cloudflare en la misma cuenta que el Worker. Después de que el Worker responda correctamente en su URL workers.dev, agregue el dominio de producción como una ruta de Wrangler:

{
	"routes": [{ "pattern": "www.example.com", "custom_domain": true }],
}

Vuelva a desplegar y verifique ambas direcciones. Mantener disponible la dirección workers.dev mientras prueba el DNS ayuda a distinguir un problema de enrutamiento de un problema de la aplicación. Para que el nuevo dominio sea la dirección en los sitemaps y los metadatos de búsqueda, y en los correos y plugins cuando no se establece siteUrl, EMDASH_SITE_URL ni SITE_URL, use Change domain en Settings > General; consulte Cambiar a un nuevo dominio.

Sin siteUrl, EMDASH_SITE_URL ni SITE_URL, la configuración registra como origen del sitio la dirección en la que se ejecuta, y las passkeys creadas durante la configuración solo funcionan en esa dirección. Ejecute la configuración en el dominio de producción, o establezca EMDASH_SITE_URL con ese dominio antes de la configuración. Si establece EMDASH_SITE_URL con otro dominio después de la configuración, el inicio de sesión pasa a ese dominio y las passkeys creadas en la dirección anterior dejan de funcionar allí.

Acceso público R2

De forma predeterminada, los medios se sirven a través de la ruta de medios autenticada de EmDash. Si el bucket tiene un dominio personalizado público, establezca ese origen como publicUrl para que las URL de medios generadas lo usen:

storage: r2({
	binding: "MEDIA",
	publicUrl: "https://media.example.com",
}),

El acceso público al bucket se aplica a todos los objetos alcanzables, no solo a los medios. Las copias de seguridad JSON automáticas usan el prefijo backups/ en el mismo backend de almacenamiento, así que no exponga ese prefijo a través del dominio público. Choose media storage explica el límite seguro.

Transformación de imágenes

EmDash redimensiona y vuelve a codificar los medios de R2 dentro del Worker, mediante el binding IMAGES de Cloudflare. El componente Image de emdash/ui y las imágenes en texto enriquecido se renderizan a través del endpoint de imágenes que EmDash instala bajo el adaptador de Cloudflare. Para los medios en la ruta interna /_emdash/api/media/file/…, ese endpoint lee los bytes de origen directamente del binding de R2, sin una obtención HTTP. Esas transformaciones siguen funcionando detrás de Cloudflare Access y con global_fetch_strictly_public. Los medios servidos desde una URL de bucket —consulte Acceso público R2— usan en su lugar el propio endpoint de transformación del adaptador, que obtiene el archivo por HTTP antes de transformarlo.

No tiene que declarar el binding. @astrojs/cloudflare lo agrega a la configuración del Worker que genera durante astro build, de la misma forma que agrega cache para Workers Caching. Lo hace siempre que el servicio de imágenes en tiempo de ejecución es cloudflare-binding: imageService sin establecer, la propia cadena o { runtime: "cloudflare-binding" }. Cualquier otro valor —"passthrough", "compile", "cloudflare", "custom"— deja fuera el binding. Incluirlo en su propio wrangler.jsonc deja clara la intención:

{
	"images": {
		"binding": "IMAGES",
	},
}

Para ver qué obtiene realmente un despliegue, lea la configuración generada en lugar de wrangler.jsonc. Una compilación escribe .wrangler/deploy/config.json, que apunta wrangler deploy al archivo combinado (dist/server/wrangler.json de forma predeterminada). Busque allí una entrada images.

Cloudflare factura estas transformaciones como Images transformations. Cada combinación única de imagen de origen y parámetros se factura una vez por mes natural, y las solicitudes repetidas dentro de ese mes son gratuitas. Si un sitio tiene 500 imágenes de origen y solicita un tamaño de miniatura y un tamaño de héroe para cada imagen, esos dos conjuntos de parámetros cuentan como 1.000 imágenes transformadas ese mes. El plan Images Free cubre 5.000 transformaciones únicas al mes. Superado ese límite, las transformaciones en caché se siguen sirviendo, pero las nuevas devuelven un error 9422 y la solicitud de imagen falla.

Autenticación Cloudflare Access

Cloudflare Access puede sustituir la autenticación con passkey por el proveedor de identidad asociado a una aplicación de Access. El valor de audiencia es un ajuste secreto en tiempo de ejecución; manténgalo fuera de astro.config.mjs indicando el nombre de su variable de entorno:

import { access } from "@emdash-cms/cloudflare";

emdash({
	auth: access({
		teamDomain: "myteam.cloudflareaccess.com",
		audienceEnvVar: "CF_ACCESS_AUDIENCE",
		roleMapping: {
			Admins: 50,
			Editors: 40,
		},
	}),
}),

Establezca CF_ACCESS_AUDIENCE con pnpm wrangler secret put CF_ACCESS_AUDIENCE. La authentication guide explica el aprovisionamiento de usuarios, los roles predeterminados y la sincronización de roles.

Correo electrónico

Los Workers de producción no tienen un servicio de entrega de correo electrónico predeterminado. El inicio de sesión con enlace mágico devuelve Email is not configured hasta que haya un plugin de correo electrónico activo.

El plugin de correo electrónico de Cloudflare usa un binding send_email. Primero registre y verifique el dominio remitente con Cloudflare Email Sending. Cloudflare rechaza los mensajes cuya dirección From no sea un remitente aceptado.

Agregue el binding y registre el proveedor:

{
	"send_email": [{ "name": "EMAIL" }],
}
import { cloudflareEmail } from "@emdash-cms/cloudflare/plugins";

emdash({
	plugins: [
		cloudflareEmail({
			from: { email: "[email protected]", name: "My Site CMS" },
			replyTo: "[email protected]",
		}),
	],
}),

El plugin está activo desde su primer despliegue. Settings → Email muestra el proveedor seleccionado y puede enviar un correo de prueba. El envío falla hasta que el remitente sea aceptado y el binding exista.

El plugin usa el binding llamado EMAIL salvo que su opción binding indique otro. Si es el único proveedor de correo electrónico activo, EmDash lo selecciona automáticamente. Si hay más de un proveedor activo, elija el proveedor de Cloudflare en Settings → Email. La dirección opcional replyTo recibe las respuestas sin cambiar la dirección From aceptada. Un plugin puede establecer replyTo en un mensaje individual, lo que sustituye esta opción para ese mensaje.

El plugin AI Search necesita tanto un registro de plugin nativo como un binding ai_search_namespaces. Después de desplegarlos, abra Cloudflare AI Search en la administración, elija las colecciones y ejecute Sync All Content. La sincronización inicial indexa el contenido publicado antes de habilitar el plugin; los hooks mantienen sincronizados los cambios posteriores.

import { aiSearch } from "@emdash-cms/cloudflare/plugins";

emdash({
	plugins: [aiSearch()],
}),
{
	"ai_search_namespaces": [{ "binding": "AI_SEARCH", "namespace": "default" }],
}

Exponga la ruta de búsqueda desde el sitio:

export { POST, prerender } from "@emdash-cms/cloudflare/plugins/ai-search";

Agregue la interfaz de búsqueda a un layout. El slot de disparo acepta un botón que coincida con el diseño del sitio:

---
import AISearchSnippet from "@emdash-cms/cloudflare/plugins/ai-search/astro";
---

<AISearchSnippet apiUrl="/api/ai-search" placeholder="Search content">
	<button slot="trigger" type="button">Search</button>
</AISearchSnippet>

Secretos del Worker

Almacene los valores secretos con pnpm wrangler secret put <NAME>. No los ponga en wrangler.jsonc ni los lea de valores de import.meta.env en tiempo de compilación.

EMDASH_ENCRYPTION_KEY cifra los ajustes de plugins declarados como secretos. Establézcala antes de guardar un secreto de plugin y consérvela por separado de las copias de seguridad de D1. Durante la rotación, proporcione primero la nueva clave y conserve las claves anteriores después de comas hasta que todos los secretos de plugins se hayan vuelto a guardar. Secrets and key management describe la rotación y la recuperación.

El puente de plugins lee directamente este binding de secreto del Worker. La ruta de ajustes de administración generada lo lee a través de process.env. Con nodejs_compat, Cloudflare rellena process.env de forma predeterminada para fechas de compatibilidad a partir de 2025-04-01. Los proyectos fijados a una fecha anterior también deben agregar nodejs_compat_populate_process_env antes de guardar ajustes cifrados.

EmDash lee sus secretos de process.env en tiempo de ejecución. El código del Worker lee los bindings de env, importado desde cloudflare:workers. No lea nunca los secretos a través de import.meta.env: Vite sustituye esos valores en tiempo de compilación y puede escribirlos en el bundle del servidor.

El secreto HMAC de vista previa y la sal de IP de los autores de comentarios se generan y se almacenan en la base de datos, salvo que proporcione anulaciones en tiempo de ejecución. Secrets and key management enumera las variables exactas, las ubicaciones de almacenamiento y los efectos de la rotación.

Despliegues de vista previa

Una vista previa puede ejecutarse como Worker Preview, que da a cada rama su propia URL en el Worker de producción, o como un Worker independiente en un entorno con nombre de Wrangler. En ambos casos, la vista previa necesita una base de datos y un bucket distintos de los de producción. No apunte nunca un binding de vista previa a una base de datos o un bucket de producción.

Worker Previews

Worker Previews requiere Wrangler 4.135.0 o posterior en las dependencias del proyecto. Los proyectos creados a partir de una plantilla de EmDash antigua pueden fijar una versión anterior; actualícela con pnpm add -D wrangler@latest. Las vistas previas no heredan los bindings ni vars de la configuración de nivel superior; enumere en un bloque previews todos los bindings que usa la vista previa.

El adaptador de Astro para Cloudflare añade un binding KV SESSION. wrangler deploy lo aprovisiona automáticamente, pero wrangler preview lo rechaza sin un ID de espacio de nombres. Cree los recursos de vista previa, incluido un espacio de nombres KV para las sesiones:

pnpm wrangler d1 create my-emdash-site-preview
pnpm wrangler r2 bucket create my-emdash-media-preview
pnpm wrangler kv namespace create my-emdash-site-preview-session

Añada a wrangler.jsonc los identificadores de recursos que imprime Wrangler:

{
	"previews": {
		"d1_databases": [
			{
				"binding": "DB",
				"database_name": "my-emdash-site-preview",
				"database_id": "00000000-0000-0000-0000-000000000000",
			},
		],
		"r2_buckets": [
			{
				"binding": "MEDIA",
				"bucket_name": "my-emdash-media-preview",
			},
		],
		"kv_namespaces": [{ "binding": "SESSION", "id": "00000000000000000000000000000000" }],
		"worker_loaders": [{ "binding": "LOADER" }],
	},
}

Repita send_email, AI Search, vars y otros bindings cuando la vista previa use esas funciones.

El asistente de configuración y las passkeys necesitan la URL del sitio de la vista previa. Sin siteUrl ni EMDASH_SITE_URL, la configuración falla con SITE_URL_REQUIRED. Pase la URL de la vista previa, https://<preview-name>-<worker-name>.<subdomain>.workers.dev, en cada despliegue de vista previa:

pnpm build
pnpm wrangler preview --name my-branch \
  --var EMDASH_SITE_URL:https://my-branch-my-emdash-site.example.workers.dev

Use el mismo nombre de vista previa en --name y en la URL. Un siteUrl en astro.config.mjs anula EMDASH_SITE_URL y se incorpora a cada vista previa, por lo que las passkeys de la vista previa quedan vinculadas al dominio de producción y ni la configuración ni el inicio de sesión funcionan allí. Establezca en su lugar la URL de producción con EMDASH_SITE_URL en vars de nivel superior.

Si el Worker establece workers_dev en false, las vistas previas no tienen URL hasta que establezca "preview_urls": true en el nivel superior de wrangler.jsonc y ejecute wrangler deploy una vez. wrangler preview no aplica este ajuste. wrangler deploy publica en producción el código de su checkout actual, no solo el ajuste, así que confirme el cambio con un commit y despliegue desde un checkout limpio de su rama de producción. Después vuelva a su rama y ejecute wrangler preview de nuevo.

Establezca un secreto para una sola vista previa con pnpm wrangler preview secret put <NAME> --name my-branch. pnpm wrangler preview base-config secret put <NAME> establece un secreto para las vistas previas creadas después; las vistas previas existentes conservan sus secretos.

Tenga presentes estas diferencias con producción:

  • Las vistas previas comparten los recursos del bloque previews. wrangler preview lee el bloque previews de la rama que despliega, así que toda rama que conserve la configuración de la rama base usa la misma base de datos. La primera solicitud a una vista previa aplica las migraciones del núcleo pendientes, de modo que una rama que actualiza EmDash también migra la base de datos que usan las demás vistas previas. Para aislar una rama, cree otra base de datos D1 y cambie database_name y database_id en el bloque previews de esa rama antes de desplegarla.
  • Una base de datos de vista previa vacía muestra el asistente de configuración. Las URL de vista previa son públicas, y el primer visitante que termine el asistente se convierte en administrador. Termine la configuración inmediatamente después de crear la vista previa, o proteja las URL de vista previa con Cloudflare Access.
  • Los Cron Triggers no se ejecutan. Las publicaciones programadas no se publican, y las tareas cron de plugins y las copias de seguridad programadas no se ejecutan en una vista previa.
  • Las passkeys solo funcionan en el nombre de host que las registró. Las passkeys de producción no funcionan en una vista previa, y el administrador creado en la primera vista previa solo puede iniciar sesión con una passkey en esa URL de vista previa. En otra vista previa que comparta la base de datos, inicie sesión con un enlace mágico, que necesita un proveedor de correo y su binding en el bloque previews, y añada allí una passkey. Como alternativa, aísle la rama con su propia base de datos como se describe arriba y ejecute allí la configuración.

Entornos con nombre

Los entornos con nombre de Wrangler no heredan bindings. Cree recursos de vista previa independientes y escríbalos en el entorno preview antes de compilar:

pnpm wrangler d1 create my-emdash-site-preview \
  --binding DB --env preview --update-config
pnpm wrangler r2 bucket create my-emdash-media-preview \
  --binding MEDIA --env preview --update-config

El entorno de vista previa debe repetir cada binding que usa el Worker de vista previa. Los bindings principales de D1, R2 y sandbox tienen esta forma después de que Wrangler escriba los identificadores de recursos:

{
	"env": {
		"preview": {
			"d1_databases": [
				{
					"binding": "DB",
					"database_name": "my-emdash-site-preview",
					"database_id": "00000000-0000-0000-0000-000000000000",
				},
			],
			"r2_buckets": [
				{
					"binding": "MEDIA",
					"bucket_name": "my-emdash-media-preview",
				},
			],
			"worker_loaders": [{ "binding": "LOADER" }],
		},
	},
}

Use el UUID de vista previa escrito por Wrangler. Repita los bindings opcionales de KV, AI Search, correo electrónico y otros cuando la vista previa use esas funciones. Agregue secretos solo de vista previa con pnpm wrangler secret put <NAME> --env preview.

Compile y despliegue el entorno de vista previa. Su primera solicitud aplica las migraciones del núcleo pendientes mediante el modo auto predeterminado.

pnpm build
pnpm wrangler deploy --env preview

Verifique la URL de vista previa, el inicio de sesión en la administración, la subida de medios y cualquier binding opcional antes de compartirla.

Verificar el despliegue

Tras el despliegue, solicite una página pública, inicie sesión en /_emdash/admin, suba y recupere un archivo multimedia de prueba, y confirme que el handler scheduled aparece en pnpm wrangler tail.

Resolución de problemas

”D1 binding not found”

Verifique que el nombre del binding en wrangler.jsonc coincida con la configuración de su base de datos:

// Must match: d1({ binding: "DB" })
"binding": "DB"

“R2 binding not found”

Compruebe que el bucket R2 esté correctamente enlazado:

// Must match: r2({ binding: "MEDIA" })
"binding": "MEDIA"

Errores de migración

Si ve errores de esquema, haga tail de los registros del Worker (wrangler tail) y reproduzca el error para capturar el mensaje subyacente; después, abra un issue con esa salida.