Riferimento Configurazione

In questa pagina

EmDash è configurato tramite due file: astro.config.mjs per l’integrazione e src/live.config.ts per le raccolte di contenuti.

Integrazione Astro

Configura EmDash come integrazione Astro in astro.config.mjs:

import { defineConfig } from "astro/config";
import emdash, { local, s3 } from "emdash/astro";
import { sqlite, libsql } 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 di integrazione

database

Obbligatorio. Configurazione dell’adattatore del database. Scegli un adattatore:

// 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 dettagli.

storage

Obbligatorio. Configurazione dell’adattatore di archiviazione media. Scegli un adattatore:

// File system 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", // opzionale
});

// Compatibile S3 (qualsiasi piattaforma) — tutti i campi da variabili d'ambiente S3_*
storage: s3()

// Oppure con valori espliciti
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", // opzionale, predefinito: "auto"
	publicUrl: "https://cdn.example.com", // opzionale
});

Vedi Opzioni di Archiviazione per dettagli.

plugins

Opzionale. Array di plugin EmDash. L’esempio seguente registra un plugin:

import seoPlugin from "@emdash-cms/plugin-seo";

plugins: [seoPlugin()];

fonts

Opzionale. Configurazione dei font dell’interfaccia amministrativa.

Per impostazione predefinita, EmDash carica Noto Sans tramite l’API Font di Astro. I font vengono scaricati da Google al momento della build e auto-ospitati, quindi non ci sono richieste CDN a runtime. Il font base copre scritture latina, cirillica, greca, devanagari e vietnamita.

Per aggiungere supporto per sistemi di scrittura aggiuntivi, passa i nomi degli script. L’esempio seguente aggiunge arabo e giapponese:

emdash({
  fonts: {
    scripts: ["arabic", "japanese"],
  },
})

Gli script disponibili sono arabic, armenian, bengali, chinese-simplified, chinese-traditional, chinese-hongkong, devanagari, ethiopic, farsi, georgian, gujarati, gurmukhi, hebrew, japanese, kannada, khmer, korean, lao, malayalam, myanmar, oriya, sinhala, tamil, telugu, thai e tibetan.

Ogni script corrisponde alla variante Noto Sans corrispondente su Google Fonts (ad es. "arabic" carica Noto Sans Arabic). Tutte le facce del font condividono un singolo nome font-family e usano unicode-range in modo che il browser scarichi solo i file necessari per i caratteri sulla pagina.

Imposta su false per disabilitare completamente l’iniezione di font e utilizzare i font di sistema:

emdash({
	fonts: false,
})

Il CSS amministrativo utilizza la variabile CSS --font-emdash. Questa viene impostata automaticamente dalla configurazione del font sopra.

auth

Opzionale. Un adattatore di autenticazione. Il login integrato di EmDash utilizza le passkey; impostare auth le sostituisce con un provider esterno. L’adattatore Cloudflare Access, access(), è fornito da @emdash-cms/cloudflare:

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

emdash({
	auth: access({
		teamDomain: "myteam.cloudflareaccess.com",
		audience: "your-app-audience-tag",
		roleMapping: {
			Admins: 50,
			Editors: 40,
		},
	}),
});

Opzioni per access():

OpzioneTipoPredefinitoDescrizione
teamDomainstringobbligatorioIl tuo dominio team Cloudflare Access
audiencestringTag Application Audience (AUD). Su Workers, preferisci audienceEnvVar.
audienceEnvVarstring"CF_ACCESS_AUDIENCE"Variabile d’ambiente da cui leggere il tag audience a runtime
autoProvisionbooleantrueCrea un utente EmDash al primo login
defaultRolenumber30Livello di ruolo per utenti non corrispondenti a roleMapping (vedi Ruoli utente)
syncRolesbooleanfalseRiapplica roleMapping ad ogni login invece che solo al provisioning
roleMappingobjectMappa nomi gruppo IdP a livelli di ruolo EmDash; vince la prima corrispondenza

authProviders

Opzionale. Un array di provider di login collegabili (livello superiore, accanto a auth). Ogni voce è il risultato della chiamata di una factory di provider, come mostrato di seguito:

import { github } from "emdash/auth/providers/github";
import { google } from "emdash/auth/providers/google";
import { atproto } from "@emdash-cms/auth-atproto";

emdash({
	authProviders: [github(), google(), atproto()],
});

