Riferimento alla configurazione

In questa pagina

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.

OpzioneTipoPredefinitoDescrizione
domainsstring[][]Domini email autorizzati
defaultRolenumber20Ruolo 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.

OpzioneTipoPredefinitoDescrizione
maxAgenumber2592000 (30g)Durata della sessione in secondi
slidingbooleantrueReimposta scadenza ad ogni attività

auth.cloudflareAccess

Usa Cloudflare Access come provider di autenticazione al posto delle passkey.

OpzioneTipoPredefinitoDescrizione
teamDomainstringobbligatorioIl dominio del tuo team Access
audiencestringobbligatorioApplication Audience (AUD) tag
autoProvisionbooleantrueCrea utenti al primo accesso
defaultRolenumber30Ruolo predefinito per i nuovi utenti
syncRolesbooleanfalseAggiorna ruolo ad ogni accesso
roleMappingobjectMappa 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.

OpzioneTipoDescrizione
urlstringPercorso file con prefisso file:
sqlite({ url: "file:./data.db" });

libsql(config)

Database libSQL.

OpzioneTipoDescrizione
urlstringURL del database
authTokenstringToken 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.

OpzioneTipoDescrizione
connectionStringstringURL di connessione PostgreSQL
hoststringHost del database
portnumberPorta del database
databasestringNome del database
userstringUtente del database
passwordstringPassword del database
sslbooleanAbilita SSL
pool.minnumberDimensione minima del pool (predefinito: 0)
pool.maxnumberDimensione massima del pool (predefinito: 10)
postgres({ connectionString: process.env.DATABASE_URL });

d1(config)

Database Cloudflare D1. Importa da @emdash-cms/cloudflare.

OpzioneTipoPredefinitoDescrizione
bindingstringNome binding D1 da wrangler.jsonc
sessionstring"disabled"Modalità replica lettura: "disabled", "auto" o "primary-first"
bookmarkCookiestring"__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.

OpzioneTipoDescrizione
directorystringPercorso directory
baseUrlstringURL base per servire i file
local({
	directory: "./uploads",
	baseUrl: "/_emdash/api/media/file",
});

r2(config)

Binding Cloudflare R2.

OpzioneTipoDescrizione
bindingstringNome binding R2
publicUrlstringURL 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.

OpzioneTipoDescrizione
endpointstringURL endpoint S3 (S3_ENDPOINT)
bucketstringNome bucket (S3_BUCKET)
accessKeyIdstringChiave di accesso (S3_ACCESS_KEY_ID)
secretAccessKeystringChiave segreta (S3_SECRET_ACCESS_KEY)
regionstringRegione, predefinito "auto" (S3_REGION)
publicUrlstringURL 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:

VariabileDescrizione
EMDASH_SITE_URLOrigine pubblica lato browser (fallback a SITE_URL)
EMDASH_DATABASE_URLSovrascrive l’URL del database
EMDASH_AUTH_SECRETSecret per l’autenticazione con passkey
EMDASH_PREVIEW_SECRETSecret per la generazione del token di anteprima
EMDASH_URLURL 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"
	}
}
OpzioneDescrizione
labelNome del template visualizzato
descriptionDescrizione del template
seedPercorso del file seed JSON
urlURL remoto per la sincronizzazione schema
previewURL 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