EmDash 的主要設定位於 astro.config.mjs,而 src/live.config.ts 用於註冊內容載入器。與部署相關的值也可以來自環境變數。package.json 中的一小段中繼資料區塊支援範本標籤和舊版本機 CLI 流程。
Astro 整合
在 astro.config.mjs 中將 EmDash 設定為 Astro 整合:
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: [],
}),
],
});
整合選項
database
必填。 資料庫配接器設定。選擇一個配接器:
// 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 from @emdash-cms/cloudflare)
database: d1({ binding: "DB" });
詳情請參閱資料庫選項。
migrations
選填。 控制 EmDash 內部資料庫遷移在執行階段的處理方式。省略此選項時,預設值為 { runtime: "auto" }。
migrations: {
runtime: "check", // "auto" | "check" | "manual"
dev: "auto", // optional development override
}
auto 會檢查並套用待處理的遷移;check 在執行中的建置已知存在待處理遷移時回傳 503(EmDash 安裝的影像端點的請求除外);manual 在執行階段不執行任何遷移查詢。EMDASH_MIGRATIONS_MODE 會覆寫實際生效的執行階段模式。在採用 check 或 manual 之前,請先參閱管理核心資料庫遷移。
storage
選填。 媒體儲存配接器設定。省略此選項時,EmDash 會將檔案儲存在 ./.emdash/uploads 中,並透過 /_emdash/api/media/file 提供存取。當預設的本機目錄不適用時,請選擇一個配接器:
// Local filesystem (development)
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-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
});
詳情請參閱儲存選項。
images
選填。 控制 EmDash 是否將已儲存的媒體與 Astro 的影像最佳化整合。預設值為 true。
啟用後,EmDash 會包裝 Astro 的影像端點,讓 <Image> 和 getImage() 可以直接從已設定的儲存配接器讀取來源位元組。當原始媒體 URL 位於 Cloudflare Access 之後時,這同樣有效。對被包裝端點的請求會略過 EmDash 的執行階段啟動,因此在這些請求中 locals.emdash 只包含 storage,已登入的使用者還會包含 db。locals.user 的設定方式與其他公開路由相同。當由其他影像服務處理媒體,或需要在不經過 EmDash 端點包裝器的情況下轉譯每張影像時,請設定 images: false。
emdash({
images: false,
});
mediaProviders
選填。 向媒體庫新增媒體服務。以儲存為基礎的本機提供者會自動保持可用;此陣列中的每個描述元都會為編輯者增加一個可瀏覽或上傳媒體的位置。
下面的範例新增 Cloudflare Images 和 Cloudflare Stream:
import { cloudflareImages, cloudflareStream } from "@emdash-cms/cloudflare";
emdash({
mediaProviders: [cloudflareImages({}), cloudflareStream({})],
});
提供者的憑證在執行階段解析。上面的空設定使用 cloudflareImages(config) 和 cloudflareStream(config) 配接器章節中描述的預設 Cloudflare 環境變數。有關繫結和轉譯設定,請參閱媒體庫:媒體提供者。
objectCache
選填。 將內容和設定的查詢結果快取到鍵/值儲存中,讓讀取無需在每次請求時查詢資料庫。省略時停用。選擇一個配接器:
// Cloudflare KV (shared across all isolates)
import { kvCache } from "@emdash-cms/cloudflare";
objectCache: kvCache({ binding: "CACHE" });
// In-memory (Node.js / development)
import { memoryCache } from "emdash/astro";
objectCache: memoryCache();
設定與選項請參閱物件快取。
middleware.outer
選填。 在整個 EmDash 中介軟體堆疊之外註冊一個 Astro 中介軟體模組。由於整合使用 Astro 的 order: "pre" 註冊它,它也會先於 src/middleware.ts 中定義的中介軟體執行。可用於需要在快取命中時避免執行階段和資料庫初始化的請求攔截或完整回應快取,也可用於依賴 EmDash 最終 HTML 的回應標頭。
emdash({
middleware: {
outer: "./src/outer-middleware.ts",
},
});
執行順序如下:
- 外層中介軟體執行到
await next()。 - EmDash 初始化其執行階段和資料庫,然後執行設定、身分驗證和請求內容中介軟體。
- Astro 路由轉譯。
- EmDash 套用對回應的修改,包括視覺化編輯 HTML 以及安全/計時標頭。
next()帶著該最終回應解析回外層中介軟體。
在呼叫 next() 之前,中介軟體擁有一般的 Astro 請求和平台執行內容,但 locals.emdash、locals.user、資料庫以及請求範圍內的 EmDash 狀態均無法使用。提前回傳的 Response 會完全略過 EmDash,因此必須自行包含所需的安全和快取標頭。next() 解析之後,可以安全地最終確定 CSP nonce、快取完整本文或設定 Content-Length。如果中介軟體修改了本文,請移除或重新計算任何既有的 Content-Length 標頭。
該掛鉤在 Node 和 Cloudflare 上都使用 Astro 的中介軟體 API。下面這個最小化的 Cloudflare Cache API 範例只快取匿名 HTML 回應,並在 EmDash 初始化之前回傳命中結果:
import { waitUntil } from "cloudflare:workers";
import { defineMiddleware } from "astro:middleware";
export const onRequest = defineMiddleware(async ({ request }, next) => {
if (request.method !== "GET" || request.headers.has("cookie")) {
return next();
}
const cacheKey = new Request(request.url, { method: "GET" });
const cached = await caches.default.match(cacheKey);
if (cached) return cached;
const response = await next();
const isHtml = response.headers.get("content-type")?.includes("text/html");
const isPrivate = response.headers.get("cache-control")?.includes("no-store");
if (response.ok && isHtml && !isPrivate) {
waitUntil(caches.default.put(cacheKey, response.clone()));
}
return response;
});
在 Node 上,請使用相同形態的中介軟體,並搭配 Redis 等相容 Node 的快取。快取鍵和略過規則必須包含會改變轉譯回應的每一個請求屬性。
playground
選填。 啟用一次性、以瀏覽器為基礎的 EmDash 實驗場所使用的中介軟體。它會為每個工作階段建立一個可寫入的 Durable Object 資料庫,套用設定的種子資料,並在一般 EmDash 中介軟體執行之前將訪客以匿名管理員身分登入。
import { playgroundDatabase } from "@emdash-cms/cloudflare";
emdash({
database: playgroundDatabase({ binding: "PLAYGROUND_DB" }),
playground: {
middlewareEntrypoint: "@emdash-cms/cloudflare/db/playground-middleware",
},
});
此模式需要 @emdash-cms/cloudflare 和一個 Durable Object 繫結。它會繞過一般的設定和身分驗證中介軟體,因此只應用於暫時性的展示站台,而不是正式環境的 CMS。
plugins
選填。 與 Astro 站台在同一個處理程序中執行的外掛程式陣列。原生外掛程式應放在這裡。當你信任某個與沙箱相容的外掛程式擁有完整的處理程序存取權限且不需要隔離時,它也可以在這裡執行。
下面的範例註冊一個原生外掛程式:
import seoPlugin from "@emdash-cms/plugin-seo";
plugins: [seoPlugin()];
原生外掛程式可以直接使用伺服器端和框架 API,因此除非該套件同時提供與沙箱相容的外掛程式進入點,否則無法移到 sandboxed 中。有關開發和部署方面的差異,請參閱選擇外掛程式格式。
sandboxed
選填。 與沙箱相容的外掛程式陣列,這些外掛程式使用 EmDash 宣告的外掛程式 API,並在隔離的執行階段中執行。請勿在此處放置原生外掛程式:原生程式碼可能依賴沙箱不提供的處理程序和框架存取權限。
import thirdPartyPlugin from "third-party-emdash-plugin";
import { sandbox } from "@emdash-cms/cloudflare";
emdash({
sandboxed: [thirdPartyPlugin()],
sandboxRunner: sandbox(),
});
當未設定可用的沙箱執行器時,沙箱化外掛程式會被略過。有關 Cloudflare 和 Node.js 執行器的設定,請參閱外掛程式沙箱。
sandboxRunner
選填。 啟動隔離外掛程式執行階段的工廠的模組指定名稱。sandboxed 中的外掛程式以及市集或登錄庫外掛程式都需要它。
在 Cloudflare Workers 上,使用 sandbox() 配接器:
import { sandbox } from "@emdash-cms/cloudflare";
emdash({
sandboxRunner: sandbox(),
});
Node.js 部署使用外掛程式沙箱:Node.js 中記載的 workerd 執行器模組。
sandbox
選填。 控制已設定的沙箱執行器是否隔離外掛程式。設定了 sandboxRunner 時,沙箱即啟用。僅在診斷問題出在外掛程式本身還是其沙箱執行階段的情況下,才設定 sandbox: false:
emdash({
sandboxRunner: sandbox(),
sandbox: false,
});
設為 false 時,在 sandboxed 中宣告的外掛程式以及從市集安裝的外掛程式會在主伺服器處理程序中執行,沒有隔離也沒有資源限制。診斷完成後請還原沙箱。
registry
選填。 設定外掛程式登錄庫的彙整器和政策。未明確設定時,如果設定了 sandboxRunner 且 sandbox 不是 false,EmDash 會使用 https://registry.emdashcms.com。
設定 registry: false 可停用登錄庫探索和透過登錄庫安裝的外掛程式,同時保留沙箱執行器,供在 sandboxed 中宣告的外掛程式和舊版市集外掛程式使用:
emdash({
sandboxRunner: sandbox(),
registry: false,
});
可以將登錄庫服務 URL 作為字串傳入;當站台需要審核來源或發布時長政策時,使用物件形式。下面的範例使用物件形式:
import { sandbox } from "@emdash-cms/cloudflare";
emdash({
sandboxRunner: sandbox(),
registry: {
aggregatorUrl: "https://registry.emdashcms.com",
acceptLabelers: "did:web:labels.emdashcms.com",
policy: {
minimumReleaseAge: "48h",
minimumReleaseAgeExclude: ["did:plc:yourfirstpartydid"],
},
},
});
| 選項 | 型別 | 說明 |
|---|---|---|
aggregatorUrl | string | 登錄庫服務的基礎 URL。正式環境請使用 HTTPS。 |
acceptLabelers | string | 請求所接受的審核服務的去中心化識別碼(DID),以逗號分隔,選填。DID 是穩定的 Atmosphere 帳號識別碼。此設定無法覆寫登錄庫服務的政策。 |
policy.minimumReleaseAge | string | number | 暫緩發布比此時長更新的版本。時長字串("48h"、"7d")或秒數。 |
policy.minimumReleaseAgeExclude | string[] | 不受暫緩限制的發布者 DID,或 <did>/<plugin-slug> 組合。 |
僅當登錄庫回報只保留了一個版本,並確認其持續觀察過該套件時,發布時長政策才會豁免套件的首個版本。回填的套件、被刪除的早期版本或缺少歷史證據,都會使暫緩繼續生效。明確的發布者和套件豁免不論歷史如何都適用。
有關安裝流程和信任模型,請參閱外掛程式登錄庫。
marketplace
已棄用。 用於更新從舊版市集安裝的外掛程式的基礎 URL。管理後台不會顯示市集瀏覽和新安裝。只要設定了此選項,現有的市集外掛程式仍可更新和解除安裝。
emdash({
marketplace: "https://marketplace.emdashcms.com",
sandboxRunner: sandbox(),
});
正式環境的 URL 必須使用 HTTPS;開發期間僅對 localhost 和 127.0.0.1 接受 HTTP。在所有市集外掛程式都被取代或解除安裝之前請保留此選項,之後再將其移除。完整流程請參閱從市集遷移。
fonts
選填。 管理後台介面的字型設定。
預設情況下,EmDash 透過 Astro Font API 載入 Noto Sans。字型在建置時從 Google 下載並自行代管,因此執行階段沒有 CDN 請求。基礎字型涵蓋拉丁文、西里爾文、希臘文、天城文和越南文字。
若要新增對其他書寫系統的支援,請傳入文字名稱。下面的範例新增阿拉伯文和日文:
emdash({
fonts: {
scripts: ["arabic", "japanese"],
},
})
可用的文字包括 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 和 tibetan。
每種文字對應 Google Fonts 上相應的 Noto Sans 變體(例如 "arabic" 載入 Noto Sans Arabic)。所有字型樣式共用同一個 font-family 名稱並使用 unicode-range,因此瀏覽器只會下載頁面字元所需的檔案。
設為 false 可完全停用字型注入並使用系統字型:
emdash({
fonts: false,
})
管理後台的 CSS 使用 --font-emdash CSS 變數。它由上述字型設定自動設定。
auth
選填。 身分驗證配接器。EmDash 內建的登入方式是通行金鑰;設定 auth 會用外部提供者取代它。Cloudflare Access 配接器 access() 由 @emdash-cms/cloudflare 提供:
import { access } from "@emdash-cms/cloudflare";
emdash({
auth: access({
teamDomain: "myteam.cloudflareaccess.com",
audience: "your-app-audience-tag",
roleMapping: {
Admins: 50,
Editors: 40,
},
}),
});
access() 的選項:
| 選項 | 型別 | 預設值 | 說明 |
|---|---|---|---|
teamDomain | string | 必填 | 你的 Cloudflare Access 團隊網域 |
audience | string | — | 應用程式 Audience(AUD)標籤。在 Workers 上,建議使用 audienceEnvVar。 |
audienceEnvVar | string | "CF_ACCESS_AUDIENCE" | 執行階段從中讀取 audience 標籤的環境變數 |
autoProvision | boolean | true | 首次登入時建立 EmDash 使用者 |
defaultRole | number | 30 | 未被 roleMapping 比對到的使用者的角色等級(參見使用者角色) |
syncRoles | boolean | false | 每次登入時重新套用 roleMapping,而不是僅在建立帳號時套用 |
syncName | boolean | true | 在每次已驗證的請求中,用 IdP 中的姓名取代使用者姓名。設為 false 可讓姓名在管理後台中可編輯 |
roleMapping | object | — | 將 IdP 群組名稱對應到 EmDash 角色等級;先比對到者優先 |
authProviders
選填。 可插拔登入提供者的陣列(位於頂層,與 auth 並列)。每個項目都是呼叫某個提供者工廠函式的結果,如下所示:
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()],
});
內建提供者:
github()— 讀取EMDASH_OAUTH_GITHUB_CLIENT_ID/EMDASH_OAUTH_GITHUB_CLIENT_SECRET(或不帶前綴的備用變數)。google()— 讀取EMDASH_OAUTH_GOOGLE_CLIENT_ID/EMDASH_OAUTH_GOOGLE_CLIENT_SECRET。microsoft()— 讀取EMDASH_OAUTH_MICROSOFT_CLIENT_ID/EMDASH_OAUTH_MICROSOFT_CLIENT_SECRET/EMDASH_OAUTH_MICROSOFT_TENANT_ID(或不帶前綴的備用變數)。接受{ emailVerified }。參見 Microsoft。atproto()— Atmosphere 帳號登入(Bluesky 以及更廣泛的 AT Protocol 網路)。無需環境變數。接受{ allowedDIDs, allowedHandles, defaultRole }。參見 Atmosphere 登入指南。
第三方套件可以使用相同的 AuthProviderDescriptor 結構註冊自己的提供者——參見登入提供者。
mcp
選填。 在 /_emdash/api/mcp 啟用 Model Context Protocol(MCP)端點。該端點預設啟用且需要 Bearer 權杖,因此啟用它並不會授予匿名存取權限。
當站台不得公開 MCP 端點時,將該選項設為 false:
emdash({
mcp: false,
});
有關權杖建立和用戶端設定,請參閱 MCP 伺服器參考。
siteUrl
站台面向瀏覽器的公開來源(通訊協定 + 主機 + 選填連接埠,不含路徑)。請在執行正式環境設定之前設定它,除非站台部署到 Cloudflare Workers。否則,只有迴路開發主機才能在未設定來源的情況下完成設定。
在終止 TLS 的反向代理之後,Astro.url 會回傳內部位址(http://localhost:4321),而不是公開位址(https://cms.example.com)。這會破壞通行金鑰、CSRF 來源比對、OAuth 重新導向、登入重新導向、MCP 探索、快照匯出、網站地圖、robots.txt 以及 JSON-LD 結構化資料。設定 siteUrl 即可一次性修復所有這些問題。
整合會在載入時驗證該值:它必須是通訊協定為 http: 或 https: 的有效 URL,並被正規化為來源(路徑會被移除)。
下面的範例設定公開來源:
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
siteUrl: "https://cms.example.com",
});
當設定中未設定 siteUrl 時,EmDash 會依序檢查環境變數:先是 EMDASH_SITE_URL,然後是 SITE_URL。這對於在執行階段設定公開 URL 的容器部署很有用。
圖片最佳化會在建置時讀取這些環境變數,因為 Astro 在執行 astro build 時會把允許的圖片來源記錄到 image.remotePatterns 中。如果 EMDASH_SITE_URL 或 SITE_URL 只在執行階段環境中設定(例如在應用程式啟動時才注入變數的代管平台),本機儲存的媒體將以未最佳化的形式提供。請同時在建置環境中設定該變數,或在設定中設定 siteUrl。
在非迴路主機上,如果兩個來源都未設定,設定會失敗並報 SITE_URL_REQUIRED。這可以防止第一個未經身分驗證的設定請求選擇後續身分驗證電子郵件中使用的來源。部署到 Cloudflare Workers 的正式環境建置是例外:Cloudflare 只會路由綁定到該 Worker 的主機名稱的請求,因此設定會記錄 https:// 和其執行所在的主機名稱。同一個建置如果由 wrangler dev、astro preview 或可存取位址上的自行託管 workerd 提供服務,則沒有這樣的保證;請先在那裡設定 siteUrl 或 EMDASH_SITE_URL。
登入、邀請、自助註冊、帳號復原和評論通知郵件中的連結,會使用以下來源中第一個可用的來源(origin):siteUrl 或其環境變數、Settings > General 中的 Site URL,最後是設定過程中記錄的來源。只會使用 Site URL 的來源,並且除非主機是 localhost、127.0.0.1 或 [::1],否則必須使用 https://。ctx.site.url 使用與郵件連結相同的來源,並在執行階段啟動時讀取。如果在未設定 siteUrl 的情況下把站台遷移到新網域,請用 Change domain 修改 Site URL,這樣郵件連結才會指向新網域;參見遷移到新網域。網站地圖和 robots.txt 的順序不同,Site URL 優先於 siteUrl;參見 robots.txt。
在 Cloudflare Workers 上,環境變數後備機制讀取 process.env。使用 nodejs_compat 時,對於相容性日期為 2025-04-01 及之後的專案,Cloudflare 預設會填入 process.env。固定在更早日期的專案還必須加入 nodejs_compat_populate_process_env。
// wrangler.jsonc
{
"compatibility_date": "2026-02-24",
"compatibility_flags": ["nodejs_compat"],
"vars": { "EMDASH_SITE_URL": "https://cms.example.com" },
}
allowedOrigins
選填。 對於可透過多個主機名稱存取的部署,通行金鑰驗證所接受的額外瀏覽器來源。
siteUrl 定義單一標準來源。當同一個 EmDash 部署可透過共用同一可註冊父網域的多個主機名稱存取時(例如 https://example.com 和 https://preview.example.com),通行金鑰驗證會拒絕來源與 siteUrl 不完全一致的斷言——儘管 WebAuthn 允許通行金鑰在同一 rpId 下跨子網域有效。
透過 astro.config.mjs 中的 allowedOrigins 或 EMDASH_ALLOWED_ORIGINS 環境變數宣告額外接受的來源。標準的 siteUrl 仍是 rpId 的來源;此處列出的項目會在驗證時被接受。兩個來源會在執行階段合併,因此設定可以宣告穩定的來源(納入版本控制、經過程式碼審查),而環境變數則加入特定於環境的額外項目(例如暫時性的 PR 預覽)。
下面的範例在設定中宣告一個額外的來源:
emdash({
siteUrl: "https://example.com",
allowedOrigins: ["https://preview.example.com"],
})
等效的值也可以來自環境變數:
EMDASH_SITE_URL=https://example.com
EMDASH_ALLOWED_ORIGINS=https://preview.example.com,https://staging.example.com
驗證
EmDash 會驗證這些值,以防止出現瀏覽器永遠不會遵守的無效設定:
- 每個項目都必須是可解析的
http:或https:URL,不帶結尾的點,且主機名稱中沒有空標籤。 - 當
allowedOrigins非空時,必須設定siteUrl(來自任一來源),並且它不能是 IP 字面值,也不能是帶結尾點的主機名稱。 - 每個來源都必須與
siteUrl是同一主機名稱,或者是其子網域。(WebAuthn 要求rpId是每個來源的可註冊後綴。)
驗證失敗時,你會看到帶有來源標註的錯誤,例如 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.
錯誤出現的位置取決於這些值的宣告位置:
- Astro 啟動時:當
config.allowedOrigins和config.siteUrl都來自astro.config.mjs時——程式碼中的拼字錯誤會導致建置失敗。 - 首次通行金鑰驗證時:當其中任一值來自
EMDASH_ALLOWED_ORIGINS或EMDASH_SITE_URL時——環境不一致會在首次驗證嘗試時表現為 500 錯誤。
反向代理設定
只有當公開主機被允許時,Astro 才會反映 X-Forwarded-*。請為使用者存取的主機名稱(及通訊協定)設定 security.allowedDomains。在 astro dev 中,請加入相符的 vite.server.allowedHosts,讓 Vite 接受代理的 Host 標頭。
請優先修正 allowedDomains(以及轉送的標頭);當重建出的 URL 仍然與瀏覽器來源不一致時(典型情況是 TLS 在前端終止,而上游請求仍是 http://),再使用 siteUrl。
在前端有 TLS 的情況下,將開發伺服器繫結到迴路位址(astro dev --host 127.0.0.1)通常就足夠了:代理在本機連線,而 siteUrl 與公開的 HTTPS 來源一致。
如果你的代理會寫入用戶端 IP 標頭,請設定 trustedProxyHeaders,這樣 EmDash 的速率限制就能使用真實的用戶端 IP。沒有可信的用戶端 IP 時,身分驗證速率限制會被略過,所有公開留言提交共用同一個速率限制桶(所有訪客合計每 10 分鐘 20 則留言)。
下面的設定為反向代理部署同時設定 allowedDomains、vite.server.allowedHosts 和 siteUrl:
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
選填。 在你所控制的反向代理之後執行時,用於解析用戶端 IP 而被信任的標頭。供身分驗證速率限制(魔法連結、註冊、通行金鑰、OAuth 裝置流程、OAuth 用戶端註冊)和公開留言端點使用。
在 Cloudflare 上會自動使用附加到請求的 cf 物件——通常無需設定此項。在 nginx、Caddy、Traefik、Fly、Railway 等之後的自行代管部署中,請將此項設定為你的代理所寫入的標頭,這樣速率限制就能依真實用戶端 IP 統計請求。
下面的範例信任由 nginx、Caddy 或 Traefik 設定的 x-real-ip 標頭:
emdash({
database: sqlite({ url: "file:./data.db" }),
trustedProxyHeaders: ["x-real-ip"],
});
標頭會依序嘗試。符合 *-forwarded-for 的值會被解析為逗號分隔的清單,並使用第一個項目。下面的範例優先使用 Fly.io 的標頭,並退回到 x-forwarded-for:
emdash({
trustedProxyHeaders: ["fly-client-ip", "x-forwarded-for"],
});
當設定中未設定時,EmDash 會讀取 EMDASH_TRUSTED_PROXY_HEADERS 環境變數(以逗號分隔)。設定中明確的空陣列會覆寫該環境變數。
maxUploadSize
選填。 允許上傳的媒體檔案的最大大小(位元組)。同時適用於直接 multipart 上傳和簽署 URL 上傳。預設值為 52_428_800(50 MB)。下面的範例將上限提高到 100 MB:
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
maxUploadSize: 100 * 1024 * 1024, // 100 MB
});
| 值 | 說明 |
|---|---|
number(位元組) | 必須是正的有限整數 |
| 省略 | 預設為 50 MB |
超過所設定上限的上傳會被拒絕:直接上傳路徑回傳 413 Payload Too Large 回應,簽署 URL 路徑回傳 400 Validation Error。
admin
選填。 取代管理介面中的 EmDash 品牌標識,並限制建置中包含哪些管理介面語言。這些品牌標識值不會變更公開站台的標題、標誌或網站圖示。
emdash({
admin: {
logo: "/images/agency-logo.webp",
siteName: "Agency CMS",
footerLabel: "AgencyX",
favicon: "/favicon.ico",
},
});
| 選項 | 型別 | 說明 |
|---|---|---|
logo | string | 登入頁和側邊欄使用的標誌 URL 或路徑 |
siteName | string | 顯示在側邊欄標題和瀏覽器標題中的名稱 |
footerLabel | string | false | 顯示在側邊欄版本號旁的標籤。預設為 "EmDash";false 將其隱藏 |
favicon | string | 管理頁面的網站圖示 URL 或路徑 |
locales | string[] | 要包含的管理介面語言,例如 ["en", "de"]。其他語言的語言包不會打入管理後台的套件中,語言切換器中也只會顯示列出的語言。偏好語言未列出的使用者將看到英語。en 始終作為後備語言包含在內,即使你省略了它,因此 admin: { locales: ["en-GB"] } 會同時提供英語和英式英語。未知的代碼會導致建置失敗,錯誤訊息會列出可用的代碼。 |
updateCheck
選填。 控制管理儀表板中的核心更新通知。預設為 true。
啟用後,EmDash 會檢查公共 npm 登錄庫中已發布的 emdash 版本——每天最多一次,並在回應之後延遲執行,因此絕不會拖慢請求——當有更新的版本可用時,在管理儀表板中顯示一個可關閉的橫幅。關閉橫幅會針對該版本將其隱藏;當出現更新的版本時它會再次顯示。
橫幅所提示的是已公開至少 24 小時的最新穩定版本,因此一個版本會在發布一天後才被提示。若要與套件管理員的發布時長政策(例如 pnpm 的 minimumReleaseAge)保持一致,請將 minimumReleaseAge 設為時長字串("48h"、"7d")或秒數。設為 "7d" 時,橫幅會提示至少已發布一週的最新版本:
emdash({
updateCheck: { minimumReleaseAge: "7d" },
})
該檢查是對 https://registry.npmjs.org/emdash 的一個一般 GET 請求,不攜帶任何站台資料。沒有對外網際網路存取的部署只是永遠不會顯示橫幅。設為 false 可完全停用該檢查:
emdash({
updateCheck: false,
})
橫幅僅作提示——更新 EmDash 意味著提升專案中 emdash 套件的版本並重新部署,而管理後台無法(也不應)觸發這項操作。
toolbar
選填。 控制編輯者工具列(公開頁面上的浮動膠囊)的傳遞方式。預設為 "server"。
| 值 | 行為 |
|---|---|
"server"(預設) | 工具列會在伺服器端注入到為已通過身分驗證的編輯者轉譯的每個 HTML 回應中。 |
"client" | 公開 HTML 對每位訪客都完全相同。一個很小的啟動腳本會在已登入過管理後台的瀏覽器中顯示一個「Edit」膠囊;點擊它會驗證工作階段,並攜帶 _edit 查詢參數重新載入頁面,該頁面一律即時轉譯(從不快取),並帶有完整的工具列。 |
false | 從不轉譯工具列或啟動腳本。 |
emdash({
toolbar: "client",
})
當你的公開 HTML 透過共用快取提供時(Cloudflare Cache Everything / Workers Cache、Fastly、Varnish 等),請使用 "client"。使用伺服器端注入時,只要匿名訪客先預熱了快取,瀏覽公開站台的編輯者就會收到快取的匿名版本——不帶工具列——因此工具列會隨快取狀態時有時無。在用戶端模式下,不會向可共用的 HTML 中注入任何與工作階段相關的內容,因此快取保持完全有效,工具列也很可靠。
關於 "client" 模式的說明:
- 已登出的訪客開啟共用的
?_editURL 時會被重新導向到標準 URL,因此該參數不會洩漏草稿,也不會用頁面內容預熱額外的快取項目。 - 「已登入」訊號是由管理後台設定的、非機密的
localStorage旗標;膠囊在進入編輯檢視之前會驗證真實的工作階段。 - 啟動腳本是一小段內嵌
<script>。如果你的站台傳送了不帶'unsafe-inline'的嚴格Content-Security-Policy,請為它加入雜湊——伺服器端注入的工具列同樣如此。 - EmDash 不會注入任何與工作階段相關的內容——但如果你自己的範本根據
Astro.locals.user分支(例如為已登入使用者顯示「Admin」導覽連結),這種差異仍會留在你的 HTML 中,並且仍會使快取碎片化。
無論哪種模式,都可以在瀏覽器中透過 × 按鈕關閉工具列(依瀏覽器生效,直到編輯者下次開啟管理後台)。預覽和編輯模式的回應一律在伺服器端轉譯,並帶有 Cache-Control: private, no-store。
資料庫配接器
從 emdash/db 匯入配接器:
import { sqlite, libsql, postgres } from "emdash/db";
sqlite(config)
使用 Node.js 內建資料庫驅動程式的 SQLite 資料庫。下面的範例連線到本機檔案:
| 選項 | 型別 | 說明 |
|---|---|---|
url | string | 帶 file: 前綴的檔案路徑 |
sqlite({ url: "file:./data.db" });
libsql(config)
libSQL 資料庫。下面的範例連線到遠端 libSQL 資料庫:
| 選項 | 型別 | 說明 |
|---|---|---|
url | string | 資料庫 URL |
authToken | string | 執行階段身分驗證權杖(本機檔案可選) |
migrationAuthTokenEnv | string | 遷移權杖變數名稱(預設 TURSO_AUTH_TOKEN) |
libsql({
url: process.env.LIBSQL_DATABASE_URL,
authToken: process.env.LIBSQL_AUTH_TOKEN,
});
postgres(config)
帶連線池的 PostgreSQL 資料庫。
| 選項 | 型別 | 說明 |
|---|---|---|
connectionString | string | PostgreSQL 連線 URL |
host | string | 資料庫主機 |
port | number | 資料庫連接埠 |
database | string | 資料庫名稱 |
user | string | 資料庫使用者 |
password | string | 資料庫密碼 |
ssl | boolean | 啟用 SSL |
pool.min | number | 最小連線池大小(預設:0) |
pool.max | number | 最大連線池大小(預設:10) |
pool.connectionTimeoutMillis | number | 最長連線等待時間(pg 預設:0,無逾時) |
pool.idleTimeoutMillis | number | 閒置用戶端的存活時間(pg 預設:10,000 毫秒) |
migrationConnectionStringEnv | string | 遷移連線字串變數名稱(預設 DATABASE_URL) |
下面的範例使用連線字串進行連線:
postgres({ connectionString: process.env.DATABASE_URL });
d1(config)
Cloudflare D1 資料庫。從 @emdash-cms/cloudflare 匯入。
| 選項 | 型別 | 預設值 | 說明 |
|---|---|---|---|
binding | string | — | wrangler.jsonc 中的 D1 繫結名稱 |
session | string | "disabled" | 讀取複寫模式:"disabled"、"auto" 或 "primary-first" |
bookmarkCookie | string | "__em_d1_bookmark" | 工作階段書籤的 Cookie 名稱 |
coalesce | boolean | false | 在同一事件迴圈週期內批次處理並行讀取;需要 "disabled" 以外的工作階段模式 |
下面的範例展示了基本繫結以及啟用讀取複本的繫結:
// Basic
d1({ binding: "DB" });
// With read replicas
d1({ binding: "DB", session: "auto" });
當 session 為 "auto" 或 "primary-first" 時,EmDash 使用 D1 Sessions API 將讀取查詢路由到就近的複本。已通過身分驗證的使用者可獲得基於書籤的「讀己所寫」一致性。詳情請參閱資料庫選項 — 讀取複本。
hyperdrive(config?)
透過 Cloudflare Hyperdrive 繫結使用 PostgreSQL。從 @emdash-cms/cloudflare 匯入此配接器。
| 選項 | 型別 | 預設值 | 說明 |
|---|---|---|---|
binding | string | "HYPERDRIVE" | 停用查詢快取的主 Hyperdrive 繫結 |
cachedBinding | string | — | 選填的第二個啟用快取的繫結,用於匿名公開讀取 |
preferUncachedAfterWriteMs | number | 60_000 | 設定 cachedBinding 時,內容寫入之後公開讀取使用主繫結的持續時間 |
migrationConnectionStringEnv | string | 由主繫結衍生 | 包含 emdash migrate 所用的 PostgreSQL 直接連線 URL 的環境變數 |
max | number | 5 | 單一 Worker isolate 到 Hyperdrive 的最大連線數 |
下面的範例讓已通過身分驗證的請求和寫入走未快取的繫結,而匿名公開讀取可以使用已快取的繫結:
hyperdrive({
binding: "HYPERDRIVE",
cachedBinding: "HYPERDRIVE_CACHED",
preferUncachedAfterWriteMs: 60_000,
});
兩個繫結必須指向同一個資料庫。請安裝 pg 8.16.3 或更高版本,啟用 nodejs_compat 相容性旗標,並為部署遷移設定資料庫直接連線 URL。完整的 Worker 和遷移設定請參閱資料庫選項:Hyperdrive。
durableObjects(config)
將 CMS 儲存在一個由 SQLite 支援的 Durable Object 中。從 @emdash-cms/cloudflare 匯入此配接器。
| 選項 | 型別 | 預設值 | 說明 |
|---|---|---|---|
binding | string | 必填 | EmDashDB 類別的 Durable Object 命名空間繫結 |
name | string | "emdash" | 單例物件名稱;僅在需要在同一繫結後隔離多個資料庫時才修改 |
session | string | "disabled" | "auto" 將匿名讀取路由到複本,將寫入路由到主庫 |
bookmarkCookie | string | "__em_do_bookmark" | 在 "auto" 模式下用於「讀己所寫」一致性的 Cookie |
durableObjects({ binding: "DB_DO", session: "auto" });
複本路由需要 experimental 和 replica_routing 相容性旗標,以及 wrangler.jsonc 中的 Durable Object 類別和遷移項目。
previewDatabase(config)
在 Durable Object 中為每個預覽工作階段建立一個隔離的快照資料庫。唯一的選項是必填的 binding 名稱:
previewDatabase({ binding: "PREVIEW_DB" });
此配接器用於預覽基礎架構,而非正式站台的主資料庫。
playgroundDatabase(config)
在 Durable Object 中為每個實驗場工作階段建立一個可寫入且帶種子資料的資料庫。請將其與 playground 整合選項搭配使用:
playgroundDatabase({ binding: "PLAYGROUND_DB" });
必填的 binding 用於識別實驗場的 Durable Object 命名空間。此配接器僅用於一次性展示站台。
儲存配接器
從 emdash/astro 匯入 local 和 s3。r2 配接器從 @emdash-cms/cloudflare 匯入:
import emdash, { local, s3 } from "emdash/astro";
import { r2 } from "@emdash-cms/cloudflare";
local(config)
本機檔案系統儲存。下面的範例從本機目錄提供上傳檔案:
| 選項 | 型別 | 說明 |
|---|---|---|
directory | string | 目錄路徑 |
baseUrl | string | 提供檔案存取的基礎 URL |
local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
});
r2(config)
Cloudflare R2 繫結。下面的範例使用帶公開 URL 的 R2 繫結:
| 選項 | 型別 | 說明 |
|---|---|---|
binding | string | R2 繫結名稱 |
publicUrl | string | 選填的公開 URL |
r2({
binding: "MEDIA",
publicUrl: "https://pub-xxxx.r2.dev",
});
s3(config?)
與 S3 相容的儲存。所有設定欄位都是選填的:s3({...}) 中省略的任何欄位,
都會在 Node 處理程序啟動時從對應的 S3_* 環境變數解析。明確的值一律優先。
設定值和環境變數值合併之後,endpoint 和 bucket 為必填項。如果設定了任一憑證,則 accessKeyId 和 secretAccessKey 都是必填項。缺少值會導致啟動失敗,錯誤碼為 MISSING_S3_CONFIG。
先決條件: 請在你的專案中安裝 @aws-sdk/client-s3 和 @aws-sdk/s3-request-presigner。
EmDash 核心不內附 AWS SDK。詳情請參閱
儲存選項:S3 相容儲存。
| 選項 | 型別 | 說明 |
|---|---|---|
endpoint | string | S3 端點 URL(S3_ENDPOINT) |
bucket | string | 儲存貯體名稱(S3_BUCKET) |
accessKeyId | string | 存取金鑰(S3_ACCESS_KEY_ID) |
secretAccessKey | string | 私密金鑰(S3_SECRET_ACCESS_KEY) |
region | string | 區域,預設 "auto"(S3_REGION) |
publicUrl | string | 選填的 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
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",
})
執行階段環境變數解析是僅限 Node 的功能。在 Cloudflare
Workers 上,密鑰和變數透過 fetch 處理常式的 env 參數公開,
而不是透過 process.env,因此不會讀取 S3_* 環境變數。
Workers 部署應使用 r2(config) 配接器,
或向 s3({...}) 傳入明確的值。詳情請參閱
儲存選項。
物件快取配接器
將其中之一傳給 objectCache 選項。
kvCache(config)
Cloudflare KV 後端,在所有 isolate 之間共用。從 @emdash-cms/cloudflare 匯入。
kvCache({
binding: "CACHE", // KV binding name (required)
defaultTtl: 3600, // entry TTL in seconds (optional, KV minimum 60)
revalidate: 1000, // cross-isolate staleness window in ms (optional)
timeout: 2000, // per-op timeout in ms before a miss (optional, 0 disables)
keyPrefix: "em", // cache key prefix (optional)
})
memoryCache(config?)
適用於 Node.js 和開發環境的處理程序內後端。從 emdash/astro 匯入。
memoryCache({
defaultTtl: 3600, // entry TTL in seconds (optional)
revalidate: 1000, // staleness window in ms (optional)
maxEntries: 1000, // max cached keys before eviction (optional)
keyPrefix: "em", // cache key prefix (optional)
})
設定與行為請參閱物件快取。
身分驗證與沙箱配接器
這些配接器為 auth 和 sandboxRunner 整合選項回傳值。
access(config)
用 Cloudflare Access 身分驗證取代內建的通行金鑰登入。從 @emdash-cms/cloudflare 匯入,並將結果傳給 auth:
import { access } from "@emdash-cms/cloudflare";
emdash({
auth: access({
teamDomain: "myteam.cloudflareaccess.com",
audienceEnvVar: "CF_ACCESS_AUDIENCE",
}),
});
teamDomain 為必填項。配接器可以從 audience 或 audienceEnvVar 所指定的變數中讀取應用程式 audience;它還接受 autoProvision、defaultRole、syncRoles、syncName 和 roleMapping。這些選項的預設值、角色行為和姓名同步記載於 auth 選項中。
sandbox()
選擇 Cloudflare Worker Loader 作為外掛程式沙箱執行器。從 @emdash-cms/cloudflare 匯入,並將其回傳值傳給 sandboxRunner:
import { sandbox } from "@emdash-cms/cloudflare";
emdash({
sandboxRunner: sandbox(),
});
站台還需要一個 Worker Loader 繫結和外掛程式橋接進入點。有關這些部署設定,請參閱外掛程式沙箱:Cloudflare Workers。
媒體提供者配接器
將媒體提供者描述元傳給 mediaProviders。兩個內建的 Cloudflare 提供者都從 @emdash-cms/cloudflare 匯入。
下面每個 *EnvVar 選項都指定一個環境變數名稱。提供者依以下順序讀取:先是同名的 Cloudflare Workers 繫結,然後是 Node 配接器上的 process.env。對應的直接選項(accountId、accountHash、apiToken)一律優先於這兩者。
cloudflareImages(config)
新增 Cloudflare Images,用於瀏覽、上傳、刪除和傳遞影像資產。
| 選項 | 型別 | 預設值 | 說明 |
|---|---|---|---|
accountId | string | 來自 CF_ACCOUNT_ID | Cloudflare 帳號 ID |
accountIdEnvVar | string | "CF_ACCOUNT_ID" | 省略 accountId 時使用的變數 |
accountHash | string | 來自 CF_IMAGES_ACCOUNT_HASH | 傳遞 URL 中使用的帳號雜湊 |
accountHashEnvVar | string | "CF_IMAGES_ACCOUNT_HASH" | 省略 accountHash 時使用的變數 |
apiToken | string | 來自 CF_IMAGES_TOKEN | 具有 Cloudflare Images 讀取和編輯權限的權杖 |
apiTokenEnvVar | string | "CF_IMAGES_TOKEN" | 省略 apiToken 時使用的變數 |
deliveryDomain | string | imagedelivery.net | 自訂影像傳遞主機名稱 |
defaultVariant | string | "public" | 用於顯示的影像變體 |
mediaProviders: [cloudflareImages({ defaultVariant: "public" })];
cloudflareStream(config)
新增 Cloudflare Stream,用於瀏覽、搜尋、上傳、刪除和播放影片資產。
| 選項 | 型別 | 預設值 | 說明 |
|---|---|---|---|
accountId | string | 來自 CF_ACCOUNT_ID | Cloudflare 帳號 ID |
accountIdEnvVar | string | "CF_ACCOUNT_ID" | 省略 accountId 時使用的變數 |
apiToken | string | 來自 CF_STREAM_TOKEN | 具有 Cloudflare Stream 讀取和編輯權限的權杖 |
apiTokenEnvVar | string | "CF_STREAM_TOKEN" | 省略 apiToken 時使用的變數 |
customerSubdomain | string | Cloudflare 預設值 | 自訂 Stream 傳遞主機名稱 |
controls | boolean | true | 顯示播放器控制項 |
autoplay | boolean | false | 自動開始播放 |
loop | boolean | false | 循環播放 |
muted | boolean | false,啟用自動播放時為 true | 靜音播放 |
mediaProviders: [cloudflareStream({ controls: true })];
有關所需的繫結和轉譯元件,請參閱媒體庫:媒體提供者。
即時集合
在 src/live.config.ts 中設定 EmDash 載入器:
import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";
export const collections = {
_emdash: defineLiveCollection({
loader: emdashLoader(),
}),
};
載入器選項
emdashLoader() 函式不接受參數:
emdashLoader();
環境變數
EmDash 會識別以下環境變數:
| 變數 | 說明 |
|---|---|
EMDASH_SITE_URL | 面向瀏覽器的公開來源(後備為 SITE_URL) |
EMDASH_ALLOWED_ORIGINS | 通行金鑰驗證所接受的額外來源的逗號分隔清單(多子網域部署)。 |
EMDASH_DATABASE_URL | 覆寫資料庫 URL |
EMDASH_ENCRYPTION_KEY | 用於靜態加密外掛程式密鑰的金鑰。由維運人員提供——絕不儲存在資料庫中。 |
EMDASH_PREVIEW_SECRET | 預覽 HMAC 密鑰的選填覆寫。未設定時,會產生每個站台穩定的值並儲存在資料庫中。 |
EMDASH_IP_SALT | 留言者 IP 雜湊鹽值的選填覆寫。未設定時,會產生每個站台穩定的值並儲存在資料庫中。 |
EMDASH_AUTH_SECRET | 舊版。如已設定,則用作 IP 鹽值的來源;現有安裝應保留它,以便在升級後保持留言者 IP 雜湊穩定。 |
EMDASH_TURNSTILE_SECRET_KEY | Cloudflare Turnstile 密鑰(後備為 TURNSTILE_SECRET_KEY)。設定後,留言提交必須包含有效的 Turnstile 權杖——請與 <CommentForm> 上的 turnstileSiteKey 屬性搭配使用。 |
EMDASH_URL | 用於結構描述同步的遠端 EmDash URL |
使用以下指令產生加密金鑰:
npx emdash secrets generate
package.json 設定
範本和站台可以在 package.json 的 emdash 鍵下宣告選填的中繼資料:
{
"emdash": {
"label": "My Blog Template",
"schema": ".emdash/schema.sql",
"seed": ".emdash/seed.json"
}
}
| 選項 | 說明 |
|---|---|
label | 用於顯示的範本名稱 |
schema | emdash init 讀取的選填 SQL 結構描述 |
seed | 種子 JSON 檔案的路徑 |
TypeScript 設定
在本機開發期間,Astro 整合會在專案根目錄產生 emdash-env.d.ts,並在結構描述變更後重新整理它。該檔案擴充了 emdash 模組,因此標準的 getEmDashCollection() 和 getEmDashEntry() 匯入無需路徑別名即可推斷本機集合的欄位。
單獨的 emdash types 指令會從執行中的本機或遠端執行個體取得結構描述,預設寫入 .emdash/types.ts。僅當應用程式碼直接匯入該獨立輸出時,才需要新增別名:
{
"compilerOptions": {
"paths": {
"@emdash-cms/types": ["./.emdash/types.ts"]
}
}
}
使用以下指令產生遠端結構描述的獨立型別:
npx emdash types