Provider integrati:

  • github() — legge EMDASH_OAUTH_GITHUB_CLIENT_ID / EMDASH_OAUTH_GITHUB_CLIENT_SECRET (o fallback senza prefisso).
  • google() — legge EMDASH_OAUTH_GOOGLE_CLIENT_ID / EMDASH_OAUTH_GOOGLE_CLIENT_SECRET.
  • atproto() — Login account Atmosphere (Bluesky e la rete AT Protocol più ampia). Nessuna variabile d’ambiente necessaria. Accetta { allowedDIDs, allowedHandles, defaultRole }. Vedi la guida al login Atmosphere.

I pacchetti di terze parti possono registrare i propri provider utilizzando la stessa forma AuthProviderDescriptor — vedi Provider di Login.

siteUrl

Opzionale. L’origine pubblica rivolta al browser per il sito (schema + host + porta opzionale, nessun percorso).

Dietro un proxy inverso di terminazione TLS, Astro.url restituisce l’indirizzo interno (http://localhost:4321) invece di quello pubblico (https://cms.example.com). Questo rompe passkey, corrispondenza origine CSRF, reindirizzamenti OAuth, reindirizzamenti login, scoperta MCP, esportazioni snapshot, sitemap, robots.txt e dati strutturati JSON-LD. Imposta siteUrl per risolvere tutto in una volta.

L’integrazione valida questo valore al momento del caricamento: deve essere un URL valido con protocollo http: o https: ed è normalizzato a origine (il percorso viene rimosso).

L’esempio seguente imposta l’origine pubblica:

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 deployment di container in cui l’URL pubblico è impostato a runtime.

Verifica passkey multi-origine

siteUrl definisce un’unica origine canonica. Quando lo stesso deployment EmDash è raggiungibile sotto diversi hostname che condividono un dominio genitore registrabile (ad es. https://example.com e https://preview.example.com), la verifica passkey rifiuta asserzioni la cui origine non corrisponde esattamente a siteUrl — anche se WebAuthn permette alle passkey di essere valide tra sottodomini sotto lo stesso rpId.

Dichiara origini accettate aggiuntive tramite allowedOrigins in astro.config.mjs o la variabile d’ambiente EMDASH_ALLOWED_ORIGINS. Il siteUrl canonico rimane la fonte di rpId; le voci elencate qui sono accettate al momento della verifica. Le due fonti vengono unite a runtime, quindi la configurazione può dichiarare le origini stabili (versionizzate, revisionate nel codice) mentre l’ambiente aggiunge extra specifici dell’ambiente (ad es. anteprime PR effimere).

L’esempio seguente dichiara un’origine aggiuntiva nella configurazione:

emdash({
	siteUrl: "https://example.com",
	allowedOrigins: ["https://preview.example.com"],
})

I valori equivalenti possono anche provenire da variabili d’ambiente:

EMDASH_SITE_URL=https://example.com
EMDASH_ALLOWED_ORIGINS=https://preview.example.com,https://staging.example.com
Validazione

EmDash valida questi per prevenire configurazioni morte che il browser non onorerebbe mai:

  • Ogni voce deve essere un URL analizzabile http: o https: senza punto finale e senza etichette vuote nell’hostname.
  • Quando allowedOrigins non è vuoto, siteUrl deve essere impostato (qualsiasi fonte) e non deve essere un letterale IP o avere un hostname con punto finale.
  • Ogni origine deve essere lo stesso hostname di siteUrl o un sottodominio di esso. (WebAuthn richiede che rpId sia un suffisso registrabile di ogni origine.)

Quando la validazione fallisce, vedrai un errore attribuito alla fonte come EmDash config error in EMDASH_ALLOWED_ORIGINS: "https://other-site.com" is not a subdomain of siteUrl "https://example.com". Allowed origins must be the same hostname as siteUrl or a subdomain of it.

Dove appare l’errore dipende da dove sono dichiarati i valori:

  • All’avvio di Astro, quando sia config.allowedOrigins che config.siteUrl provengono da astro.config.mjs — errori di battitura nel codice fanno fallire la build.
  • Alla prima verifica passkey, quando uno dei valori proviene da EMDASH_ALLOWED_ORIGINS o EMDASH_SITE_URL — le discrepanze ambientali emergono come 500 al primo tentativo di verifica.

Configurazione proxy inverso

Astro riflette X-Forwarded-* solo quando l’host pubblico è consentito. Configura security.allowedDomains per l’hostname (e gli schemi) che i tuoi utenti raggiungono. In astro dev, aggiungi vite.server.allowedHosts corrispondenti affinché Vite accetti l’header Host del proxy.

Preferisci correggere prima allowedDomains (e gli header inoltrati); usa siteUrl quando l’URL ricostruito diverge ancora dall’origine del browser (tipico quando TLS è terminato davanti e la richiesta upstream rimane http://).

Con TLS davanti, vincolare il server di sviluppo a loopback (astro dev --host 127.0.0.1) è spesso sufficiente: il proxy si connette localmente mentre siteUrl corrisponde all’origine HTTPS pubblica.

Se il tuo proxy scrive un header IP client, imposta trustedProxyHeaders affinché i limiti di rate di EmDash possano utilizzare l’IP client reale invece di raggruppare ogni richiesta sotto una chiave “unknown” condivisa.

La seguente configurazione imposta allowedDomains, vite.server.allowedHosts e siteUrl insieme per un deployment con proxy inverso:

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",
		}),
	],
});

