EmDash 的主要設定位於 astro.config.mjs,src/live.config.ts 負責註冊內容載入器。與部署相關的值也可來自環境變數。package.json 中較小的 emdash 中繼資料區塊用於範本標籤及舊版本機 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(從 @emdash-cms/cloudflare 匯入)
database: d1({ binding: "DB" });
詳見資料庫選項。
migrations
可選。 控制 EmDash 內部資料庫遷移在執行時的處理方式。省略此選項時預設為 { runtime: "auto" }。
migrations: {
runtime: "check", // "auto" | "check" | "manual"
dev: "auto", // 可選的開發環境覆寫
}
auto 會檢查並應用待執行的遷移;check 在執行中的建置已知有待執行遷移時返回 503;manual 不在執行時查詢遷移。EMDASH_MIGRATIONS_MODE 會覆寫實際生效的執行時模式。在採用 check 或 manual 之前,請參閱管理核心資料庫遷移。
storage
可選。 媒體儲存配接器設定。省略此選項時,EmDash 將檔案保存在 ./.emdash/uploads,並透過 /_emdash/api/media/file 提供存取。當預設本機目錄不適用時,請選擇配接器:
// 本機檔案系統(開發)
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
});
// R2 綁定(Cloudflare Workers)
storage: r2({
binding: "MEDIA",
publicUrl: "https://pub-xxxx.r2.dev", // 可選
});
// S3 相容(任意平台)— 全部欄位來自 S3_* 環境變數
storage: s3()
// 或使用顯式值
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", // 可選,預設:"auto"
publicUrl: "https://cdn.example.com", // 可選
});
詳見儲存選項。
images
可選。 控制 EmDash 是否將已儲存媒體與 Astro 圖片最佳化整合。預設為 true。
啟用後,EmDash 會包裝 Astro 的圖片端點,使 <Image> 和 getImage() 可直接從已設定的儲存配接器讀取源位元組。當原始媒體 URL 位於 Cloudflare Access 之後時同樣有效。若由其他圖片服務處理媒體,或希望所有圖片不經 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(在所有 isolate 之間共享)
import { kvCache } from "@emdash-cms/cloudflare";
objectCache: kvCache({ binding: "CACHE" });
// 記憶體快取(Node.js / 開發)
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 試玩場(playground)所用中介軟體。每個工作階段建立可寫的 Durable Object 資料庫,應用設定的 seed 資料,並在常規 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
可選。 管理後台 UI 字型設定。
預設情況下,EmDash 透過 Astro Font API 載入 Noto Sans。字型在建置時從 Google 下載並自托管,執行時無 CDN 請求。基礎字型覆寫拉丁、西里尔、希腊、天城文與越南文等書寫系統。
要支援更多書寫系統,傳入書寫系統(script)名稱。以下範例新增阿拉伯文與日文:
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 內建登入為通行密鑰(passkey);設定 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,而非僅在首次開通時 |
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。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
站台面向瀏覽器的公開源站(origin,即 scheme + host + 可選端口,不含 path)。在生產環境初始化之前設定。僅回環(loopback)開發主機可在未設定源站時完成初始化。
在終止 TLS 的反向代理之後,Astro.url 返回內部地址(http://localhost:4321)而非公開地址(https://cms.example.com)。這會破壞通行密鑰、CSRF 源站匹配、OAuth 重定向、登入重定向、MCP 探索、快照匯出、站台地圖、robots.txt 與 JSON-LD 結構化資料。設定 siteUrl 可一次性修復上述問題。
整合在載入時校驗該值:須為有效 URL,協定為 http: 或 https:,並規範化為 origin(剝離 path)。
以下範例設定公開源站:
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 的容器部署。
在非回環主機上,若兩處均未設定,初始化將因 SITE_URL_REQUIRED 失敗。這可避免首次未身分驗證的初始化請求選定後續身分驗證郵件中使用的源站。
在 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-*。為使用者實際存取的主機名(及 scheme)設定 security.allowedDomains。在 astro dev 中新增匹配的 vite.server.allowedHosts,以便 Vite 接受代理的 Host 標頭。
優先修復 allowedDomains(及轉發標頭);當重建 URL 仍與瀏覽器源站不一致時使用 siteUrl(常見於前端終止 TLS 而上游請求仍為 http://)。
前端有 TLS 時,將開發伺服器綁定到回環地址(astro dev --host 127.0.0.1)通常足夠:代理本機连接,siteUrl 匹配公開 HTTPS 源站。
若代理写入用戶端 IP 標頭,請設定 trustedProxyHeaders,使 EmDash 速率限制使用真实用戶端 IP,而非將所有請求归入共享的 “unknown” 桶。
以下設定為反向代理部署同時設定 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 裝置流程)及公開评论端點。
在 Cloudflare 上會自動使用請求附带的 cf 物件 — 通常無需設定此項。在 nginx、Caddy、Traefik、Fly、Railway 等之後的自托管部署中,設為代理写入的標頭,以便速率限制按真实用戶端 IP 分桶,而非將每個請求视為 “unknown”。
以下範例信任 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 品牌。這些值不會更改公開站台的标题、logo 或 favicon。
emdash({
admin: {
logo: "/images/agency-logo.webp",
siteName: "Agency CMS",
favicon: "/favicon.ico",
},
});
| 選項 | 類型 | 描述 |
|---|---|---|
logo | string | 登入页與侧栏的 logo URL 或路径 |
siteName | string | 侧栏與瀏覽器标题中顯示的名稱 |
favicon | string | 管理頁面的 favicon URL 或路径 |
toolbar
可選。 控制編輯工具栏(公開頁面上的浮动胶囊按钮)的交付方式。預設為 "server"。
| 值 | 行為 |
|---|---|
"server"(預設) | 工具栏在服務端注入到為已身分驗證編輯者渲染的每個 HTML 回應中。 |
"client" | 公開 HTML 对所有訪客相同。小型引导脚本在已登入管理後台的瀏覽器中顯示「編輯」胶囊;点击后驗證工作階段並以 _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分支(例如為登入使用者顯示「管理」导航連結),該差異仍在 HTML 中並仍會碎片化快取。
任何模式下,編輯者都可透過工具栏 × 按钮在瀏覽器中关闭(按瀏覽器,直至下次打开管理後台)。預覽與編輯模式回應始终在服務端渲染,並带 Cache-Control: private, no-store。
experimental
可選。 可選啟用的功能,其行為或线上協定格式可能在次版本中變更或移除。各欄位独立啟用。
experimental.registry
已棄用。 請使用頂層 registry 選項。省略頂層選項時,現有 experimental.registry 設定仍可用。若两者並存,頂層值優先。
以下變更將現有註冊表 URL 移到頂層:
emdash({
experimental: {
registry: "https://registry.example.com",
},
registry: "https://registry.example.com",
});
資料庫配接器
從 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 | 遷移 token 變數名(預設 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 ms) |
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" | 工作階段 bookmark 的 Cookie 名 |
coalesce | boolean | false | 在同一事件循环回合批量並发读;需要非 "disabled" 的 session 模式 |
以下範例展示基本綁定與啟用读副本的設定:
// 基本
d1({ binding: "DB" });
// 啟用读副本
d1({ binding: "DB", session: "auto" });
當 session 為 "auto" 或 "primary-first" 時,EmDash 使用 D1 Sessions API 將读查詢路由到附近副本。已身分驗證使用者获得基於书签(bookmark)的「写后读」一致性。詳見資料庫選項 — 读副本。
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)
在每個 playground 工作階段的 Durable Object 中建立一個可寫、已填充 seed 資料的資料庫。與 playground 整合選項配合使用:
playgroundDatabase({ binding: "PLAYGROUND_DB" });
必選的 binding 标识 playground 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 相容儲存。所有設定欄位均為可選:Node 處理序啟動時,s3({...}) 中省略的欄位會從匹配的 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) |
以下範例分别從環境解析全部欄位、混合設定與環境,或顯式傳入每個欄位:
// 全部欄位來自 S3_* 環境變數(Node 容器部署)
s3()
// 混合:CDN 來自設定,其余來自環境變數
s3({ publicUrl: "https://cdn.example.com" })
// 全部顯式指定
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 綁定名(必填)
defaultTtl: 3600, // 項目 TTL(秒,可選,KV 最小 60)
revalidate: 1000, // 跨 isolate 陈旧窗口(毫秒,可選)
timeout: 2000, // 每次操作超時(毫秒,超時后视為未命中,可選,0 表示停用)
keyPrefix: "em", // 快取鍵前綴(可選)
})
memoryCache(config?)
Node.js 與開發用的處理序内后端。從 emdash/astro 匯入。
memoryCache({
defaultTtl: 3600, // 項目 TTL(秒,可選)
revalidate: 1000, // 陈旧窗口(毫秒,可選)
maxEntries: 1000, // 驱逐前最大快取鍵数(可選)
keyPrefix: "em", // 快取鍵前綴(可選)
})
設定與行為詳見物件快取。
身分驗證與沙箱配接器
這些配接器為 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 與 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,或在 autoplay 時為 true | 静音播放 |
mediaProviders: [cloudflareStream({ controls: true })];
所需綁定與渲染组件見媒體庫:媒體提供商。
Astro 快取配接器
cloudflareCache(config?)
舊版配接器返回 Astro cache.provider,在 Workers Cache API 中儲存回應,並透過 Cloudflare REST API 按標籤清除快取:
import { cloudflareCache } from "@emdash-cms/cloudflare";
export default defineConfig({
cache: {
provider: cloudflareCache(),
},
});
接受 cacheName(預設 "emdash")與 bookmarkCookie(預設 "__em_d1_bookmark"),以及用於按標籤清除的 zoneId 或 zoneIdEnvVar 與 apiToken 或 apiTokenEnvVar。預設變數名為 CF_ZONE_ID 與 CF_CACHE_PURGE_TOKEN。
即時集合
在 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 | 用於 schema 同步的远程 EmDash URL |
使用以下命令生成加密密鑰:
npx emdash secrets generate
package.json 設定
模板與站台可在 package.json 的 emdash 鍵下宣告可選元資料:
{
"emdash": {
"label": "My Blog Template",
"schema": ".emdash/schema.sql",
"seed": ".emdash/seed.json",
"url": "https://my-site.pages.dev"
}
}
| 選項 | 描述 |
|---|---|
label | 用於展示的模板名稱 |
schema | emdash init 讀取的可選 SQL 架构檔案 |
seed | seed 資料 JSON 檔案路径 |
url | 已棄用 emdash dev --types 流程使用的远程 URL |
TypeScript 設定
本機開發時,Astro 整合會在專案根目錄生成 emdash-env.d.ts,並在 schema 變更后刷新。該檔案增强 emdash 模組,因此标准的 getEmDashCollection() 與 getEmDashEntry() 匯入可在无路径别名的情況下推断本機集合欄位。
独立的 emdash types 命令從執行中的本機或远程实例获取 schema,預設写入 .emdash/types.ts。僅當應用程式碼直接匯入該独立输出時再新增别名:
{
"compilerOptions": {
"paths": {
"@emdash-cms/types": ["./.emdash/types.ts"]
}
}
}
使用以下命令生成独立的远程 schema 類型:
npx emdash types