為每個部署選擇一個資料庫配接器。資料庫保存內容模型、條目、使用者、設定和外掛資料。媒體二進位檔案屬於單獨的儲存後端。
概述
| 資料庫 | 使用場景 | 執行環境 |
|---|---|---|
| SQLite | 一個 Node.js 程序擁有持久化磁碟 | Node.js 或本機開發 |
| D1 | 站點在 Cloudflare Workers 上執行且應使用 Cloudflare SQL | Cloudflare Workers |
| Hyperdrive | 站點在 Workers 上執行且必須使用現有 PostgreSQL 來源 | Cloudflare Workers |
| PostgreSQL | 多個 Node.js 程序需要一個共用資料庫 | Node.js |
| libSQL | Node.js 部署需要遠端 SQLite 相容資料庫 | Node.js |
D1 是 Cloudflare 範本的預設選項。SQLite 是最簡單的 Node.js 選項,但需要一個可寫的持久卷和營運資料庫備份。
SQLite
SQLite 使用 Node.js 內建的資料庫驅動程式,是 Node.js 部署最簡單的選項。
import { sqlite } from "emdash/db";
export default defineConfig({
integrations: [
emdash({
database: sqlite({ url: "file:./data.db" }),
}),
],
});
設定
| 選項 | 型別 | 描述 |
|---|---|---|
url | string | 帶 file: 前綴的檔案路徑 |
檔案路徑
url 必須以 file: 開頭:
// 相對路徑
database: sqlite({ url: "file:./data/emdash.db" });
// 絕對路徑
database: sqlite({ url: "file:/var/data/emdash.db" });
// 從環境變數
database: sqlite({ url: `file:${process.env.DATABASE_PATH}` });
Cloudflare D1
D1 是 Cloudflare 的無伺服器 SQLite 資料庫。在部署到 Cloudflare Workers 時使用。
import { d1 } from "@emdash-cms/cloudflare";
export default defineConfig({
integrations: [
emdash({
database: d1({ binding: "DB" }),
}),
],
});
設定
| 選項 | 型別 | 預設值 | 描述 |
|---|---|---|---|
binding | string | — | wrangler.jsonc 中的 D1 繫結名稱 |
session | string | "disabled" | 讀取複寫模式(見下文) |
bookmarkCookie | string | "__em_d1_bookmark" | 工作階段書籤的 Cookie 名稱 |
Wrangler 繫結
wrangler.jsonc
{
"d1_databases": [
{
"binding": "DB",
"database_name": "emdash-db"
}
]
} wrangler.toml
[[d1_databases]]
binding = "DB"
database_name = "emdash-db" Wrangler 可以在部署期間從此繫結佈建缺失的 D1 資料庫。EmDash 遷移是單獨的步驟。完整的繫結集請參見部署到 Cloudflare,遷移操作手冊請參見管理核心資料庫遷移。
讀取副本
D1 支援讀取複寫以降低全球分佈站點的讀取延遲。啟用後,讀取查詢會路由到附近的副本,而非始終存取主資料庫。
EmDash 使用 D1 Sessions API 來透明管理此功能。通過 session 選項啟用:
import { d1 } from "@emdash-cms/cloudflare";
export default defineConfig({
integrations: [
emdash({
database: d1({
binding: "DB",
session: "auto",
}),
}),
],
});
工作階段模式
| 模式 | 行為 |
|---|---|
"disabled" | 無工作階段。所有查詢傳送到主庫。預設值。 |
"auto" | 匿名請求從最近的副本讀取。經過驗證的使用者透過書籤 Cookie 取得寫後讀一致性。 |
"primary-first" | 類似 "auto",但第一個查詢始終傳送到主庫。用於寫入非常頻繁的站點。 |
運作方式
- 匿名訪客 取得
first-unconstrained— 讀取從最近的副本進行以取得最低延遲。由於匿名使用者從不寫入,因此不需要一致性保證。 - 經過驗證的使用者(編輯者、作者)取得基於書籤的工作階段。寫入後,書籤 Cookie 確保下一個請求至少看到該狀態。
- 寫入請求(
POST、PUT、DELETE)始終從主資料庫開始。 - 建置時查詢(Astro Content Collections)完全繞過工作階段並直接使用主庫。
libSQL
libSQL 是 SQLite 的一個分支,支援遠端連線。當你需要遠端資料庫但不使用 Cloudflare D1 時使用。
import { libsql } from "emdash/db";
export default defineConfig({
integrations: [
emdash({
database: libsql({
url: process.env.LIBSQL_DATABASE_URL,
authToken: process.env.LIBSQL_AUTH_TOKEN,
}),
}),
],
});
設定
| 選項 | 型別 | 描述 |
|---|---|---|
url | string | 資料庫 URL(libsql://... 或 file:...) |
authToken | string | 遠端資料庫的執行時驗證權杖(本機可選) |
migrationAuthTokenEnv | string | 遷移權杖變數名(預設 TURSO_AUTH_TOKEN) |
本機開發
開發期間使用本機 libSQL 檔案:
database: libsql({ url: "file:./data.db" });
PostgreSQL
PostgreSQL 支援需要完整關聯式資料庫的 Node.js 部署。
import { postgres } from "emdash/db";
export default defineConfig({
integrations: [
emdash({
database: postgres({
connectionString: process.env.DATABASE_URL,
}),
}),
],
});
設定
你可以使用連線字串或單獨的參數進行連線:
// 連線字串
database: postgres({
connectionString: "postgres://user:password@localhost:5432/emdash",
});
// 單獨參數
database: postgres({
host: "localhost",
port: 5432,
database: "emdash",
user: "emdash",
password: process.env.DB_PASSWORD,
ssl: true,
});
| 選項 | 型別 | 描述 |
|---|---|---|
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) |
將 pool.connectionTimeoutMillis 設為非零值以限制當 PostgreSQL 不可達或沒有可用池化連線時請求的等待時間。將 pool.idleTimeoutMillis 設為 0 以保持閒置用戶端開放直到池關閉。省略任一選項將保留 pg 預設值。
資料庫角色要求
EmDash 建立和更新自己的 PostgreSQL 表。核心遷移建立和修改系統表和集合表,內容型別建立 ec_* 表,新增或移除欄位會修改其集合表。因此,配置的 PostgreSQL 角色需要在站點的整個生命週期內具有架構權限,而不僅僅是在初始設定期間。
為 EmDash 使用一個正規角色。它需要:
- 資料庫的
CONNECT; - 活動架構的
USAGE和CREATE; - 每個 EmDash 表和函式的擁有權,直接或透過對擁有角色的
INHERIT成員資格;以及 - 這些表上的
SELECT、INSERT、UPDATE和DELETE。
它不需要是超級使用者,不需要 CREATEDB 或 CREATEROLE,也不需要建立擴充功能。PostgreSQL 不提供表的 ALTER 或 DROP 授權:這些操作屬於物件擁有者和繼承其權限的角色。向不同角色授予表的 ALL 不會使該角色成為擁有者。EmDash 不執行 SET ROLE,因此沒有繼承的成員資格是不夠的。
大多數安裝可以使用資料庫的現有架構,通常是 public。當資料庫專用於 EmDash 時,這是最簡單的選項。在下面的範例中,emdash_app 是 EmDash 連線字串中的登入角色;使用現有的提供者角色或建立專用登入。使用管理連線授予存取權限,替換你的資料庫、架構和角色名稱:
GRANT CONNECT ON DATABASE app TO emdash_app;
GRANT USAGE, CREATE ON SCHEMA public TO emdash_app;
這些授權允許角色建立新物件。它們不會變更現有表的擁有者;當現有站點有混合擁有者時,使用 PostgreSQL 擁有權修復操作手冊。
EmDash 使用 PostgreSQL 的活動 current_schema()。它不建立架構也不設定 search_path,因此在部署前驗證連線:
SELECT
current_database(),
session_user,
current_user,
current_schema(),
current_setting('search_path');
可選:使用專用架構
當 EmDash 與另一個應用程式共用資料庫,或者你想將其物件從 public 中隔離時,使用專用架構。這是可選的,在 EmDash 首次設定之前配置最為簡單。專用於 EmDash 的資料庫不需要單獨的架構。
假設正規角色 emdash_app 已存在,使用管理連線建立並選擇其架構:
GRANT CONNECT ON DATABASE app TO emdash_app;
CREATE SCHEMA emdash AUTHORIZATION emdash_app;
ALTER ROLE emdash_app IN DATABASE app SET search_path = emdash;
這不會從 public 移動現有安裝,也不會修復混合擁有權。現有站點應保持其目前架構,改為使用 PostgreSQL 擁有權修復操作手冊。
連線池
配接器使用 pg.Pool。根據你的部署調整池大小:
database: postgres({
connectionString: process.env.DATABASE_URL,
pool: { min: 2, max: 20 },
});
Hyperdrive
使用 hyperdrive() 配接器在 Cloudflare Workers 上執行 EmDash,後端使用現有的 PostgreSQL — 或 Postgres 相容(如 PlanetScale Postgres)— 資料庫。Hyperdrive 透過 Cloudflare 網路池化和加速連線;EmDash 的 PostgreSQL 方言執行查詢。
import { hyperdrive, r2 } from "@emdash-cms/cloudflare";
export default defineConfig({
integrations: [
emdash({
database: hyperdrive({ binding: "HYPERDRIVE" }),
storage: r2({ binding: "MEDIA" }),
}),
],
});
要求
- 站點中安裝
pg >= 8.16.3(pnpm add pg) compatibility_flags: ["nodejs_compat"]compatibility_date >= "2024-09-23"
設定
首先準備 PostgreSQL 角色。然後使用該角色的連線字串建立 Hyperdrive 設定,並將繫結新增到 Wrangler 設定中:
wrangler hyperdrive create emdash-db \
--connection-string "postgres://user:password@host/db?sslmode=verify-full" \
--caching-disabled
wrangler.jsonc
{
"hyperdrive": [
{
"binding": "HYPERDRIVE",
"id": "<your-hyperdrive-id>"
}
]
} wrangler.toml
[[hyperdrive]]
binding = "HYPERDRIVE"
id = "<your-hyperdrive-id>" 設定
| 選項 | 型別 | 預設值 | 描述 |
|---|---|---|---|
binding | string | "HYPERDRIVE" | 主(快取停用)Hyperdrive 繫結名稱 |
cachedBinding | string | — | 可選的快取啟用繫結用於匿名讀取(見下文) |
preferUncachedAfterWriteMs | number | 60000* | 內容發佈後,在匿名公開讀取中優先使用 binding 持續該時間(ms)(與 Hyperdrive max_age 匹配) |
migrationConnectionStringEnv | string | CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_<BINDING> | 包含 emdash migrate 直接 PostgreSQL 來源 URL 的環境變數 |
max | number | 5 | Worker 內到 Hyperdrive 連線池的最大大小 |
*預設值 60000 僅在設定了 cachedBinding 時適用;否則忽略。
從快取提供匿名讀取
預設情況下完全停用 Hyperdrive 快取,因為管理面板和寫入需要寫後讀一致性。但 使用 GET 或 HEAD 的匿名公開請求可以容忍短暫的過期視窗。如果這個權衡可以接受,在同一個資料庫上執行兩個 Hyperdrive 設定:一個停用快取(主 binding)和一個啟用快取(cachedBinding)。EmDash 透過啟用快取的繫結路由這些匿名公開請求,透過未快取的主繫結路由其他所有請求。
# 主 — 快取關閉(用於管理、驗證請求、寫入、遷移)
wrangler hyperdrive create emdash-db \
--connection-string "postgres://user:password@host/db?sslmode=verify-full" \
--caching-disabled
# 快取 — 相同的資料庫角色和連線字串,快取開啟
wrangler hyperdrive create emdash-db-cached \
--connection-string "postgres://user:password@host/db?sslmode=verify-full"
{
"hyperdrive": [
{ "binding": "HYPERDRIVE", "id": "<快取停用 ID>" },
{ "binding": "HYPERDRIVE_CACHED", "id": "<快取啟用 ID>" }
]
}
database: hyperdrive({ binding: "HYPERDRIVE", cachedBinding: "HYPERDRIVE_CACHED" });
這是 Cloudflare 為快取記錄的雙設定模式。EmDash 按請求決定使用哪個繫結:
- 公開路徑的匿名讀取(
GET/HEAD,無工作階段,不在/_emdash下)→ 快取啟用的cachedBinding,除了內容發佈後的短暫視窗(預設 60s;將preferUncachedAfterWriteMs設為 Hyperdrivemax_age),此時 EmDash 優先使用未快取的binding,以防重建使用仍然過期的 Hyperdrive 結果重新填充邊緣/物件快取。 - 驗證請求(編輯者、作者)→ 未快取的
binding。 - 變更請求(
POST、PUT、PATCH、DELETE,包括匿名)→ 未快取的binding。 /_emdash下的任何請求(管理、設定、驗證、內部 API),即使是匿名GET→ 未快取的binding。- 執行時遷移和冷啟動 → 始終使用主
binding。 - 部署管理的遷移 → 使用
migrationConnectionStringEnv直接連線到 PostgreSQL 來源;從不使用任何 Hyperdrive 繫結。
可選:使用單獨的快取角色
遷移、設定、驗證請求和明確寫入請求始終使用主 binding。cachedBinding 的單獨角色不需要架構擁有權或 CREATE,但需要 CONNECT、架構 USAGE 和公開站點使用的每個表上的 SELECT。
匿名公開 GET 和 HEAD 請求還可以記錄重新導向命中和 404。要保留這些功能,快取角色還需要 _emdash_redirects 上的 UPDATE 和 _emdash_404_log 上的 SELECT、INSERT、UPDATE 和 DELETE。在公開 GET 或 HEAD 期間寫入的外掛或應用程式程式碼可能需要更多。除非你已使用受限快取角色測試了站點,否則對兩個繫結使用相同的角色。
在 EmDash 完成初始遷移後新增快取角色。以下範例使用可選的 emdash 架構;替換你的活動架構,如 public。使用你提供者的管理角色建立登入和資料庫設定:
CREATE ROLE emdash_cached LOGIN PASSWORD '替換為密鑰';
GRANT CONNECT ON DATABASE app TO emdash_cached;
ALTER ROLE emdash_cached IN DATABASE app SET search_path = emdash;
然後以架構和表的擁有者 emdash_app 連線,授予對現有和未來表的存取權限:
GRANT USAGE ON SCHEMA emdash TO emdash_cached;
GRANT SELECT ON ALL TABLES IN SCHEMA emdash TO emdash_cached;
GRANT UPDATE ON emdash._emdash_redirects TO emdash_cached;
GRANT SELECT, INSERT, UPDATE, DELETE ON emdash._emdash_404_log TO emdash_cached;
ALTER DEFAULT PRIVILEGES IN SCHEMA emdash
GRANT SELECT ON TABLES TO emdash_cached;
使用兩個角色連線並驗證它們報告相同的 current_database() 和 current_schema(),然後再啟用 cachedBinding。在共用架構上,GRANT SELECT ON ALL TABLES 也會暴露不相關的表。改為授予對單個 EmDash 表的存取權限,並在新增集合或其他架構物件時更新這些授權。
核心遷移
EmDash 預設為每個支援的方言自動執行核心遷移。Astro 建置和同步還會輸出經過驗證的、不含密鑰的 .emdash/migrations.json,emdash migrate 可以在部署前套用。SQLite、libSQL、PostgreSQL、D1 和 Hyperdrive 背後的直接 PostgreSQL 來源都有部署執行器。
有關目標憑證、CI 序列化、auto/check/manual 執行時策略以及從未知記錄或模糊 D1 寫入中復原,請參見管理核心資料庫遷移。
對於 PostgreSQL,執行時遷移透過配置的連線執行;Hyperdrive 執行時遷移始終使用主繫結。部署管理的 Hyperdrive 遷移直接連線到 PostgreSQL 來源。核心遷移可以建立表、索引和函式,修改或刪除欄和約束,以及更新現有列。能夠連線和修改列但不擁有現有 EmDash 物件的角色是不夠的。設定精靈無法修復缺失的資料庫權限,因為執行時遷移在設定之前執行。
如果資料庫為空(無集合)且設定精靈未完成,EmDash 還會在首次啟動時套用種子檔案。種子從 .emdash/seed.json、package.json#emdash.seed 中的路徑或 seed/seed.json 讀取 — 以最先找到的為準 — 並在編譯時內聯到建置中。如果都不存在,則使用內建的預設種子。後續針對現有資料庫的啟動不會修改其內容。
為不同環境使用不同的資料庫
為開發、預覽、暫存和正式環境分別提供自己的資料庫。指向正式環境的預覽部署可能會對正式資料執行核心遷移或破壞性內容模型命令。
對於 Cloudflare,在對應的 Wrangler 環境下定義每個 D1 或 Hyperdrive 繫結,並向 Wrangler 命令傳遞 --env。對於 Node.js,為每個執行時環境注入不同的資料庫 URL。將憑證保存在執行時密鑰中,而不是 astro.config.mjs 中。