Zum Inhalt springen

Plugin-System im Überblick

Das Plugin-System von EmDash ermöglicht es Ihnen, das CMS zu erweitern, ohne den Kerncode zu verändern. Plugins können in Inhaltslebenszyklus-Ereignisse eingreifen, eigene Daten speichern, Einstellungen für Administratoren bereitstellen und benutzerdefinierte UI zum Admin-Oberfläche hinzufügen.

EmDash-Plugins sind Konfigurations-Transformer, keine separaten Anwendungen. Sie laufen im selben Prozess wie Ihre Astro-Site und interagieren über klar definierte Schnittstellen.

Wesentliche Prinzipien:

  • Deklarativ — Hooks, Speicher und Routen werden zur Definitionszeit deklariert, nicht dynamisch registriert
  • Typsicher — Volle TypeScript-Unterstützung mit typisierten Kontextobjekten
  • Sandboxing-fähig — APIs sind für isolierte Ausführung auf Cloudflare Workers ausgelegt
  • Fähigkeitsbasiert — Plugins deklarieren, was sie benötigen; die Laufzeitumgebung erzwingt den Zugriff

In Ereignisse einhaken

Code vor oder nach dem Speichern von Inhalten, dem Hochladen von Medien und Plugin-Lebenszyklus-Ereignissen ausführen.

Daten speichern

Plugin-spezifische Daten in indizierten Sammlungen persistieren, ohne Datenbank-Migrationen schreiben zu müssen.

Einstellungen bereitstellen

Ein Einstellungsschema deklarieren und eine automatisch generierte Admin-Oberfläche für die Konfiguration erhalten.

Admin-Seiten hinzufügen

Benutzerdefinierte Admin-Seiten und Widgets für die Übersicht mit React-Komponenten erstellen.

API-Routen erstellen

Endpunkte für die Admin-UI Ihres Plugins oder externe Integrationen bereitstellen.

HTTP-Anfragen stellen

Externe APIs mit deklarierten Host-Einschränkungen für Sicherheit aufrufen.

Jedes Plugin wird mit definePlugin() erstellt:

import { definePlugin } from "emdash";
export default definePlugin({
id: "my-plugin",
version: "1.0.0",
// Welche APIs das Plugin benötigt
capabilities: ["read:content", "network:fetch"],
// Hosts, zu denen das Plugin HTTP-Anfragen stellen kann
allowedHosts: ["api.example.com"],
// Persistente Speichersammlungen
storage: {
entries: {
indexes: ["userId", "createdAt"],
},
},
// Ereignishandler
hooks: {
"content:afterSave": async (event, ctx) => {
ctx.log.info("Inhalt gespeichert", { id: event.content.id });
},
},
// REST API-Endpunkte
routes: {
status: {
handler: async (ctx) => ({ ok: true }),
},
},
// Admin-UI-Konfiguration
admin: {
settingsSchema: {
apiKey: { type: "secret", label: "API-Schlüssel" },
},
pages: [{ path: "/dashboard", label: "Übersicht" }],
widgets: [{ id: "status", size: "half" }],
},
});

Jeder Hook und jeder Routen-Handler erhält ein PluginContext-Objekt mit Zugriff auf:

EigenschaftBeschreibungVerfügbarkeit
ctx.storageDokumentensammlungen des PluginsImmer (falls deklariert)
ctx.kvSchlüssel-Wert-Speicher für Einstellungen und StatusImmer
ctx.contentSite-Inhalte lesen/schreibenMit read:content oder write:content
ctx.mediaMedien-Dateien lesen/schreibenMit read:media oder write:media
ctx.httpHTTP-Client für externe AnfragenMit network:fetch
ctx.logStrukturierter Logger (debug, info, warn, error)Immer
ctx.pluginPlugin-Metadaten (id, version)Immer
ctx.siteSite-Info: name, url, localeImmer
ctx.url()Absolute URLs aus Pfaden generierenImmer
ctx.usersBenutzerinfo lesen: get(), getByEmail(), list()Mit read:users
ctx.cronAufgaben planen: schedule(), cancel(), list()Immer
ctx.emailE-Mail senden: send()Mit email:send + Provider konfiguriert

Die Kontextstruktur ist über alle Hooks und Routen hinweg identisch. Eigenschaften, die von Fähigkeiten abhängen, sind nur vorhanden, wenn das Plugin die erforderliche Fähigkeit deklariert.

Fähigkeiten bestimmen, welche APIs im Plugin-Kontext verfügbar sind:

FähigkeitGewährt Zugriff auf
read:contentctx.content.get(), ctx.content.list()
write:contentctx.content.create(), ctx.content.update(), ctx.content.delete()
read:mediactx.media.get(), ctx.media.list()
write:mediactx.media.getUploadUrl(), ctx.media.upload(), ctx.media.delete()
network:fetchctx.http.fetch() (eingeschränkt auf allowedHosts)
network:fetch:anyctx.http.fetch() (uneingeschränkt — für benutzerkonfigurierte URLs)
read:usersctx.users.get(), ctx.users.getByEmail(), ctx.users.list()
email:sendctx.email.send() (erfordert ein Provider-Plugin)
email:provideemail:deliver exklusiven Hook registrieren (Transport-Provider)
email:interceptemail:beforeSend / email:afterSend Hooks registrieren
page:injectpage:metadata / page:fragments Hooks registrieren

Registrieren Sie Plugins in Ihrer Astro-Konfiguration:

astro.config.mjs
import { defineConfig } from "astro/config";
import { emdash } from "emdash/astro";
import seoPlugin from "@emdash-cms/plugin-seo";
import auditLogPlugin from "@emdash-cms/plugin-audit-log";
export default defineConfig({
integrations: [
emdash({
plugins: [seoPlugin({ generateSitemap: true }), auditLogPlugin({ retentionDays: 90 })],
}),
],
});

Plugins werden zur Build-Zeit aufgelöst. Die Reihenfolge ist für Hooks mit derselben Priorität entscheidend – frühere Plugins im Array werden zuerst ausgeführt.

EmDash unterstützt zwei Plugin-Ausführungsmodi:

ModusBeschreibungPlattform
VertrauenswürdigPlugins laufen im Prozess mit vollem ZugriffBeliebig
SandboxedPlugins laufen in isolierten V8-WorkernNur Cloudflare

Im vertrauenswürdigen Modus, der standardmäßig aktiv ist, dienen Capabilities nur als Dokumentation. Im Sandboxed-Modus werden sie auf Runtime-Ebene durchgesetzt.