外掛系統概述

本頁內容

EmDash 的外掛系統讓你在不修改核心程式碼的情況下擴充 CMS。外掛可以鉤入內容生命週期事件、儲存自己的資料、向管理員暴露設定,以及向後台面板新增自訂 UI。

設計理念

EmDash 外掛有兩種類型:沙盒化原生。沙盒化外掛在隔離的 V8 worker 中執行,可以從市場一鍵安裝。原生外掛在處理程序中執行,並在程式碼中設定。

優先使用沙盒化外掛。 它們可以從後台 UI 安裝、更新和刪除,而無需觸及程式碼或重新部署。僅在需要需要建置時整合的功能(React 後台頁面、Portable Text 渲染元件或頁面片段注入)時使用原生外掛。

關鍵原則:

  • 沙盒優先 — 為沙盒設計;僅在需要時使用原生模式
  • 宣告式 — 鉤子、儲存和路由在定義時宣告,而不是動態註冊
  • 型別安全 — 完全支援 TypeScript,具有型別化的上下文物件
  • 基於能力 — 外掛宣告它們需要什麼;沙盒強制執行它

外掛可以做什麼

鉤入事件

在內容儲存、媒體上傳和外掛生命週期事件之前或之後執行程式碼。

儲存資料

在索引集合中持久化外掛特定的資料,無需編寫資料庫遷移。

暴露設定

宣告設定 schema 並獲得自動產生的後台 UI 以進行組態。

新增後台頁面

使用 React 元件建立自訂後台頁面和儀表板小工具。

建立 API 路由

為你的外掛的後台 UI 或外部整合暴露端點。

發出 HTTP 請求

使用宣告的主機限制呼叫外部 API 以確保安全。

外掛架構

每個外掛都使用 definePlugin() 建立:

import { definePlugin } from "emdash";

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

	// 外掛需要存取的 API
	capabilities: ["read:content", "network:fetch"],

	// 外掛可以向其發出 HTTP 請求的主機
	allowedHosts: ["api.example.com"],

	// 持久儲存集合
	storage: {
		entries: {
			indexes: ["userId", "createdAt"],
		},
	},

	// 事件處理器
	hooks: {
		"content:afterSave": async (event, ctx) => {
			ctx.log.info("Content saved", { id: event.content.id });
		},
	},

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

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

外掛上下文

每個鉤子和路由處理器都接收一個 PluginContext 物件,可以存取:

屬性描述可用性
ctx.storage外掛的文件集合始終(如果宣告)
ctx.kv用於設定和狀態的鍵值儲存始終
ctx.content讀/寫網站內容使用 read:contentwrite:content
ctx.media讀/寫媒體檔案使用 read:mediawrite:media
ctx.http用於外部請求的 HTTP 用戶端使用 network:fetch
ctx.log結構化日誌記錄器(debug、info、warn、error)始終
ctx.plugin外掛中繼資料(id、version)始終
ctx.site網站資訊:nameurllocale始終
ctx.url()從路徑產生絕對 URL始終
ctx.users讀取使用者資訊:get()getByEmail()list()使用 read:users
ctx.cron排程任務:schedule()cancel()list()始終
ctx.email傳送電子郵件:send()使用 email:send + 組態提供程式

上下文形狀在所有鉤子和路由中都是相同的。受能力限制的屬性僅在外掛宣告所需能力時才存在。

能力

能力決定了外掛上下文中可用的 API:

能力授予存取權限
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()(限制為 allowedHosts
network:fetch:anyctx.http.fetch()(無限制 — 用於使用者設定的 URL)
read:usersctx.users.get()ctx.users.getByEmail()ctx.users.list()
email:sendctx.email.send()(需要提供程式外掛)
email:provide註冊 email:deliver 獨佔鉤子(傳輸提供程式)
email:intercept註冊 email:beforeSend / email:afterSend 鉤子
page:inject註冊 page:metadata / page:fragments 鉤子

註冊

在你的 Astro 組態中註冊外掛:

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

外掛在建置時解析。對於具有相同優先順序的鉤子,順序很重要 — 陣列中較早的外掛先執行。

執行模式

EmDash 支援兩種外掛執行模式:

模式描述平台
沙盒化具有強制限制的隔離 V8 worker僅 Cloudflare
原生處理程序中,具有完全存取權限任何

在沙盒模式下,能力在執行時層級強制執行 — 外掛只能存取它們宣告的內容。在原生模式下,能力是建議性的,外掛具有完全的處理程序存取權限。

下一步