EmDash wird über zwei Dateien konfiguriert: astro.config.mjs für die Integration und src/live.config.ts für Content-Sammlungen.
Astro-Integration
Konfiguriere EmDash als Astro-Integration 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: [],
}),
],
});
Integrationsoptionen
database
Erforderlich. Datenbank-Adapter-Konfiguration. Wähle einen Adapter:
// 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 von @emdash-cms/cloudflare)
database: d1({ binding: "DB" });
Siehe Datenbankoptionen für Details.
storage
Erforderlich. Medienspeicher-Adapter-Konfiguration. Wähle einen Adapter:
// Lokales Dateisystem (Entwicklung)
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
});
// R2-Binding (Cloudflare Workers)
storage: r2({
binding: "MEDIA",
publicUrl: "https://pub-xxxx.r2.dev", // optional
});
// S3-kompatibel (jede Plattform) — alle Felder aus S3_*-Umgebungsvariablen
storage: s3()
// Oder mit expliziten Werten
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, Standard: "auto"
publicUrl: "https://cdn.example.com", // optional
});
Siehe Speicheroptionen für Details.
objectCache
Optional. Cached Content- und Konfigurations-Abfrageergebnisse in einem Key/Value-Store, sodass Lesevorgänge ohne Datenbankabfrage bei jeder Anfrage bedient werden. Deaktiviert wenn weggelassen. Wähle einen Adapter:
// Cloudflare KV (geteilt über alle Isolates)
import { kvCache } from "@emdash-cms/cloudflare";
objectCache: kvCache({ binding: "CACHE" });
// In-Memory (Node.js / Entwicklung)
import { memoryCache } from "emdash/astro";
objectCache: memoryCache();
Siehe Object Cache für Einrichtung und Optionen.
plugins
Optional. Array von EmDash-Plugins. Das folgende Beispiel registriert ein Plugin:
import seoPlugin from "@emdash-cms/plugin-seo";
plugins: [seoPlugin()];
fonts
Optional. Schriftart-Konfiguration der Admin-Oberfläche.
Standardmäßig lädt EmDash Noto Sans über die Astro Font API. Schriftarten werden zur Build-Zeit von Google heruntergeladen und selbst gehostet, sodass keine Runtime-CDN-Anfragen entstehen. Die Basisschrift deckt lateinische, kyrillische, griechische, Devanagari- und vietnamesische Schriften ab.
Um Unterstützung für zusätzliche Schriftsysteme hinzuzufügen, übergib Schriftnamen. Das folgende Beispiel fügt Arabisch und Japanisch hinzu:
emdash({
fonts: {
scripts: ["arabic", "japanese"],
},
})
Die verfügbaren Schriften sind 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 und tibetan.
Jedes Skript wird der entsprechenden Noto-Sans-Variante auf Google Fonts zugeordnet (z.B. "arabic" lädt Noto Sans Arabic). Alle Font-Faces teilen einen einzigen font-family-Namen und verwenden unicode-range, sodass der Browser nur die Dateien herunterlädt, die er für die Zeichen auf der Seite benötigt.
Auf false setzen, um die Schrifteinspeisung vollständig zu deaktivieren und Systemschriften zu verwenden:
emdash({
fonts: false,
})
Das Admin-CSS verwendet die CSS-Variable --font-emdash. Diese wird automatisch durch die obige Schriftkonfiguration gesetzt.
auth
Optional. Ein Authentifizierungs-Adapter. EmDash’s eingebautes Login verwendet Passkeys; das Setzen von auth ersetzt sie durch einen externen Anbieter. Der Cloudflare-Access-Adapter access() wird von @emdash-cms/cloudflare bereitgestellt:
import { access } from "@emdash-cms/cloudflare";
emdash({
auth: access({
teamDomain: "myteam.cloudflareaccess.com",
audience: "your-app-audience-tag",
roleMapping: {
Admins: 50,
Editors: 40,
},
}),
});
Optionen für access():
| Option | Typ | Standard | Beschreibung |
|---|---|---|---|
teamDomain | string | erforderlich | Deine Cloudflare Access Team-Domain |
audience | string | — | Application Audience (AUD) Tag. Auf Workers bevorzuge audienceEnvVar. |
audienceEnvVar | string | "CF_ACCESS_AUDIENCE" | Umgebungsvariable, aus der der Audience-Tag zur Laufzeit gelesen wird |
autoProvision | boolean | true | EmDash-Benutzer beim ersten Login erstellen |
defaultRole | number | 30 | Rollenstufe für Benutzer, die nicht durch roleMapping zugeordnet werden (siehe Benutzerrollen) |
syncRoles | boolean | false | roleMapping bei jedem Login erneut anwenden statt nur bei der Bereitstellung |
roleMapping | object | — | IdP-Gruppennamen auf EmDash-Rollenstufen abbilden; erster Treffer gewinnt |
authProviders
Optional. Ein Array von einsteckbaren Login-Providern (auf oberster Ebene, neben auth). Jeder Eintrag ist das Ergebnis des Aufrufs einer Provider-Factory, wie unten gezeigt:
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()],
});
Eingebaute Provider:
github()— liestEMDASH_OAUTH_GITHUB_CLIENT_ID/EMDASH_OAUTH_GITHUB_CLIENT_SECRET(oder unpräfixierte Fallbacks).google()— liestEMDASH_OAUTH_GOOGLE_CLIENT_ID/EMDASH_OAUTH_GOOGLE_CLIENT_SECRET.atproto()— Atmosphere-Konto-Login (Bluesky und das weitergehende AT-Protokoll-Netzwerk). Keine Umgebungsvariablen erforderlich. Akzeptiert{ allowedDIDs, allowedHandles, defaultRole }. Siehe den Atmosphere-Login-Guide.
Drittanbieter-Pakete können ihre eigenen Provider mit der gleichen AuthProviderDescriptor-Form registrieren — siehe Login-Provider.
siteUrl
Optional. Der öffentliche, browserseitige Origin für die Seite (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. Setze siteUrl, um all das auf einmal zu beheben.
Die Integration validiert diesen Wert beim Laden: Er muss eine gültige URL mit http:- oder https:-Protokoll sein und wird auf Origin normalisiert (Pfad wird entfernt).
Das folgende Beispiel setzt den öffentlichen Origin:
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.
Auf Cloudflare Workers liest der Env-Var-Fallback process.env, was leer ist, es sei denn, das Kompatibilitäts-Flag nodejs_compat_populate_process_env ist aktiviert. Um dort die Env-Var anstelle der Konfigurationsoption zu verwenden, setze beides:
// wrangler.jsonc
{
"compatibility_flags": ["nodejs_compat", "nodejs_compat_populate_process_env"],
"vars": { "EMDASH_SITE_URL": "https://cms.example.com" },
}
Multi-Origin Passkey-Verifizierung
siteUrl definiert einen einzelnen kanonischen Origin. Wenn dasselbe EmDash-Deployment unter mehreren Hostnamen erreichbar ist, die eine gemeinsame registrierbare Eltern-Domain teilen (z.B. https://example.com und https://preview.example.com), lehnt die Passkey-Verifizierung Assertions ab, deren Origin nicht exakt mit siteUrl übereinstimmt — obwohl WebAuthn erlaubt, dass Passkeys über Subdomains unter derselben rpId gültig sind.
Deklariere zusätzlich akzeptierte Origins entweder über allowedOrigins in astro.config.mjs oder die EMDASH_ALLOWED_ORIGINS Umgebungsvariable. Der kanonische siteUrl bleibt die Quelle der rpId; hier aufgeführte Einträge werden bei der Verifizierung akzeptiert. Die beiden Quellen werden zur Laufzeit zusammengeführt, sodass die Konfiguration die stabilen Origins deklarieren kann (versioniert, code-reviewed), während Umgebungsvariablen umgebungsspezifische Extras hinzufügen (z.B. ephemere PR-Vorschauen).
Das folgende Beispiel deklariert einen zusätzlichen Origin in der Konfiguration:
emdash({
siteUrl: "https://example.com",
allowedOrigins: ["https://preview.example.com"],
})
Die äquivalenten Werte können auch aus Umgebungsvariablen stammen:
EMDASH_SITE_URL=https://example.com
EMDASH_ALLOWED_ORIGINS=https://preview.example.com,https://staging.example.com
Validierung
EmDash validiert diese, um tote Konfigurationen zu verhindern, die der Browser nie respektieren würde:
- Jeder Eintrag muss eine parsbare
http:- oderhttps:-URL sein, ohne abschließenden Punkt und ohne leere Labels im Hostnamen. - Wenn
allowedOriginsnicht leer ist, musssiteUrlgesetzt sein (aus jeder Quelle) und darf kein IP-Literal oder ein Hostname mit abschließendem Punkt sein. - Jeder Origin muss derselbe Hostname wie
siteUrloder eine Subdomain davon sein. (WebAuthn erfordert, dassrpIdein registrierbares Suffix jedes Origins ist.)
Wenn die Validierung fehlschlägt, siehst du einen quellenattribuierten Fehler wie 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.
Wo der Fehler auftritt, hängt davon ab, wo die Werte deklariert sind:
- Beim Astro-Start, wenn sowohl
config.allowedOriginsals auchconfig.siteUrlausastro.config.mjsstammen — Tippfehler im Code lassen den Build fehlschlagen. - Bei der ersten Passkey-Verifizierung, wenn einer der Werte aus
EMDASH_ALLOWED_ORIGINSoderEMDASH_SITE_URLstammt — Env-Fehlanpassungen zeigen sich als 500er beim ersten Verifizierungsversuch.
Reverse-Proxy-Einrichtung
Astro reflektiert X-Forwarded-* nur, wenn der öffentliche Host erlaubt ist. Konfiguriere security.allowedDomains für den Hostnamen (und Schemas), den deine Benutzer verwenden. In astro dev füge passendes vite.server.allowedHosts hinzu, damit Vite den Proxy-Host-Header akzeptiert.
Bevorzuge die Korrektur von allowedDomains (und weitergeleiteten Headern) zuerst; verwende siteUrl wenn die rekonstruierte URL weiterhin vom Browser-Origin abweicht (typisch wenn TLS davor terminiert wird und die Upstream-Anfrage http:// bleibt).
Mit TLS davor reicht es oft, den Dev-Server an Loopback zu binden (astro dev --host 127.0.0.1): Der Proxy verbindet sich lokal, während siteUrl dem öffentlichen HTTPS-Origin entspricht.
Wenn dein Proxy einen Client-IP-Header schreibt, setze trustedProxyHeaders, damit EmDash’s Rate-Limits die echte Client-IP verwenden können anstatt jede Anfrage unter einem gemeinsamen “unknown”-Schlüssel einzuordnen.
Die folgende Konfiguration setzt allowedDomains, vite.server.allowedHosts und siteUrl zusammen für ein Reverse-Proxy-Deployment:
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
Optional. Header, denen für die Client-IP-Auflösung vertraut wird, wenn hinter einem von dir kontrollierten Reverse Proxy. Wird von Auth-Rate-Limits (Magic-Link, Registrierung, Passkey, OAuth Device Flow) und dem öffentlichen Kommentar-Endpunkt verwendet.
Auf Cloudflare wird das an die Anfrage angehängte cf-Objekt automatisch verwendet — du musst dies normalerweise nicht setzen. Bei selbst gehosteten Deployments hinter nginx, Caddy, Traefik, Fly, Railway oder ähnlichen, setze dies auf den Header, den dein Proxy schreibt, damit Rate-Limits nach echter Client-IP gruppieren können anstatt jede Anfrage als “unknown” zu behandeln.
Das folgende Beispiel vertraut dem x-real-ip-Header, der von nginx, Caddy oder Traefik gesetzt wird:
emdash({
database: sqlite({ url: "file:./data.db" }),
trustedProxyHeaders: ["x-real-ip"],
});
Header werden der Reihe nach versucht. Werte, die auf *-forwarded-for passen, werden als kommagetrennte Listen geparst und der erste Eintrag wird verwendet. Das folgende Beispiel bevorzugt Fly.ios Header und fällt auf x-forwarded-for zurück:
emdash({
trustedProxyHeaders: ["fly-client-ip", "x-forwarded-for"],
});
Wenn nicht in der Konfiguration gesetzt, liest EmDash die Umgebungsvariable EMDASH_TRUSTED_PROXY_HEADERS (kommagetrennt). Ein explizites leeres Array in der Konfiguration überschreibt die Umgebungsvariable.
maxUploadSize
Optional. Maximal erlaubte Mediendatei-Upload-Größe in Bytes. Gilt sowohl für direkte Multipart-Uploads als auch für Signed-URL-Uploads. Standard ist 52_428_800 (50 MB). Das folgende Beispiel erhöht das Limit auf 100 MB:
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
maxUploadSize: 100 * 1024 * 1024, // 100 MB
});
| Wert | Beschreibung |
|---|---|
number (Bytes) | Muss eine positive endliche Ganzzahl sein |
| weggelassen | Standard 50 MB |
Uploads, die das konfigurierte Limit überschreiten, werden mit einer 413 Payload Too Large-Antwort auf dem direkten Upload-Pfad oder einem 400 Validation Error auf dem Signed-URL-Pfad abgelehnt.
toolbar
Optional. Steuert, wie die Editor-Toolbar (die schwebende Pille auf öffentlichen Seiten) ausgeliefert wird. Standard ist "server".
| Wert | Verhalten |
|---|---|
"server" (Standard) | Die Toolbar wird serverseitig in jede HTML-Antwort injiziert, die für einen authentifizierten Editor gerendert wird. |
"client" | Öffentliches HTML ist identisch für jeden Besucher. Ein kleines Bootstrap-Skript zeigt eine “Edit”-Pille in Browsern, die sich im Admin angemeldet haben; ein Klick verifiziert die Sitzung und lädt die Seite mit einem _edit-Query-Parameter neu, der immer frisch gerendert wird (nie gecacht) mit der vollständigen Toolbar. |
false | Toolbar oder Bootstrap-Skript niemals rendern. |
emdash({
toolbar: "client",
})
Verwende "client", wenn dein öffentliches HTML über einen gemeinsamen Cache bereitgestellt wird (Cloudflare Cache Everything / Workers Cache, Fastly, Varnish, …). Mit serverseitiger Injection erhält ein Editor, der die öffentliche Seite durchsucht, die gecachte anonyme Variante — ohne die Toolbar — wann immer ein anonymer Besucher den Cache zuerst befüllt hat, sodass die Toolbar mit dem Cache-Zustand erscheint und verschwindet. Im Client-Modus wird nichts Sitzungsspezifisches in teilbares HTML injiziert, sodass der Cache vollständig effektiv bleibt und die Toolbar zuverlässig ist.
Hinweise zum "client"-Modus:
- Abgemeldete Besucher, die eine geteilte
?_edit-URL öffnen, werden zur kanonischen URL weitergeleitet, sodass der Parameter keine Entwürfe verraten oder zusätzliche Cache-Einträge mit Seiteninhalt anlegen kann. - Das “angemeldet”-Signal ist ein nicht-geheimes
localStorage-Flag, das vom Admin gesetzt wird; die Pille verifiziert die echte Sitzung, bevor sie die Bearbeitungsansicht betritt. - Das Bootstrap ist ein kleines Inline-
<script>. Wenn deine Seite eine strikteContent-Security-Policyohne'unsafe-inline'sendet, füge einen Hash dafür hinzu — dasselbe gilt für die serverseitig injizierte Toolbar. - EmDash injiziert nichts Sitzungsspezifisches — aber wenn deine eigenen Templates auf
Astro.locals.userverzweigen (z.B. ein “Admin”-Nav-Link für angemeldete Benutzer), ist diese Varianz weiterhin in deinem HTML und fragmentiert den Cache weiterhin.
In jedem Modus kann die Toolbar im Browser über die ×-Schaltfläche geschlossen werden (pro Browser, bis zum nächsten Mal, wenn ein Editor den Admin öffnet). Vorschau- und Bearbeitungsmodus-Antworten werden immer serverseitig mit Cache-Control: private, no-store gerendert.
experimental
Optional. Opt-in Features, deren Verhalten oder Datenformat sich in einem Minor-Release ändern oder entfernt werden kann. Jedes Feld wird unabhängig aktiviert.
experimental.registry
Optional. Lenkt die Plugin-Browse- und Installations-Flows des Admin-Dashboards auf eine föderierte Plugin-Registry statt auf den zentralen Marktplatz. Erfordert sandboxRunner, da Registry-Plugins sandboxed ausgeführt werden.
Übergib eine einfache Aggregator-URL-Zeichenkette oder ein Objekt, wenn du einen Labeller oder eine Release-Age-Policy benötigst. Das folgende Beispiel verwendet die Objekt-Form:
emdash({
sandboxRunner: "@emdash-cms/sandbox-cloudflare",
experimental: {
registry: {
aggregatorUrl: "https://registry.emdashcms.com",
acceptLabelers: "did:plc:emdashverification",
policy: {
minimumReleaseAge: "48h",
minimumReleaseAgeExclude: ["did:plc:yourfirstpartydid"],
},
},
},
});
| Option | Typ | Beschreibung |
|---|---|---|
aggregatorUrl | string | Aggregator-Origin, wo die XRPC-Endpunkte der Registry gemountet sind. HTTPS in Produktion. |
acceptLabelers | string | Kommagetrennte Labeller-DIDs, die an den Aggregator für Takedown- und Verifizierungs-Labels weitergeleitet werden. |
policy.minimumReleaseAge | string | number | Releases zurückhalten, die neuer als dieses Alter sind. Dauer-String ("48h", "7d") oder Sekunden. |
policy.minimumReleaseAgeExclude | string[] | DIDs (oder <did>/<slug>-Paare), die vom Holdback ausgenommen sind. |
Siehe Die Plugin-Registry für den vollständigen Workflow, das Vertrauensmodell und wie du die Registry von deiner eigenen Seite abfragen kannst.
Datenbank-Adapter
Importiere die Adapter aus emdash/db:
import { sqlite, libsql, postgres } from "emdash/db";
sqlite(config)
SQLite-Datenbank mit better-sqlite3. Das folgende Beispiel verbindet sich mit einer lokalen Datei:
| Option | Typ | Beschreibung |
|---|---|---|
url | string | Dateipfad mit file:-Präfix |
sqlite({ url: "file:./data.db" });
libsql(config)
libSQL-Datenbank. Das folgende Beispiel verbindet sich mit einer entfernten libSQL-Datenbank:
| Option | Typ | Beschreibung |
|---|---|---|
url | string | Datenbank-URL |
authToken | string | Auth-Token (optional für lokale Dateien) |
libsql({
url: process.env.LIBSQL_DATABASE_URL,
authToken: process.env.LIBSQL_AUTH_TOKEN,
});
postgres(config)
PostgreSQL-Datenbank mit Connection Pooling.
| Option | Typ | Beschreibung |
|---|---|---|
connectionString | string | PostgreSQL-Verbindungs-URL |
host | string | Datenbank-Host |
port | number | Datenbank-Port |
database | string | Datenbankname |
user | string | Datenbankbenutzer |
password | string | Datenbankpasswort |
ssl | boolean | SSL aktivieren |
pool.min | number | Minimale Pool-Größe (Standard: 0) |
pool.max | number | Maximale Pool-Größe (Standard: 10) |
Das folgende Beispiel verbindet sich mit einem Connection-String:
postgres({ connectionString: process.env.DATABASE_URL });
d1(config)
Cloudflare D1-Datenbank. Importiere aus @emdash-cms/cloudflare.
| Option | Typ | Standard | Beschreibung |
|---|---|---|---|
binding | string | — | D1-Binding-Name aus wrangler.jsonc |
session | string | "disabled" | Lesereplikationsmodus: "disabled", "auto" oder "primary-first" |
bookmarkCookie | string | "__em_d1_bookmark" | Cookie-Name für Session-Lesezeichen |
Das folgende Beispiel zeigt ein einfaches Binding und eines mit aktivierten Leserepliken:
// Einfach
d1({ binding: "DB" });
// Mit Leserepliken
d1({ binding: "DB", session: "auto" });
Wenn session "auto" oder "primary-first" ist, nutzt EmDash die D1 Sessions API, um Leseabfragen an nahegelegene Repliken weiterzuleiten. Authentifizierte Benutzer erhalten Bookmark-basierte Read-your-writes-Konsistenz. Siehe Datenbankoptionen — Leserepliken für Details.
Speicher-Adapter
Importiere local und s3 aus emdash/astro. Der r2-Adapter wird aus @emdash-cms/cloudflare importiert:
import emdash, { local, s3 } from "emdash/astro";
import { r2 } from "@emdash-cms/cloudflare";
local(config)
Lokaler Dateisystem-Speicher. Das folgende Beispiel stellt Uploads aus einem lokalen Verzeichnis bereit:
| Option | Typ | Beschreibung |
|---|---|---|
directory | string | Verzeichnispfad |
baseUrl | string | Basis-URL für die Dateibereitstellung |
local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
});
r2(config)
Cloudflare R2-Binding. Das folgende Beispiel verwendet ein R2-Binding mit einer öffentlichen URL:
| Option | Typ | Beschreibung |
|---|---|---|
binding | string | R2-Binding-Name |
publicUrl | string | Optionale öffentliche URL |
r2({
binding: "MEDIA",
publicUrl: "https://pub-xxxx.r2.dev",
});
s3(config?)
S3-kompatibler Speicher. Alle Konfigurationsfelder sind optional: jedes Feld, das in s3({...}) weggelassen wird, wird aus der entsprechenden S3_*-Umgebungsvariable aufgelöst, wenn der Node-Prozess startet. Explizite Werte haben immer Vorrang.
Voraussetzung: Installiere @aws-sdk/client-s3 und @aws-sdk/s3-request-presigner in deinem Projekt. EmDash Core bündelt 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) |
Die folgenden Beispiele lösen alle Felder aus der Umgebung auf, mischen Konfiguration und Umgebung oder übergeben jedes Feld explizit:
// Alle Felder aus S3_*-Umgebungsvariablen (Node-Container-Deployments)
s3()
// Mix: CDN aus Konfiguration, Rest aus Umgebung
s3({ publicUrl: "https://cdn.example.com" })
// Alle explizit
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 Laufzeitauflösung von Umgebungsvariablen ist eine Node-only-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 entweder den r2(config)-Adapter verwenden oder explizite Werte an s3({...}) übergeben. Siehe Speicheroptionen für Details.
Object-Cache-Adapter
Übergib einen dieser an die objectCache-Option.
kvCache(config)
Cloudflare KV-Backend, geteilt über alle Isolates. Importiere aus @emdash-cms/cloudflare.
kvCache({
binding: "CACHE", // KV-Binding-Name (erforderlich)
defaultTtl: 3600, // Eintrags-TTL in Sekunden (optional, KV-Minimum 60)
revalidate: 1000, // Cross-Isolate Staleness-Fenster in ms (optional)
timeout: 2000, // Per-Op Timeout in ms vor einem Miss (optional, 0 deaktiviert)
keyPrefix: "em", // Cache-Schlüssel-Präfix (optional)
})
memoryCache(config?)
In-Process Backend für Node.js und Entwicklung. Importiere aus emdash/astro.
memoryCache({
defaultTtl: 3600, // Eintrags-TTL in Sekunden (optional)
revalidate: 1000, // Staleness-Fenster in ms (optional)
maxEntries: 1000, // Max gecachte Schlüssel vor Eviction (optional)
keyPrefix: "em", // Cache-Schlüssel-Präfix (optional)
})
Siehe Object Cache für Einrichtung und Verhalten.
Live-Sammlungen
Konfiguriere 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 emdashLoader()-Funktion nimmt keine Argumente:
emdashLoader();
Umgebungsvariablen
EmDash beachtet diese Umgebungsvariablen:
| Variable | Beschreibung |
|---|---|
EMDASH_SITE_URL | Öffentlicher browserseitiger Origin (fällt auf SITE_URL zurück) |
EMDASH_ALLOWED_ORIGINS | Kommagetrennte Liste zusätzlicher Origins, die von der Passkey-Verifizierung akzeptiert werden (Multi-Subdomain-Deployments). |
EMDASH_DATABASE_URL | Datenbank-URL überschreiben |
EMDASH_ENCRYPTION_KEY | Schlüssel zum Verschlüsseln von Plugin-Secrets im Ruhezustand. Vom Betreiber bereitgestellt — wird nie in der Datenbank gespeichert. |
EMDASH_PREVIEW_SECRET | Optionale Überschreibung für das Vorschau-HMAC-Secret. Wenn nicht gesetzt, wird ein stabiler site-spezifischer Wert generiert und in der Datenbank gespeichert. |
EMDASH_IP_SALT | Optionale Überschreibung für den Kommentator-IP-Hash-Salt. Wenn nicht gesetzt, wird ein stabiler site-spezifischer Wert generiert und in der Datenbank gespeichert. |
EMDASH_AUTH_SECRET | Legacy. Wird als IP-Salt-Quelle verwendet, wenn gesetzt; bestehende Installationen sollten dies beibehalten, um stabile Kommentator-IP-Hashes beim Upgrade zu bewahren. |
EMDASH_TURNSTILE_SECRET_KEY | Cloudflare Turnstile Secret Key (fällt auf TURNSTILE_SECRET_KEY zurück). Wenn gesetzt, müssen Kommentar-Einreichungen ein gültiges Turnstile-Token enthalten — kombiniere es mit der turnstileSiteKey-Prop auf <CommentForm>. |
EMDASH_URL | Remote EmDash URL für Schema-Sync |
Generiere einen Verschlüsselungsschlüssel mit dem folgenden Befehl:
npx emdash secrets generate
package.json-Konfiguration
Templates und Sites können optionale Metadaten unter einem emdash-Schlüssel in package.json deklarieren:
{
"emdash": {
"label": "My Blog Template",
"seed": ".emdash/seed.json",
"url": "https://my-site.pages.dev"
}
}
| Option | Beschreibung |
|---|---|
label | Template-Name zur Anzeige |
seed | Pfad zur Seed-JSON-Datei |
url | Remote URL für Schema-Sync |
TypeScript-Konfiguration
EmDash generiert Typen in .emdash/types.ts. Füge einen Pfad-Alias zu deiner tsconfig.json hinzu:
{
"compilerOptions": {
"paths": {
"@emdash-cms/types": ["./.emdash/types.ts"]
}
}
}
Generiere Typen mit dem folgenden Befehl:
npx emdash types