EmDash è configurato tramite due file: astro.config.mjs per l’integrazione e src/live.config.ts per le collezioni di contenuti.
Integrazione Astro
Configura EmDash come integrazione Astro:
import { defineConfig } from "astro/config";
import emdash, { local, r2, s3 } from "emdash/astro";
import { sqlite, libsql, d1 } from "emdash/db";
export default defineConfig({
integrations: [
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
plugins: [],
}),
],
});
Opzioni dell’integrazione
database
Obbligatorio. Configurazione dell’adattatore database.
// SQLite (Node.js)
database: sqlite({ url: "file:./data.db" });
// PostgreSQL
database: postgres({ connectionString: process.env.DATABASE_URL });
// libSQL
database: libsql({
url: process.env.LIBSQL_DATABASE_URL,
authToken: process.env.LIBSQL_AUTH_TOKEN,
});
// Cloudflare D1 (importa da @emdash-cms/cloudflare)
database: d1({ binding: "DB" });
Vedi Opzioni database per i dettagli.
storage
Obbligatorio. Configurazione dell’adattatore di archiviazione media.
// Filesystem locale (sviluppo)
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
});
// Binding R2 (Cloudflare Workers)
storage: r2({
binding: "MEDIA",
publicUrl: "https://pub-xxxx.r2.dev", // optional
});
// S3-compatible (any platform) — all fields from S3_* environment variables
storage: s3()
// Or with explicit values
storage: s3({
endpoint: "https://s3.amazonaws.com",
bucket: "my-bucket",
accessKeyId: process.env.S3_ACCESS_KEY_ID,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
region: "us-east-1", // optional, default: "auto"
publicUrl: "https://cdn.example.com", // optional
});
Vedi Opzioni di storage per i dettagli.
plugins
Opzionale. Array di plugin EmDash.
import seoPlugin from "@emdash-cms/plugin-seo";
plugins: [seoPlugin()];
auth
Opzionale. Configurazione dell’autenticazione.
auth: {
// Configurazione registrazione autonoma
selfSignup: {
domains: ["example.com"],
defaultRole: 20, // Contributor
},
// Provider OAuth
oauth: {
github: {
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
},
google: {
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
},
},
// Configurazione sessione
session: {
maxAge: 30 * 24 * 60 * 60, // 30 giorni
sliding: true, // Reimposta scadenza ad ogni attività
},
// OPPURE usa Cloudflare Access (modalità esclusiva)
cloudflareAccess: {
teamDomain: "myteam.cloudflareaccess.com",
audience: "your-app-audience-tag",
autoProvision: true,
defaultRole: 30,
syncRoles: false,
roleMapping: {
"Admins": 50,
"Editors": 40,
},
},
}
auth.selfSignup
Consenti agli utenti di registrarsi autonomamente se il loro dominio email è autorizzato.
| Opzione | Tipo | Predefinito | Descrizione |
|---|---|---|---|
domains | string[] | [] | Domini email autorizzati |
defaultRole | number | 20 | Ruolo per le autoregistrazioni |
selfSignup: {
domains: ["example.com", "acme.org"],
defaultRole: 20, // Contributor
}
auth.oauth
Configura i provider di accesso OAuth.
oauth: {
github: {
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
},
google: {
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
},
}
auth.session
Configurazione della sessione.
| Opzione | Tipo | Predefinito | Descrizione |
|---|---|---|---|
maxAge | number | 2592000 (30g) | Durata della sessione in secondi |
sliding | boolean | true | Reimposta scadenza ad ogni attività |
auth.cloudflareAccess
Usa Cloudflare Access come provider di autenticazione al posto delle passkey.
| Opzione | Tipo | Predefinito | Descrizione |
|---|---|---|---|
teamDomain | string | obbligatorio | Il dominio del tuo team Access |
audience | string | obbligatorio | Application Audience (AUD) tag |
autoProvision | boolean | true | Crea utenti al primo accesso |
defaultRole | number | 30 | Ruolo predefinito per i nuovi utenti |
syncRoles | boolean | false | Aggiorna ruolo ad ogni accesso |
roleMapping | object | — | Mappa gruppi IdP a ruoli |
siteUrl
Opzionale. L’origine pubblica lato browser del sito (schema + host + porta opzionale, nessun percorso).
Dietro un proxy inverso con terminazione TLS, Astro.url restituisce l’indirizzo interno (http://localhost:4321) anziché quello pubblico (https://cms.example.com). Questo rompe passkey, corrispondenza dell’origine CSRF, redirect OAuth, redirect di login, scoperta MCP, esportazione snapshot, sitemap, robots.txt e dati strutturati JSON-LD. Impostando siteUrl si correggono tutti questi problemi in una volta sola.
L’integrazione valida questo valore al caricamento: deve essere un URL valido con protocollo http: o https: e viene normalizzato come origin (il percorso viene rimosso).
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
siteUrl: "https://cms.example.com",
});
Quando siteUrl non è impostato nella configurazione, EmDash controlla le variabili d’ambiente in ordine: EMDASH_SITE_URL, poi SITE_URL. Questo è utile per i deployment in container dove l’URL pubblico viene impostato a runtime.
Configurazione del proxy inverso
Astro riflette X-Forwarded-* solo quando l’host pubblico è autorizzato. Configura security.allowedDomains per l’hostname (e gli schemi) dei tuoi utenti. In astro dev, aggiungi il corrispondente vite.server.allowedHosts affinché Vite accetti l’header Host del proxy.
Preferisci correggere prima allowedDomains (e gli header inoltrati); usa siteUrl quando l’URL ricostruito ancora diverge dall’origine del browser (tipico quando TLS viene terminato a monte e la richiesta upstream resta http://).
Con TLS a monte, collegare il server di sviluppo al loopback (astro dev --host 127.0.0.1) è spesso sufficiente: il proxy si connette localmente mentre siteUrl corrisponde all’origine HTTPS pubblica.
import { defineConfig } from "astro/config";
import emdash, { local } from "emdash/astro";
import { sqlite } from "emdash/db";
export default defineConfig({
security: {
allowedDomains: [
{ hostname: "cms.example.com", protocol: "https" },
{ hostname: "cms.example.com", protocol: "http" },
],
},
vite: {
server: {
allowedHosts: ["cms.example.com"],
},
},
integrations: [
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
siteUrl: "https://cms.example.com",
}),
],
});
Adattatori database
Importa da emdash/db:
import { sqlite, libsql, postgres, d1 } from "emdash/db";
sqlite(config)
Database SQLite tramite better-sqlite3.
| Opzione | Tipo | Descrizione |
|---|---|---|
url | string | Percorso file con prefisso file: |
sqlite({ url: "file:./data.db" });
libsql(config)
Database libSQL.
| Opzione | Tipo | Descrizione |
|---|---|---|
url | string | URL del database |
authToken | string | Token di autenticazione (opzionale per file locali) |
libsql({
url: process.env.LIBSQL_DATABASE_URL,
authToken: process.env.LIBSQL_AUTH_TOKEN,
});
postgres(config)
Database PostgreSQL con pool di connessioni.
| Opzione | Tipo | Descrizione |
|---|---|---|
connectionString | string | URL di connessione PostgreSQL |
host | string | Host del database |
port | number | Porta del database |
database | string | Nome del database |
user | string | Utente del database |
password | string | Password del database |
ssl | boolean | Abilita SSL |
pool.min | number | Dimensione minima del pool (predefinito: 0) |
pool.max | number | Dimensione massima del pool (predefinito: 10) |
postgres({ connectionString: process.env.DATABASE_URL });
d1(config)
Database Cloudflare D1. Importa da @emdash-cms/cloudflare.
| Opzione | Tipo | Predefinito | Descrizione |
|---|---|---|---|
binding | string | — | Nome binding D1 da wrangler.jsonc |
session | string | "disabled" | Modalità replica lettura: "disabled", "auto" o "primary-first" |
bookmarkCookie | string | "__ec_d1_bookmark" | Nome cookie per i bookmark di sessione |
// Base
d1({ binding: "DB" });
// Con repliche di lettura
d1({ binding: "DB", session: "auto" });
Quando session è "auto" o "primary-first", EmDash usa l’API D1 Sessions per instradare le query di lettura alle repliche vicine. Gli utenti autenticati ottengono coerenza lettura-dopo-scrittura basata su bookmark. Vedi Opzioni database — Repliche di lettura per i dettagli.
Adattatori di storage
Importa da emdash/astro:
import emdash, { local, r2, s3 } from "emdash/astro";
local(config)
Archiviazione su filesystem locale.
| Opzione | Tipo | Descrizione |
|---|---|---|
directory | string | Percorso directory |
baseUrl | string | URL base per servire i file |
local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
});
r2(config)
Binding Cloudflare R2.
| Opzione | Tipo | Descrizione |
|---|---|---|
binding | string | Nome binding R2 |
publicUrl | string | URL pubblico opzionale |
r2({
binding: "MEDIA",
publicUrl: "https://pub-xxxx.r2.dev",
});
s3(config?)
Storage compatibile S3. Tutti i campi di configurazione sono opzionali: qualsiasi campo omesso in s3({...}) viene risolto dalla corrispondente variabile d’ambiente S3_* all’avvio del processo Node. I valori espliciti hanno sempre la precedenza.
Prerequisito: installa @aws-sdk/client-s3 e @aws-sdk/s3-request-presigner nel tuo progetto. Il core di EmDash non include l’AWS SDK. Vedi Opzioni di storage → Storage compatibile S3 per i dettagli.
| Opzione | Tipo | Descrizione |
|---|---|---|
endpoint | string | URL endpoint S3 (S3_ENDPOINT) |
bucket | string | Nome bucket (S3_BUCKET) |
accessKeyId | string | Chiave di accesso (S3_ACCESS_KEY_ID) |
secretAccessKey | string | Chiave segreta (S3_SECRET_ACCESS_KEY) |
region | string | Regione, predefinito "auto" (S3_REGION) |
publicUrl | string | URL CDN opzionale (S3_PUBLIC_URL) |
// All fields from S3_* environment variables (Node container deployments)
s3()
// Mix: CDN from config, rest from environment
s3({ publicUrl: "https://cdn.example.com" })
// All explicit (unchanged from before)
s3({
endpoint: "https://xxx.r2.cloudflarestorage.com",
bucket: "media",
accessKeyId: process.env.R2_ACCESS_KEY_ID,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
publicUrl: "https://cdn.example.com",
})
La risoluzione delle variabili d’ambiente a runtime è una funzionalità esclusiva di Node. Su Cloudflare Workers, i secret e le variabili sono esposti tramite il parametro env del fetch handler, non tramite process.env, quindi le variabili d’ambiente S3_* non vengono lette. I deployment su Workers dovrebbero usare l’adattatore r2(config) o passare valori espliciti a s3({...}). Vedi Opzioni di storage per i dettagli.
Live Collections
Configura il loader EmDash in src/live.config.ts:
import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";
export const collections = {
_emdash: defineLiveCollection({
loader: emdashLoader(),
}),
};
Opzioni del loader
La funzione emdashLoader() accetta configurazione opzionale:
emdashLoader({
// Attualmente nessuna opzione — riservato per uso futuro
});
Variabili d’ambiente
EmDash rispetta queste variabili d’ambiente:
| Variabile | Descrizione |
|---|---|
EMDASH_SITE_URL | Origine pubblica lato browser (fallback a SITE_URL) |
EMDASH_DATABASE_URL | Sovrascrive l’URL del database |
EMDASH_AUTH_SECRET | Secret per l’autenticazione con passkey |
EMDASH_PREVIEW_SECRET | Secret per la generazione del token di anteprima |
EMDASH_URL | URL EmDash remoto per la sincronizzazione dello schema |
Genera un secret di autenticazione con:
npx emdash auth secret
Configurazione package.json
Configurazione opzionale in package.json:
{
"emdash": {
"label": "My Blog Template",
"description": "A clean, minimal blog template",
"seed": ".emdash/seed.json",
"url": "https://my-site.pages.dev",
"preview": "https://emdash-blog.pages.dev"
}
}
| Opzione | Descrizione |
|---|---|
label | Nome del template visualizzato |
description | Descrizione del template |
seed | Percorso del file seed JSON |
url | URL remoto per la sincronizzazione schema |
preview | URL sito demo per l’anteprima del template |
Configurazione TypeScript
EmDash genera i tipi in .emdash/types.ts. Aggiungi al tuo tsconfig.json:
{
"compilerOptions": {
"paths": {
"@emdash-cms/types": ["./.emdash/types.ts"]
}
}
}
Genera i tipi con:
npx emdash types