Plugin-System Übersicht

Auf dieser Seite

Das Plugin-System von EmDash ermöglicht es Ihnen, das CMS zu erweitern, ohne den Kerncode zu ändern. Plugins können sich in Content-Lifecycle-Events einhaken, eigene Daten speichern, Einstellungen für Administratoren bereitstellen und benutzerdefinierte UI zum Admin-Panel hinzufügen.

Designphilosophie

EmDash-Plugins gibt es in zwei Varianten: sandboxed und nativ. Sandboxed Plugins laufen in isolierten V8-Workern und können mit einem Klick vom Marketplace installiert werden. Native Plugins laufen im Prozess und werden im Code konfiguriert.

Bevorzugen Sie sandboxed Plugins. Sie können über die Admin-UI installiert, aktualisiert und entfernt werden, ohne Code anzufassen oder neu zu deployen. Verwenden Sie native Plugins nur, wenn Sie Funktionen benötigen, die Build-Zeit-Integration erfordern (React-Admin-Seiten, Portable Text-Rendering-Komponenten oder Page-Fragment-Injection).

Kernprinzipien:

  • Sandbox-First — Entwerfen Sie für die Sandbox; verwenden Sie den nativen Modus nur bei Bedarf
  • Deklarativ — Hooks, Speicher und Routen werden zur Definitionszeit deklariert, nicht dynamisch registriert
  • Typsicher — Vollständige TypeScript-Unterstützung mit typisierten Kontextobjekten
  • Capability-basiert — Plugins deklarieren, was sie benötigen; die Sandbox setzt es durch

Was Plugins tun können

In Events einhaken

Code vor oder nach Content-Speicherungen, Media-Uploads und Plugin-Lifecycle-Events ausführen.

Daten speichern

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

Einstellungen bereitstellen

Ein Einstellungsschema deklarieren und eine automatisch generierte Admin-UI zur Konfiguration erhalten.

Admin-Seiten hinzufügen

Benutzerdefinierte Admin-Seiten und Dashboard-Widgets mit React-Komponenten erstellen.

API-Routen erstellen

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

HTTP-Anfragen stellen

Externe APIs mit deklarierten Host-Beschränkungen zur Sicherheit aufrufen.

Plugin-Architektur

Jedes Plugin wird mit definePlugin() erstellt:

import { definePlugin } from "emdash";

export default definePlugin({
	id: "my-plugin",
	version: "1.0.0",

	// APIs, auf die das Plugin zugreifen muss
	capabilities: ["read:content", "network:fetch"],

	// Hosts, an die das Plugin HTTP-Anfragen stellen kann
	allowedHosts: ["api.example.com"],

	// Persistente Speichersammlungen
	storage: {
		entries: {
			indexes: ["userId", "createdAt"],
		},
	},

	// Event-Handler
	hooks: {
		"content:afterSave": async (event, ctx) => {
			ctx.log.info("Content saved", { id: event.content.id });
		},
	},

	// REST-API-Endpoints
	routes: {
		status: {
			handler: async (ctx) => ({ ok: true }),
		},
	},

	// Admin-UI-Konfiguration
	admin: {
		settingsSchema: {
			apiKey: { type: "secret", label: "API Key" },
		},
		pages: [{ path: "/dashboard", label: "Dashboard" }],
		widgets: [{ id: "status", size: "half" }],
	},
});

Plugin-Kontext

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

EigenschaftBeschreibungVerfügbarkeit
ctx.storageDokumentsammlungen des PluginsImmer (wenn deklariert)
ctx.kvKey-Value-Store für Einstellungen und ZustandImmer
ctx.contentSite-Content lesen/schreibenMit read:content oder write:content
ctx.mediaMediendateien 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 Kontextform ist bei allen Hooks und Routen identisch. Capability-geschützte Eigenschaften sind nur vorhanden, wenn das Plugin die erforderliche Capability deklariert.

Capabilities

Capabilities bestimmen, welche APIs im Plugin-Kontext verfügbar sind:

CapabilityGewä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() (beschränkt auf allowedHosts)
network:fetch:anyctx.http.fetch() (unbeschränkt — für benutzerkonfigurierte URLs)
read:usersctx.users.get(), ctx.users.getByEmail(), ctx.users.list()
email:sendctx.email.send() (erfordert Provider-Plugin)
email:provideemail:deliver exklusiven Hook registrieren (Transport-Provider)
email:interceptemail:beforeSend / email:afterSend Hooks registrieren
page:injectpage:metadata / page:fragments Hooks registrieren

Registrierung

Registrieren Sie Plugins in Ihrer Astro-Konfiguration:

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 wichtig für Hooks mit gleicher Priorität — frühere Plugins im Array laufen zuerst.

Ausführungsmodi

EmDash unterstützt zwei Plugin-Ausführungsmodi:

ModusBeschreibungPlattform
SandboxedIsolierte V8-Worker mit erzwungenen LimitsNur Cloudflare
NativIm Prozess mit vollem ZugriffAlle

Im Sandbox-Modus werden Capabilities auf Runtime-Ebene durchgesetzt — Plugins können nur auf das zugreifen, was sie deklarieren. Im nativen Modus sind Capabilities beratend und Plugins haben vollen Prozesszugriff.

Nächste Schritte