trustedProxyHeaders

Opzionale. Header di cui fidarsi per la risoluzione IP client quando si esegue dietro un proxy inverso che controlli. Utilizzato dai limiti di rate di autenticazione (magic-link, registrazione, passkey, flusso dispositivo OAuth) e dall’endpoint commenti pubblici.

Su Cloudflare l’oggetto cf allegato alla richiesta viene utilizzato automaticamente — normalmente non è necessario impostare questo. Su deployment auto-ospitati dietro nginx, Caddy, Traefik, Fly, Railway o simili, impostalo sull’header che il tuo proxy scrive affinché i limiti di rate possano raggruppare per IP client reale invece di trattare ogni richiesta come “unknown”.

L’esempio seguente si fida dell’header x-real-ip impostato da nginx, Caddy o Traefik:

emdash({
	database: sqlite({ url: "file:./data.db" }),
	trustedProxyHeaders: ["x-real-ip"],
});

Gli header vengono provati in ordine. I valori che corrispondono a *-forwarded-for vengono analizzati come liste separate da virgole e viene utilizzata la prima voce. L’esempio seguente preferisce l’header di Fly.io e ricade su x-forwarded-for:

emdash({
	trustedProxyHeaders: ["fly-client-ip", "x-forwarded-for"],
});

Quando non è impostato nella configurazione, EmDash legge la variabile d’ambiente EMDASH_TRUSTED_PROXY_HEADERS (separata da virgole). Un array vuoto esplicito nella configurazione sovrascrive la variabile d’ambiente.

maxUploadSize

Opzionale. Dimensione massima consentita per il caricamento di file multimediali in byte. Si applica sia ai caricamenti multipart diretti che ai caricamenti URL firmati. Predefinito a 52_428_800 (50 MB). L’esempio seguente aumenta il limite a 100 MB:

emdash({
	database: sqlite({ url: "file:./data.db" }),
	storage: local({
		directory: "./uploads",
		baseUrl: "/_emdash/api/media/file",
	}),
	maxUploadSize: 100 * 1024 * 1024, // 100 MB
});
ValoreDescrizione
number (byte)Deve essere un intero finito positivo
omessoPredefinito a 50 MB

I caricamenti che superano il limite configurato vengono rifiutati con una risposta 413 Payload Too Large sul percorso di caricamento diretto, o un 400 Validation Error sul percorso URL firmato.

Adattatori database

Importa gli adattatori da emdash/db:

import { sqlite, libsql, postgres } from "emdash/db";

sqlite(config)

Database SQLite usando better-sqlite3. L’esempio seguente si connette a un file locale:

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

libsql(config)

Database libSQL. L’esempio seguente si connette a un database libSQL remoto:

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 connection pooling.

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 pool (predefinito: 0)
pool.maxnumberDimensione massima pool (predefinito: 10)

L’esempio seguente si connette con una stringa di connessione:

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"__em_d1_bookmark"Nome cookie per segnalibri di sessione

L’esempio seguente mostra un binding di base e uno con repliche di lettura abilitate:

// Base
d1({ binding: "DB" });

// Con repliche di lettura
d1({ binding: "DB", session: "auto" });

