EmDash wird über zwei Dateien konfiguriert: astro.config.mjs für die Integration und src/live.config.ts für Inhaltssammlungen.
Astro-Integration
Konfigurieren Sie EmDash als Astro-Integration:
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: [],
}),
],
});
Integrationsoptionen
database
Erforderlich. Konfiguration des Datenbankadapters.
// 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 (Import aus @emdash-cms/cloudflare)
database: d1({ binding: "DB" });
Weitere Informationen finden Sie unter Datenbankoptionen.
storage
Erforderlich. Konfiguration des Medienspeicheradapters.
// Lokales Dateisystem (Entwicklung)
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
});
// R2-Bindung (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
});
Weitere Informationen finden Sie unter Speicheroptionen.
plugins
Optional. Array von EmDash-Plugins.
import seoPlugin from "@emdash-cms/plugin-seo";
plugins: [seoPlugin()];
auth
Optional. Authentifizierungskonfiguration.
auth: {
// Selbstregistrierungskonfiguration
selfSignup: {
domains: ["example.com"],
defaultRole: 20, // Contributor
},
// OAuth-Anbieter
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,
},
},
// Sitzungskonfiguration
session: {
maxAge: 30 * 24 * 60 * 60, // 30 Tage
sliding: true, // Ablauf bei Aktivität zurücksetzen
},
// ODER Cloudflare Access verwenden (exklusiver Modus)
cloudflareAccess: {
teamDomain: "myteam.cloudflareaccess.com",
audience: "your-app-audience-tag",
autoProvision: true,
defaultRole: 30,
syncRoles: false,
roleMapping: {
"Admins": 50,
"Editors": 40,
},
},
}
auth.selfSignup
Ermöglicht Benutzern die Selbstregistrierung, wenn ihre E-Mail-Domäne zugelassen ist.
| Option | Typ | Standard | Beschreibung |
|---|---|---|---|
domains | string[] | [] | Zugelassene E-Mail-Domänen |
defaultRole | number | 20 | Rolle für Selbstregistrierungen |
selfSignup: {
domains: ["example.com", "acme.org"],
defaultRole: 20, // Contributor
}
auth.oauth
Konfigurieren Sie OAuth-Anmeldeanbieter.
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
Sitzungskonfiguration.
| Option | Typ | Standard | Beschreibung |
|---|---|---|---|
maxAge | number | 2592000 (30d) | Sitzungsdauer in Sekunden |
sliding | boolean | true | Ablauf bei Aktivität zurücksetzen |
auth.cloudflareAccess
Verwenden Sie Cloudflare Access als Authentifizierungsanbieter anstelle von Passkeys.
| Option | Typ | Standard | Beschreibung |
|---|---|---|---|
teamDomain | string | erforderlich | Ihre Access-Team-Domäne |
audience | string | erforderlich | Anwendungs-Audience-Tag (AUD) |
autoProvision | boolean | true | Benutzer beim ersten Login erstellen |
defaultRole | number | 30 | Standardrolle für neue Benutzer |
syncRoles | boolean | false | Rolle bei jeder Anmeldung aktualisieren |
roleMapping | object | — | IdP-Gruppen Rollen zuordnen |
siteUrl
Optional. Der öffentliche, browserseitige Ursprung der Website (Schema + Host + optionaler Port, kein Pfad).
Hinter einem TLS-terminierenden Reverse-Proxy gibt Astro.url die interne Adresse (http://localhost:4321) statt der öffentlichen (https://cms.example.com) zurück. Dies bricht Passkeys, CSRF-Origin-Matching, OAuth-Weiterleitungen, Login-Weiterleitungen, MCP-Erkennung, Snapshot-Exporte, Sitemap, robots.txt und JSON-LD strukturierte Daten. Setzen Sie siteUrl, um all dies auf einmal zu beheben.
Die Integration validiert diesen Wert beim Laden: Es muss eine gültige URL mit dem Protokoll http: oder https: sein und wird auf origin normalisiert (der Pfad wird entfernt).
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
siteUrl: "https://cms.example.com",
});
Wenn siteUrl nicht in der Konfiguration gesetzt ist, prüft EmDash Umgebungsvariablen in dieser Reihenfolge: EMDASH_SITE_URL, dann SITE_URL. Dies ist nützlich für Container-Deployments, bei denen die öffentliche URL zur Laufzeit gesetzt wird.
Reverse-Proxy-Einrichtung
Astro berücksichtigt X-Forwarded-* nur, wenn der öffentliche Host zugelassen ist. Konfigurieren Sie security.allowedDomains für den Hostnamen (und die Schemata), über die Ihre Benutzer zugreifen. Fügen Sie in astro dev passende vite.server.allowedHosts hinzu, damit Vite den Host-Header des Proxys akzeptiert.
Korrigieren Sie vorzugsweise zuerst allowedDomains (und weitergeleitete Header). Verwenden Sie siteUrl, wenn die rekonstruierte URL weiterhin vom Browser-Ursprung abweicht (typisch, wenn TLS vor der Anwendung terminiert wird und die Upstream-Anfrage http:// bleibt).
Wenn TLS vorgeschaltet ist, reicht es oft aus, den Entwicklungsserver an Loopback zu binden (astro dev --host 127.0.0.1): Der Proxy verbindet sich lokal, während siteUrl mit dem öffentlichen HTTPS-Ursprung übereinstimmt.
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",
}),
],
});
Datenbankadapter
Import aus emdash/db:
import { sqlite, libsql, postgres, d1 } from "emdash/db";
sqlite(config)
SQLite-Datenbank mit better-sqlite3.
| Option | Typ | Beschreibung |
|---|---|---|
url | string | Dateipfad mit dem Präfix file: |
sqlite({ url: "file:./data.db" });
libsql(config)
libSQL-Datenbank.
| Option | Typ | Beschreibung |
|---|---|---|
url | string | Datenbank-URL |
authToken | string | Authentifizierungstoken (optional für lokale Dateien) |
libsql({
url: process.env.LIBSQL_DATABASE_URL,
authToken: process.env.LIBSQL_AUTH_TOKEN,
});
postgres(config)
PostgreSQL-Datenbank mit Verbindungspooling.
| Option | Typ | Beschreibung |
|---|---|---|
connectionString | string | PostgreSQL-Verbindungs-URL |
host | string | Datenbankhost |
port | number | Datenbankport |
database | string | Datenbankname |
user | string | Datenbankbenutzer |
password | string | Datenbankpasswort |
ssl | boolean | SSL aktivieren |
pool.min | number | Minimale Poolgröße (Standard: 0) |
pool.max | number | Maximale Poolgröße (Standard: 10) |
postgres({ connectionString: process.env.DATABASE_URL });
d1(config)
Cloudflare-D1-Datenbank. Import aus @emdash-cms/cloudflare.
| Option | Typ | Standard | Beschreibung |
|---|---|---|---|
binding | string | — | D1-Bindungsname aus wrangler.jsonc |
session | string | "disabled" | Lesereplikationsmodus: "disabled", "auto" oder "primary-first" |
bookmarkCookie | string | "__ec_d1_bookmark" | Cookie-Name für Sitzungs-Lesezeichen |
// Einfach
d1({ binding: "DB" });
// Mit Lesereplikaten
d1({ binding: "DB", session: "auto" });
Wenn session auf "auto" oder "primary-first" gesetzt ist, verwendet EmDash die D1-Sessions-API, um Leseabfragen an nahegelegene Replikate weiterzuleiten. Authentifizierte Benutzer erhalten eine lesezeichenbasierte Lese-nach-Schreib-Konsistenz. Weitere Informationen finden Sie unter Datenbankoptionen — Lesereplikate.
Speicheradapter
Import aus emdash/astro:
import emdash, { local, r2, s3 } from "emdash/astro";
local(config)
Lokaler Dateisystemspeicher.
| Option | Typ | Beschreibung |
|---|---|---|
directory | string | Verzeichnispfad |
baseUrl | string | Basis-URL zum Bereitstellen von Dateien |
local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
});
r2(config)
Cloudflare-R2-Bindung.
| Option | Typ | Beschreibung |
|---|---|---|
binding | string | R2-Bindungsname |
publicUrl | string | Optionale öffentliche URL |
r2({
binding: "MEDIA",
publicUrl: "https://pub-xxxx.r2.dev",
});
s3(config?)
S3-kompatibler Speicher. Alle Konfigurationsfelder sind optional: Felder, die in s3({...}) weggelassen werden, werden beim Start des Node-Prozesses aus der entsprechenden S3_*-Umgebungsvariable aufgelöst. Explizite Werte haben immer Vorrang.
Voraussetzung: Installieren Sie @aws-sdk/client-s3 und @aws-sdk/s3-request-presigner in Ihrem Projekt. Der EmDash-Kern enthält das AWS SDK nicht. Siehe Speicheroptionen → S3-kompatibler Speicher für Details.
| Option | Typ | Beschreibung |
|---|---|---|
endpoint | string | S3-Endpunkt-URL (S3_ENDPOINT) |
bucket | string | Bucket-Name (S3_BUCKET) |
accessKeyId | string | Zugriffsschlüssel (S3_ACCESS_KEY_ID) |
secretAccessKey | string | Geheimer Schlüssel (S3_SECRET_ACCESS_KEY) |
region | string | Region, Standard "auto" (S3_REGION) |
publicUrl | string | Optionale CDN-URL (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",
})
Die Auflösung von Umgebungsvariablen zur Laufzeit ist eine reine Node-Funktion. Auf Cloudflare Workers werden Secrets und Variablen über den env-Parameter des Fetch-Handlers bereitgestellt, nicht über process.env, sodass S3_*-Umgebungsvariablen nicht erfasst werden. Workers-Deployments sollten den r2(config)-Adapter verwenden oder explizite Werte an s3({...}) übergeben. Siehe Speicheroptionen für Details.
Live-Sammlungen
Konfigurieren Sie den EmDash-Loader in src/live.config.ts:
import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";
export const collections = {
_emdash: defineLiveCollection({
loader: emdashLoader(),
}),
};
Loader-Optionen
Die Funktion emdashLoader() akzeptiert optionale Konfiguration:
emdashLoader({
// Derzeit keine Optionen - reserviert für zukünftige Nutzung
});
Umgebungsvariablen
EmDash berücksichtigt die folgenden Umgebungsvariablen:
| Variable | Beschreibung |
|---|---|
EMDASH_SITE_URL | Öffentlicher browserseitiger Ursprung (Fallback auf SITE_URL) |
EMDASH_DATABASE_URL | Datenbank-URL überschreiben |
EMDASH_AUTH_SECRET | Geheimnis für die Passkey-Authentifizierung |
EMDASH_PREVIEW_SECRET | Geheimnis für die Vorschau-Token-Generierung |
EMDASH_URL | Remote-EmDash-URL für die Schemasynchronisierung |
Generieren Sie ein Authentifizierungsgeheimnis mit:
npx emdash auth secret
package.json-Konfiguration
Optionale Konfiguration 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"
}
}
| Option | Beschreibung |
|---|---|
label | Vorlagenname für die Anzeige |
description | Vorlagenbeschreibung |
seed | Pfad zur Seed-JSON-Datei |
url | Remote-URL für die Schemasynchronisierung |
preview | Demo-Site-URL für die Vorlagenvorschau |
TypeScript-Konfiguration
EmDash generiert Typen in .emdash/types.ts. Fügen Sie Folgendes zu Ihrer tsconfig.json hinzu:
{
"compilerOptions": {
"paths": {
"@emdash-cms/types": ["./.emdash/types.ts"]
}
}
}
Typen generieren mit:
npx emdash types