Cloudflare Workers 為 EmDash 提供了快速的全球分散式執行環境。本指南涵蓋使用 D1 作為資料庫和 R2 作為媒體儲存的部署。
前置要求
- 一個 Cloudflare 帳戶
- 已安裝 Wrangler CLI(
npm install -g wrangler) - 已通過 Cloudflare 認證(
wrangler login)
設定綁定
在專案根目錄建立包含 D1 和 R2 綁定的 wrangler.jsonc。如果資源尚不存在,Wrangler 會在首次部署時自動建立。
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-emdash-site",
"compatibility_date": "2025-01-15",
"compatibility_flags": ["nodejs_compat"],
"d1_databases": [
{
"binding": "DB",
"database_name": "emdash-db",
},
],
"r2_buckets": [
{
"binding": "MEDIA",
"bucket_name": "emdash-media",
},
],
}
設定 EmDash
更新你的 Astro 設定以使用 D1 和 R2:
import { defineConfig } from "astro/config";
import cloudflare from "@astrojs/cloudflare";
import react from "@astrojs/react";
import emdash from "emdash/astro";
import { d1, r2 } from "@emdash-cms/cloudflare";
export default defineConfig({
output: "server",
adapter: cloudflare(),
integrations: [
react(), // 必需 — 管理介面是一個 React 應用程式
emdash({
database: d1({ binding: "DB" }),
storage: r2({ binding: "MEDIA" }),
}),
],
});
首次啟動
資料庫遷移在部署後的第一個請求時自動執行,如果有新的遷移需要套用,後續每次啟動也會執行。
如果資料庫為空(沒有集合)且設定精靈未完成,EmDash 還會在首次啟動時套用種子檔案。種子在建置時從 .emdash/seed.json、package.json#emdash.seed 中的路徑或 seed/seed.json 讀取 — 以先找到的為準 — 並內嵌到打包檔案中。如果都不存在,則使用內建的預設種子。對現有資料庫的後續部署不會修改其內容。
要變更已部署網站的結構或內容模型,請參閱演進已部署的網站。
排程發佈
在 Cloudflare Workers 上,排程發佈、外掛 cron 和維護工作透過 Worker Cron Trigger 執行。新的 Cloudflare 範本自動包含此設定。如果你正在更新現有專案,請從 @emdash-cms/cloudflare/worker 匯出 EmDash Worker 進入點:
export { default, PluginBridge } from "@emdash-cms/cloudflare/worker";
然後在 wrangler.jsonc 中新增 Cron Trigger:
{
"triggers": {
"crons": ["* * * * *"],
},
}
部署
部署到 Cloudflare Workers:
wrangler deploy
你的網站現在已在 https://my-emdash-site.<your-subdomain>.workers.dev 上線。
讀取副本
對於全球分散的網站,啟用 D1 讀取複製以將讀取查詢路由到鄰近的副本,而不是總是存取主資料庫。這顯著降低了遠離主區域的訪客的延遲。
emdash({
database: d1({
binding: "DB",
session: "auto",
}),
storage: r2({ binding: "MEDIA" }),
}),
你還需要在 Cloudflare 儀表板或透過 REST API 在 D1 資料庫本身上啟用讀取複製。
有關工作階段模式和基於書籤的一致性如何運作,請參閱資料庫選項 — 讀取副本。
Object Cache
為了減少 D1 的讀取負載,將內容和設定查詢結果快取到 Cloudflare KV。讀取從 KV 提供,而不是每次請求都查詢資料庫:
import { d1, r2, kvCache } from "@emdash-cms/cloudflare";
emdash({
database: d1({ binding: "DB" }),
storage: r2({ binding: "MEDIA" }),
objectCache: kvCache({ binding: "CACHE" }),
}),
有關 KV 設定、選項和失效行為,請參閱 Object Cache。
Workers Cache
Cloudflare 的 Workers Cache(wrangler.jsonc 中的 "cache": { "enabled": true })在你的 Worker 前面放置一個邊緣快取:符合的請求完全不執行你的 Worker 就能得到回應。這與 EmDash 配合良好:
- EmDash 管理和 API 回應傳送
Cache-Control: private, no-store,永遠不會被儲存。 - 你的公開頁面透過它們回傳的
Cache-Control標頭控制自己的快取。
啟用前需要了解的兩點:
- 沒有
Cache-Control標頭的回應仍然會被快取。 Workers Cache 套用 RFC 9111 啟發式新鮮度 — 沒有任何標頭的200會被快取 2 小時。給每個自訂路由一個明確的Cache-Control(對任何依賴工作階段的內容使用private, no-store)。 - 快取的頁面與已登入的編輯者共用。 快取在你的 Worker 之前執行,因此無法基於請求 cookie 繞過。已登入的編輯者可能會收到公開頁面的快取匿名變體 — 沒有視覺編輯工具列 — 直到條目過期。編輯者渲染的回應本身永遠不會被儲存(它們攜帶
private, no-store),因此不會向另一個方向洩漏。
自訂網域
在 Cloudflare 儀表板中新增自訂網域:
- 前往 Workers & Pages > 你的 worker
- 點擊 Custom Domains > Add Custom Domain
- 輸入你的網域並按照 DNS 設定說明操作
公開 R2 存取
要直接從 R2 提供媒體(建議以提高效能):
- 在 Cloudflare 儀表板中,前往 R2 > 你的儲存桶
- 點擊 Settings > Public access
- 啟用公開存取並記下公開 URL
- 更新你的儲存設定:
storage: r2({
binding: "MEDIA",
publicUrl: "https://pub-xxx.r2.dev"
}),
Cloudflare Access 認證
如果你的組織使用 Cloudflare Access,你可以將其用作認證提供者來取代通行金鑰,透過現有的身分提供者實現單一登入。以下設定可以啟用它:
emdash({
database: d1({ binding: "DB" }),
storage: r2({ binding: "MEDIA" }),
auth: access({
teamDomain: "myteam.cloudflareaccess.com",
audience: "your-app-audience-tag",
roleMapping: {
"Admins": 50,
"Editors": 40,
},
}),
}),
有關所有設定選項,請參閱認證指南。
電子郵件
在 Workers 上,唯一的內建 email:deliver 處理器是一個開發主控台存根,因此
依賴電子郵件的流程 — 魔法連結登入、團隊邀請和留言
通知 — 在正式環境中會以 “Email is not configured” 失敗。
cloudflareEmail() 外掛透過
Cloudflare Email Sending
使用原生的 send_email Worker 綁定來傳送真實電子郵件,無需外部 API 金鑰。
1. 註冊傳送網域
在 Cloudflare 儀表板中,前往 Email 並驗證你傳送郵件的網域(或地址)。 Email Sending 會拒絕來自未驗證傳送者的訊息。
2. 新增綁定
在 wrangler.jsonc 中宣告一個 send_email 綁定:
{
"send_email": [{ "name": "EMAIL" }],
}
3. 註冊提供者
將外掛新增到你的 emdash() 整合中:
import { d1, r2 } from "@emdash-cms/cloudflare";
import { cloudflareEmail } from "@emdash-cms/cloudflare/plugins";
emdash({
database: d1({ binding: "DB" }),
storage: r2({ binding: "MEDIA" }),
plugins: [
cloudflareEmail({
from: { email: "[email protected]", name: "My Site CMS" },
replyTo: "[email protected]", // 選用
binding: "EMAIL", // 選用,預設為 "EMAIL"
}),
],
}),
4. 啟用並選擇
部署後,在 Admin → Extensions 下啟用外掛,並在 Settings → Email 下選擇它作為提供者。
選項
| 選項 | 型別 | 預設值 | 描述 |
|---|---|---|---|
from | string | { email, name? } | — (必需) | 已註冊 Email Sending 的網域上的傳送者地址。 |
replyTo | string | — | 選用的 Reply-To,當 from 是 no-reply 子網域地址時很有用。 |
binding | string | "EMAIL" | wrangler.jsonc 中 send_email 綁定的名稱。 |
環境變數
建議:加密金鑰
EMDASH_ENCRYPTION_KEY 是用於加密外掛密鑰的靜態儲存金鑰
(webhook 權杖、Turnstile 金鑰等)。金鑰在啟動時驗證;
外掛密鑰加密在啟用後使用它。在每次部署時設定它,
這樣密鑰無需後續設定變更即可得到保護。
金鑰由你提供,永遠不會儲存在資料庫中;只儲存 加密的密文。遺失它意味著遺失用它加密的每個密鑰。
使用以下命令產生金鑰並將其儲存為 Worker 密鑰:
npx emdash secrets generate
wrangler secret put EMDASH_ENCRYPTION_KEY
選用:穩定值覆寫
EmDash 自動產生預覽 HMAC 密鑰和留言者 IP 雜湊鹽, 並在首次使用時將它們持久化到資料庫中。以下環境變數 是你需要自行固定值的情況的覆寫 — 例如,當單獨行程中的預覽 Worker 需要與主網站共用密鑰時。
| 變數 | 用途 |
|---|---|
EMDASH_PREVIEW_SECRET | 自動產生的預覽 HMAC 密鑰的覆寫。 |
EMDASH_IP_SALT | 自動產生的留言者 IP 雜湊鹽的覆寫。 |
EMDASH_AUTH_SECRET | 選用。如果設定,將用作 IP 鹽來源(除非同時設定了 EMDASH_IP_SALT,後者優先),使已依賴它的安裝的留言者 IP 雜湊保持穩定。新部署請保持未設定。 |
使用 import.meta.env 或 Cloudflare 的 env 綁定在設定中存取環境變數。
有關 EmDash 使用的每個密鑰的完整清單 — 包括儲存位置、輪替步驟以及遺失金鑰時會發生什麼 — 請參閱密鑰與金鑰管理。
預覽部署
部署預覽分支:
wrangler deploy --env preview
在 wrangler.jsonc 中新增環境區段:
{
"env": {
"preview": {
"d1_databases": [
{
"binding": "DB",
"database_name": "emdash-db-preview",
},
],
},
},
}
疑難排解
”D1 binding not found”
驗證 wrangler.jsonc 中的綁定名稱與你的資料庫設定相符:
// 必須相符:d1({ binding: "DB" })
"binding": "DB"
“R2 binding not found”
檢查 R2 儲存桶是否正確綁定:
// 必須相符:r2({ binding: "MEDIA" })
"binding": "MEDIA"
遷移錯誤
如果你看到結構錯誤,追蹤 Worker 記錄(wrangler tail)並重現錯誤以擷取底層訊息 — 然後用該輸出提交 issue。