Quando session è "auto" o "primary-first", EmDash utilizza l’API Sessions D1 per instradare le query di lettura a repliche vicine. Gli utenti autenticati ottengono coerenza read-your-writes basata su segnalibri. Vedi Opzioni Database — Repliche di Lettura per dettagli.

Adattatori di archiviazione

Importa local e s3 da emdash/astro. L’adattatore r2 è importato da @emdash-cms/cloudflare:

import emdash, { local, s3 } from "emdash/astro";
import { r2 } from "@emdash-cms/cloudflare";

local(config)

Archiviazione su file system locale. L’esempio seguente serve i caricamenti da una directory locale:

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

r2(config)

Binding Cloudflare R2. L’esempio seguente utilizza un binding R2 con un URL pubblico:

OpzioneTipoDescrizione
bindingstringNome binding R2
publicUrlstringURL pubblico opzionale
r2({
	binding: "MEDIA",
	publicUrl: "https://pub-xxxx.r2.dev",
});

s3(config?)

Archiviazione compatibile S3. Tutti i campi di configurazione sono opzionali: qualsiasi campo omesso da s3({...}) viene risolto dalla variabile d’ambiente S3_* corrispondente quando il processo Node si avvia. I valori espliciti hanno sempre precedenza.

Prerequisito: installa @aws-sdk/client-s3 e @aws-sdk/s3-request-presigner nel tuo progetto. EmDash core non include l’AWS SDK. Vedi Opzioni di Archiviazione: Archiviazione Compatibile S3 per 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)

Gli esempi seguenti risolvono tutti i campi dall’ambiente, mescolano configurazione e ambiente, o passano ogni campo esplicitamente:

// Tutti i campi da variabili d'ambiente S3_* (deployment container Node)
s3()

// Misto: CDN dalla configurazione, resto dall'ambiente
s3({ publicUrl: "https://cdn.example.com" })

// Tutto esplicito
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 di variabili d’ambiente a runtime è una funzionalità solo Node. Su Cloudflare Workers, i segreti e le variabili sono esposti tramite il parametro env del gestore fetch, non tramite process.env, quindi le variabili d’ambiente S3_* non vengono raccolte. I deployment Workers dovrebbero utilizzare l’adattatore r2(config) o passare valori espliciti a s3({...}). Vedi Opzioni di Archiviazione per dettagli.

Raccolte in tempo reale

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() non accetta argomenti:

emdashLoader();

Variabili d’ambiente

EmDash rispetta queste variabili d’ambiente:

VariabileDescrizione
EMDASH_SITE_URLOrigine pubblica rivolta al browser (ricade su SITE_URL)
EMDASH_ALLOWED_ORIGINSLista separata da virgole di origini aggiuntive accettate dalla verifica passkey (deployment multi-sottodominio).
EMDASH_DATABASE_URLSovrascrivi URL database
EMDASH_ENCRYPTION_KEYChiave per crittografare i segreti dei plugin a riposo. Fornita dall’operatore — mai memorizzata nel database.
EMDASH_PREVIEW_SECRETSovrascrittura opzionale per il segreto HMAC di anteprima. Quando non impostato, viene generato un valore stabile per sito e memorizzato nel database.
EMDASH_IP_SALTSovrascrittura opzionale per il salt hash IP del commentatore. Quando non impostato, viene generato un valore stabile per sito e memorizzato nel database.
EMDASH_AUTH_SECRETLegacy. Usato come fonte salt IP se impostato; le installazioni esistenti dovrebbero mantenerlo per preservare hash IP commentatore stabili attraverso gli aggiornamenti.
EMDASH_URLURL EmDash remoto per sincronizzazione schema

Genera una chiave di crittografia con il seguente comando:

npx emdash secrets generate

Configurazione package.json

I template e i siti possono dichiarare metadati opzionali sotto una chiave emdash in package.json:

{
	"emdash": {
		"label": "My Blog Template",
		"seed": ".emdash/seed.json",
		"url": "https://my-site.pages.dev"
	}
}
OpzioneDescrizione
labelNome template per visualizzazione
seedPercorso al file JSON seed
urlURL remoto per sincronizzazione schema

Configurazione TypeScript

EmDash genera tipi in .emdash/types.ts. Aggiungi un alias di percorso al tuo tsconfig.json:

{
	"compilerOptions": {
		"paths": {
			"@emdash-cms/types": ["./.emdash/types.ts"]
		}
	}
}

Genera tipi con il seguente comando:

npx emdash types