Questa guida distribuisce un sito EmDash su Cloudflare Workers con D1 per il database e R2 per i media. Parti da un template EmDash Cloudflare o applica la stessa configurazione a un sito Astro esistente.
Prerequisiti
- Un account Cloudflare
- Le dipendenze del progetto installate
- Wrangler autenticato con Cloudflare (
pnpm wrangler login)
Configurare i binding
I template Cloudflare includono il punto di ingresso Worker completo e binding D1 e R2 con nome. Al primo deploy, Wrangler crea una delle due risorse se il nome configurato non esiste ancora. Mantieni i nomi in wrangler.jsonc; Wrangler ricollega i deploy successivi alle stesse risorse.
Il template usa i binding seguenti:
{
"$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": ["* * * * *"] },
}
I nomi DB, MEDIA e LOADER devono corrispondere agli adapter di EmDash. Il Cron Trigger esegue la pubblicazione pianificata, le attività dei plugin, i backup e la manutenzione. Consulta Plugin sandbox se il sito usa plugin in sandbox.
Configurare EmDash
La seguente configurazione Astro usa i binding D1 e 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(),
}),
],
});
Se il sito non usa plugin marketplace, registry o sandboxed, ometti sandboxRunner e il binding LOADER.
Aggiungere il punto di ingresso Worker
Il punto di ingresso Worker collega Astro al Cron Trigger ed esporta il bridge dei plugin:
import handler, { createScheduledHandler, PluginBridge } from "@emdash-cms/cloudflare/worker";
export { PluginBridge };
export default {
...handler,
scheduled: createScheduledHandler(),
} satisfies ExportedHandler;
L’export PluginBridge è innocuo quando non è installato alcun plugin in sandbox. Mantienilo se lo stesso progetto potrebbe abilitare plugin in seguito.
Per eseguire la manutenzione generale con una pianificazione diversa da ogni minuto, passa la stessa espressione Cron a createScheduledHandler({ generalCron: "..." }) e a triggers.crons. Se sono diverse, l’handler registra il trigger inatteso e lo ignora.
Compilare e distribuire
Compila e distribuisci il sito una volta, in modo che Wrangler effettui il provisioning del database D1 e del bucket R2 con nome. Wrangler usa il login locale creato da pnpm wrangler login.
pnpm build
pnpm wrangler deploy
Con la modalità di migrazione auto predefinita, EmDash applica le migrazioni core in sospeso quando il Worker distribuito riceve la sua prima richiesta. Il Worker lascia le migrazioni di contenuti di grandi dimensioni a emdash migrate e risponde con 503 finché non vengono applicate. Usa Manage core database migrations quando una pipeline di deploy deve applicare le migrazioni prima che il nuovo codice riceva traffico, oppure quando devi ispezionare, controllare o ripristinare una migrazione.
Finché la procedura guidata di configurazione non è stata completata, EmDash applica anche una sola volta, alla prima richiesta, lo schema e la struttura di un file seed. I contenuti di esempio del seed vengono applicati solo quando un amministratore li sceglie nella procedura guidata di configurazione. Il seed viene letto in fase di build da .emdash/seed.json, dal percorso in package.json#emdash.seed o da seed/seed.json — quello trovato per primo — e incorporato nel bundle. Se nessuno è presente, viene usato un seed predefinito integrato. I deploy successivi su un database esistente non toccano il suo contenuto.
Per modificare lo schema o il modello di contenuto di un sito già distribuito, consulta Evolving a Deployed Site.
Pubblicazione pianificata
Su Cloudflare, EmDash pubblica le voci pianificate solo dall’handler scheduled del Worker, che Cloudflare chiama secondo la pianificazione di un Cron Trigger. Il Worker distribuito ha bisogno di entrambi i seguenti elementi:
- Un Cron Trigger nell’elenco
triggers.cronsdiwrangler.jsonc, come mostrato in Configurare i binding. - L’handler
scheduleddi@emdash-cms/cloudflare/worker, esportato dal punto di ingresso Worker a cui puntamain, come mostrato in Aggiungere il punto di ingresso Worker.
Se manca uno dei due, le voci pianificate non vanno online. Nello sviluppo locale con astro dev, EmDash pubblica le voci pianificate tramite un timer, quindi una pianificazione che funziona in locale non conferma la configurazione distribuita.
Esegui emdash doctor dalla radice del progetto per controllare il Cron Trigger e l’handler scheduled nei file del progetto locale:
pnpm emdash doctor
Quando emdash doctor trova entrambi, l’output include un controllo scheduler wiring superato. Un controllo dello scheduler non riuscito descrive il problema e stampa la correzione. Il controllo riconosce il punto di ingresso Worker mostrato in questa pagina e il re-export su una riga che stampa come correzione, quindi un punto di ingresso scritto in un altro modo può non superare il controllo anche se funziona.
Se non compare alcun controllo dello scheduler, emdash doctor non ha trovato alcuna configurazione Wrangler nella directory corrente, oppure non ha trovato né il Cron Trigger né l’handler scheduled. In un progetto D1, il controllo database non riesce perché cerca un file SQLite locale (./data.db per impostazione predefinita). Questo errore non influisce sui controlli dello scheduler.
Distribuisci di nuovo dopo aver aggiunto o modificato il Cron Trigger o l’handler scheduled. Cloudflare può impiegare fino a 15 minuti per iniziare a eseguire un Cron Trigger nuovo o modificato. Una volta che il trigger inizia a funzionare, EmDash pubblica le voci il cui orario pianificato è già trascorso.
Per osservare il trigger, esegui pnpm wrangler tail. Ogni esecuzione stampa una riga come "* * * * *" @ 9/29/2026, 10:01:00 AM - Ok. Ok significa che l’esecuzione è terminata senza un errore non gestito. EmDash intercetta e registra gli errori di pubblicazione, quindi Ok non conferma che le voci siano state pubblicate. Un’esecuzione che pubblica voci registra anche un messaggio come [scheduled] Published 1 scheduled item(s), e gli errori dell’esecuzione compaiono nello stesso output.
Dopo il deploy, la dashboard di amministrazione mostra Scheduled publishing needs attention quando una voce pianificata ha superato il suo orario pianificato e nessuna esecuzione dello scheduler si è completata negli ultimi cinque minuti. Se il Cron Trigger viene eseguito meno spesso di ogni cinque minuti, l’avviso può comparire anche tra un’esecuzione e l’altra, finché la successiva non pubblica la voce.
Collocare il Worker vicino a D1
Cloudflare esegue per impostazione predefinita un Worker vicino al visitatore. Le richieste renderizzate lato server di EmDash fanno diversi round trip verso D1, quindi usa Targeted Placement per eseguire il Worker vicino al primario D1 e rendere più veloci queste richieste.
Wrangler accetta placement.mode: "targeted" con esattamente un selettore: region, host o hostname. Seleziona il valore che punta alla posizione del primario D1 e aggiungi l’oggetto placement risultante a wrangler.jsonc. Non abilitare le repliche di lettura D1 insieme a Targeted Placement. Senza repliche, ogni query usa il primario vicino, anche con session: "auto" e coalesce: true.
Cache degli oggetti
Per ridurre il carico di lettura su D1, memorizza nella cache di Cloudflare KV i risultati delle query di contenuti e configurazione. Le letture vengono servite da KV invece di interrogare il database a ogni richiesta:
import { d1, r2, kvCache } from "@emdash-cms/cloudflare";
emdash({
database: d1({ binding: "DB" }),
storage: r2({ binding: "MEDIA" }),
objectCache: kvCache({ binding: "CACHE" }),
}),
Consulta Object Cache per la configurazione di KV, le opzioni e il comportamento di invalidazione.
Workers Cache
Workers Cache di Cloudflare mette una cache edge davanti al tuo Worker: le richieste corrispondenti vengono servite senza eseguire affatto il tuo Worker.
Abilitarlo
Usa il provider di cache Cloudflare di Astro, così le regole di route e Astro.cache impostano gli header di cache e il provider può invalidare le voci quando il contenuto cambia.
import { cacheCloudflare } from "@astrojs/cloudflare/cache";
export default defineConfig({
adapter: cloudflare(),
cache: {
provider: cacheCloudflare(),
},
routeRules: {
"/": { maxAge: 300, swr: 86400, tags: ["posts", "pages"] },
// Altre route pubbliche possono usare durate di cache diverse.
},
});
L’adapter @astrojs/cloudflare rileva cacheCloudflare() e abilita Workers Cache nella configurazione di deploy generata.
EmDash invalida automaticamente le voci in cache quando il contenuto cambia (vedi Cache delle route per la convenzione dei tag). Devi eseguire il purge tu stesso solo per tag personalizzati, per esempio un interruttore manuale «svuota tutto», usando l’API di purge della piattaforma dal tuo Worker. Non richiede credenziali dell’API REST di Cloudflare.
Le pagine pubbliche controllano la propria cache tramite routeRules e Astro.cache. Limita le regole di route alle tue route pubbliche: un parametro rest alla radice del sito, come /[...slug], corrisponde anche alle route di amministrazione e API sotto /_emdash/.
Due cose da sapere prima di abilitarla:
- Una risposta con un qualsiasi hint di cache è memorizzabile nella cache, anche senza una regola di route. L’adapter contrassegna come
no-storele risposte che nessuna regola di route né alcuna chiamata aAstro.cache.set()ha reso memorizzabili. Un hint con tag ma senzamaxAge, come gli hint di impostazioni del sito e menu che un layout imposta su ogni pagina, produceCloudflare-CDN-Cache-Control: publicsenza durata, quindi Workers Cache ripiega sulle sue durate di cache predefinite (2 ore per una risposta200). Dai a ogni route pubblica una regola e tieni le route che dipendono dalla sessione prive di hint di cache. - Le pagine in cache sono condivise con gli editor che hanno effettuato l’accesso. La cache viene eseguita prima del tuo Worker, quindi non può essere aggirata in base ai cookie della richiesta. Con la barra degli strumenti renderizzata lato server predefinita, un editor autenticato può ricevere la variante anonima in cache di una pagina pubblica, senza la barra degli strumenti di modifica visuale, finché la voce non scade. Imposta
toolbar: "client"per mantenere affidabile la barra degli strumenti sulle pagine in cache. Le pagine renderizzate per un utente autenticato non vengono mai memorizzate nella cache delle route.
Per una guida più approfondita su regole di route, tag di cache, invalidazione integrata di EmDash alla pubblicazione e risposte che devono restare fuori dalla cache, vedi Cache delle route.
Workers Caching è separato dalla cache degli oggetti (objectCache: kvCache({ binding: "CACHE" })), che memorizza in KV, sotto il Worker, i risultati delle query del database.
Domini personalizzati
Il primo deploy riceve un URL workers.dev. Il dominio personalizzato deve essere già un dominio attivo gestito da Cloudflare nello stesso account del Worker. Dopo che il Worker risponde correttamente al suo URL workers.dev, aggiungi il dominio di produzione come route Wrangler:
{
"routes": [{ "pattern": "www.example.com", "custom_domain": true }],
}
Distribuisci di nuovo e verifica entrambi gli indirizzi. Mantenere disponibile l’indirizzo workers.dev durante il test del DNS aiuta a distinguere un problema di routing da un problema dell’applicazione. Per fare del nuovo dominio l’indirizzo usato nelle sitemap e nei metadati di ricerca, e nelle email e nei plugin quando siteUrl, EMDASH_SITE_URL o SITE_URL non è impostato, usa Change domain in Settings > General; vedi Passare a un nuovo dominio.
Senza siteUrl, EMDASH_SITE_URL o SITE_URL, il setup registra come origine del sito l’indirizzo su cui viene eseguito, e le passkey create durante il setup funzionano solo su quell’indirizzo. Esegui il setup sul dominio di produzione, oppure imposta EMDASH_SITE_URL su quel dominio prima del setup. Se imposti EMDASH_SITE_URL su un altro dominio dopo il setup, l’accesso passa a quel dominio e le passkey create sul vecchio indirizzo smettono di funzionare lì.
Accesso R2 pubblico
Per impostazione predefinita, i media vengono serviti tramite la route media autenticata di EmDash. Se il bucket ha un dominio personalizzato pubblico, imposta quell’origine come publicUrl in modo che gli URL dei media generati la usino:
storage: r2({
binding: "MEDIA",
publicUrl: "https://media.example.com",
}),
L’accesso pubblico al bucket si applica a ogni oggetto raggiungibile, non solo ai media. I backup JSON automatici usano il prefisso backups/ nello stesso backend di storage, quindi non esporre quel prefisso tramite il dominio pubblico. Choose media storage spiega il confine sicuro.
Trasformazione delle immagini
EmDash ridimensiona e ricodifica i media R2 all’interno del Worker, tramite il binding IMAGES di Cloudflare. Il componente Image di emdash/ui e le immagini nel rich text vengono entrambi renderizzati tramite l’endpoint immagini che EmDash installa con l’adapter Cloudflare. Per i media sulla route interna /_emdash/api/media/file/…, quell’endpoint legge i byte sorgente direttamente dal binding R2, senza un fetch HTTP. Queste trasformazioni continuano a funzionare dietro Cloudflare Access e con global_fetch_strictly_public. I media serviti da un URL del bucket — vedi Accesso R2 pubblico — usano invece l’endpoint di trasformazione proprio dell’adapter, che recupera il file via HTTP prima di trasformarlo.
Non devi dichiarare il binding. @astrojs/cloudflare lo aggiunge alla configurazione Worker che genera durante astro build, nello stesso modo in cui aggiunge cache per Workers Caching. Lo fa ogni volta che il servizio immagini di runtime è cloudflare-binding: imageService non impostato, la stringa stessa oppure { runtime: "cloudflare-binding" }. Qualsiasi altro valore — "passthrough", "compile", "cloudflare", "custom" — lascia fuori il binding. Elencarlo nel tuo wrangler.jsonc rende evidente l’intenzione:
{
"images": {
"binding": "IMAGES",
},
}
Per vedere cosa ottiene effettivamente un deploy, leggi la configurazione generata anziché wrangler.jsonc. Una build scrive .wrangler/deploy/config.json, che indirizza wrangler deploy al file unito (dist/server/wrangler.json per impostazione predefinita). Cerca lì una voce images.
Cloudflare fattura queste trasformazioni come Images transformations. Ogni combinazione unica di immagine sorgente e parametri viene fatturata una volta per mese di calendario, e le richieste ripetute entro quel mese sono gratuite. Se un sito ha 500 immagini sorgente e richiede una dimensione di miniatura e una dimensione hero per ogni immagine, quei due insiemi di parametri contano come 1.000 immagini trasformate per quel mese. Il piano Images Free copre 5.000 trasformazioni uniche al mese. Oltre quel limite, le trasformazioni in cache vengono comunque servite, ma quelle nuove restituiscono un errore 9422 e la richiesta dell’immagine fallisce.
Autenticazione Cloudflare Access
Cloudflare Access può sostituire l’autenticazione con passkey con il provider di identità collegato a un’applicazione Access. Il valore audience è un’impostazione segreta di runtime; tienilo fuori da astro.config.mjs indicando il nome della sua variabile d’ambiente:
import { access } from "@emdash-cms/cloudflare";
emdash({
auth: access({
teamDomain: "myteam.cloudflareaccess.com",
audienceEnvVar: "CF_ACCESS_AUDIENCE",
roleMapping: {
Admins: 50,
Editors: 40,
},
}),
}),
Imposta CF_ACCESS_AUDIENCE con pnpm wrangler secret put CF_ACCESS_AUDIENCE. La authentication guide spiega il provisioning degli utenti, i ruoli predefiniti e la sincronizzazione dei ruoli.
I Worker di produzione non hanno un servizio di consegna email predefinito. L’accesso con link magico restituisce Email is not configured finché non è attivo un plugin email.
Il plugin email di Cloudflare usa un binding send_email. Prima registra e verifica il dominio mittente con Cloudflare Email Sending. Cloudflare rifiuta i messaggi il cui indirizzo From non è un mittente accettato.
Aggiungi il binding e registra il provider:
{
"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]",
}),
],
}),
Il plugin è attivo fin dal primo deploy. Settings → Email mostra il provider selezionato e può inviare un’email di prova. L’invio fallisce finché il mittente non viene accettato e il binding non esiste.
Il plugin usa il binding chiamato EMAIL, a meno che la sua opzione binding non indichi un altro nome. Se è l’unico provider email attivo, EmDash lo seleziona automaticamente. Se è attivo più di un provider, scegli il provider Cloudflare in Settings → Email. L’indirizzo replyTo opzionale riceve le risposte senza modificare l’indirizzo From accettato. Un plugin può impostare replyTo su un singolo messaggio, il che sostituisce questa opzione per quel messaggio.
Cloudflare AI Search
Per aggiungere Cloudflare AI Search al sito, installa il plugin AI Search.
Secret del Worker
Memorizza i valori segreti con pnpm wrangler secret put <NAME>. Non inserirli in wrangler.jsonc e non leggerli dai valori import.meta.env in fase di build.
EMDASH_ENCRYPTION_KEY cifra le impostazioni dei plugin dichiarate come secret. Impostala prima di salvare il secret di un plugin e conservala separatamente dai backup D1. Durante la rotazione, fornisci prima la nuova chiave e mantieni le chiavi precedenti dopo le virgole finché ogni secret dei plugin non è stato salvato di nuovo. Secrets and key management descrive rotazione e ripristino.
Il bridge dei plugin legge direttamente questo binding di secret del Worker. La route delle impostazioni di amministrazione generata lo legge tramite process.env. Con nodejs_compat, Cloudflare popola process.env per impostazione predefinita per le date di compatibilità dal 2025-04-01 in poi. I progetti fissati a una data precedente devono anche aggiungere nodejs_compat_populate_process_env prima di salvare impostazioni cifrate.
EmDash legge i propri secret da process.env in fase di esecuzione. Il codice del Worker legge i binding da env, importato da cloudflare:workers. Non leggere mai i secret tramite import.meta.env: Vite sostituisce quei valori in fase di build e può scriverli nel bundle del server.
Il secret HMAC di anteprima e il salt degli IP dei commentatori vengono generati e memorizzati nel database, a meno che tu non fornisca override di runtime. Secrets and key management elenca le variabili esatte, le posizioni di archiviazione e gli effetti della rotazione.
Distribuzioni di anteprima
Un’anteprima può essere eseguita come Worker Preview, che assegna a ogni branch un URL proprio sul Worker di produzione, oppure come Worker separato in un ambiente Wrangler con nome. In entrambi i casi l’anteprima ha bisogno di un database e di un bucket separati dalla produzione. Non puntare mai un binding di anteprima a un database o a un bucket di produzione.
Worker Previews
Worker Previews richiede Wrangler 4.135.0 o successivo tra le dipendenze del progetto. I progetti creati da un template EmDash meno recente possono fissare una versione precedente; aggiornala con pnpm add -D wrangler@latest. Le anteprime non ereditano i binding né vars dalla configurazione di primo livello; elenca in un blocco previews ogni binding usato dall’anteprima.
L’adattatore Astro per Cloudflare aggiunge un binding KV SESSION. wrangler deploy lo predispone automaticamente, ma wrangler preview lo rifiuta senza un ID di namespace. Crea le risorse di anteprima, incluso un namespace KV per le sessioni:
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
Aggiungi a wrangler.jsonc gli identificatori delle risorse stampati da 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" }],
},
}
Ripeti send_email, AI Search, vars e gli altri binding quando l’anteprima usa quelle funzionalità.
La procedura guidata di setup e le passkey hanno bisogno dell’URL del sito dell’anteprima. Senza siteUrl o EMDASH_SITE_URL, il setup fallisce con SITE_URL_REQUIRED. Passa l’URL dell’anteprima, https://<preview-name>-<worker-name>.<subdomain>.workers.dev, a ogni distribuzione di anteprima:
pnpm build
pnpm wrangler preview --name my-branch \
--var EMDASH_SITE_URL:https://my-branch-my-emdash-site.example.workers.dev
Usa lo stesso nome di anteprima in --name e nell’URL. Un siteUrl in astro.config.mjs ha la precedenza su EMDASH_SITE_URL e viene incorporato in ogni anteprima, quindi le passkey sull’anteprima sono legate al dominio di produzione e lì non funzionano né il setup né l’accesso. Imposta invece l’URL di produzione con EMDASH_SITE_URL nei vars di primo livello.
Se il Worker imposta workers_dev su false, le anteprime non hanno URL finché non imposti "preview_urls": true al primo livello di wrangler.jsonc ed esegui wrangler deploy una volta. wrangler preview non applica questa impostazione. wrangler deploy pubblica in produzione il codice del tuo checkout attuale, non solo l’impostazione, quindi fai il commit della modifica e distribuisci da un checkout pulito del tuo branch di produzione. Poi torna al tuo branch ed esegui di nuovo wrangler preview.
Imposta un secret per una singola anteprima con pnpm wrangler preview secret put <NAME> --name my-branch. pnpm wrangler preview base-config secret put <NAME> imposta un secret per le anteprime create in seguito; le anteprime esistenti mantengono i propri secret.
Tieni presenti queste differenze rispetto alla produzione:
- Le anteprime condividono le risorse del blocco
previews.wrangler previewlegge il bloccopreviewsdel branch che distribuisce, quindi ogni branch che mantiene la configurazione del branch di base usa lo stesso database. La prima richiesta a un’anteprima applica le migrazioni del core in sospeso, quindi un branch che aggiorna EmDash migra anche il database usato dalle altre anteprime. Per isolare un branch, crea un altro database D1 e modificadatabase_nameedatabase_idnel bloccopreviewsdi quel branch prima di distribuirlo. - Un database di anteprima vuoto mostra la procedura guidata di setup. Gli URL di anteprima sono pubblici e il primo visitatore che completa la procedura guidata diventa l’amministratore. Completa il setup subito dopo aver creato l’anteprima, oppure proteggi gli URL di anteprima con Cloudflare Access.
- I Cron Trigger non vengono eseguiti. I post pianificati non vengono pubblicati, e le attività cron dei plugin e i backup pianificati non vengono eseguiti su un’anteprima.
- Le passkey funzionano solo sull’hostname che le ha registrate. Le passkey di produzione non funzionano su un’anteprima, e l’amministratore creato sulla prima anteprima può accedere con una passkey solo su quell’URL di anteprima. Su un’altra anteprima che condivide il database, accedi con un magic link, che richiede un provider di email e il suo binding nel blocco
previews, e aggiungi lì una passkey. In alternativa, isola il branch con un proprio database come descritto sopra ed esegui lì il setup.
Ambienti con nome
Gli ambienti Wrangler con nome non ereditano i binding. Crea risorse di anteprima separate e scrivile nell’ambiente preview prima della build:
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
L’ambiente di anteprima deve ripetere ogni binding usato dal Worker di anteprima. I binding core D1, R2 e sandbox hanno questa forma dopo che Wrangler ha scritto gli identificatori delle risorse:
{
"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" }],
},
},
}
Usa l’UUID di anteprima scritto da Wrangler. Ripeti i binding opzionali KV, AI Search, email e altri quando l’anteprima usa quelle funzionalità. Aggiungi i secret solo per l’anteprima con pnpm wrangler secret put <NAME> --env preview.
Compila e distribuisci l’ambiente di anteprima. La sua prima richiesta applica le migrazioni core in sospeso tramite la modalità auto predefinita.
pnpm build
pnpm wrangler deploy --env preview
Verifica l’URL di anteprima, l’accesso all’amministrazione, il caricamento dei media e qualsiasi binding opzionale prima di condividerla.
Verificare la distribuzione
Dopo il deploy, richiedi una pagina pubblica, accedi a /_emdash/admin, carica e recupera un file media di test e conferma che l’handler scheduled compaia in pnpm wrangler tail.
Risoluzione dei problemi
«D1 binding not found»
Verifica che il nome del binding in wrangler.jsonc corrisponda alla configurazione del tuo database:
// Must match: d1({ binding: "DB" })
"binding": "DB"
«R2 binding not found»
Controlla che il bucket R2 sia collegato correttamente:
// Must match: r2({ binding: "MEDIA" })
"binding": "MEDIA"
Errori di migrazione
Se vedi errori di schema, segui i log del Worker (wrangler tail) e riproduci l’errore per catturare il messaggio sottostante — poi apri una issue con quell’output.