EmDash 支持多种数据库后端。根据你的部署目标选择。
概览
| 数据库 | 最适用于 | 部署 |
|---|---|---|
| D1 | Cloudflare Workers | 边缘,全球分布 |
| Hyperdrive | Cloudflare Workers 上的 PostgreSQL | 边缘,现有 Postgres |
| PostgreSQL | 生产 Node.js | 有 Postgres 的任何平台 |
| libSQL | 远程数据库 | 边缘或 Node.js |
| SQLite | Node.js,本地开发 | 单服务器 |
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.jsonc
{
"d1_databases": [
{
"binding": "DB",
"database_name": "emdash-db"
}
]
} wrangler.toml
[[d1_databases]]
binding = "DB"
database_name = "emdash-db" 只读副本
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 获得 read-your-writes 一致性。 |
"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 或创建扩展。EmDash 不执行 SET ROLE,因此不带继承的成员关系不够用。
大多数安装可以使用数据库的现有模式(通常是 public)。使用管理连接授予访问权限:
GRANT CONNECT ON DATABASE app TO emdash_app;
GRANT USAGE, CREATE ON SCHEMA public TO emdash_app;
EmDash 使用 PostgreSQL 的活动 current_schema()。它不创建模式或设置 search_path,因此请在部署前验证连接:
SELECT
current_database(),
session_user,
current_user,
current_schema(),
current_setting('search_path');
可选:使用专用模式
当 EmDash 与另一个应用共享数据库时,使用专用模式来隔离其对象:
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;
连接池
适配器内部使用 pg.Pool。根据部署调整池大小:
database: postgres({
connectionString: process.env.DATABASE_URL,
pool: { min: 2, max: 20 },
});
Hyperdrive
使用 hyperdrive() 适配器在 Cloudflare Workers 上运行 EmDash,后端使用现有的 PostgreSQL 数据库。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 的毫秒数 |
migrationConnectionStringEnv | string | CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_<BINDING> | 包含直接 PostgreSQL origin URL 的环境变量 |
max | number | 5 | Worker 内到 Hyperdrive 的连接池最大大小 |
*默认 60000 仅在设置了 cachedBinding 时适用;否则忽略。
从缓存提供匿名读取
默认完全禁用 Hyperdrive 缓存。但 使用 GET 或 HEAD 的匿名公开请求可以容忍短暂的过时窗口。如果可以接受该折衷,在同一数据库上运行两个 Hyperdrive 配置:一个缓存关闭(主 binding),一个缓存开启(cachedBinding)。EmDash 将匿名公开请求路由到缓存启用绑定,其他所有请求路由到无缓存主绑定。
这是 Cloudflare 记录的双配置模式。EmDash 按请求决定使用哪个绑定。
修复 PostgreSQL 混合所有权
如果站点使用了多个 PostgreSQL 用户,首先选择主 EmDash 连接将继续使用的标准角色。备份并在修复所有权期间停止模式更改。
检查活动模式中的每个表:
SELECT
n.nspname AS schema_name,
c.relname AS table_name,
pg_get_userbyid(c.relowner) AS owner
FROM pg_class AS c
JOIN pg_namespace AS n ON n.oid = c.relnamespace
WHERE n.nspname = current_schema()
AND c.relkind IN ('r', 'p')
ORDER BY c.relname;
使用模式限定名称转移不匹配的对象:
ALTER TABLE emdash.content_taxonomies OWNER TO emdash_app;
ALTER TABLE emdash.ec_posts OWNER TO emdash_app;
ALTER FUNCTION emdash.emdash_media_usage_capture_work() OWNER TO emdash_app;
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}` });
迁移
EmDash 默认为所有支持的方言自动运行核心迁移。Astro build 和 sync 还会输出经过验证的无密钥 .emdash/migrations.json,emdash migrate 可以在部署前应用。SQLite、libSQL、PostgreSQL、D1 和 Hyperdrive 后面的直接 PostgreSQL origin 都有部署执行器。
参见管理核心数据库迁移了解目标凭证、CI 序列化、auto/check/manual 运行时策略和恢复指导。
如果数据库为空(无集合)且设置向导未完成,EmDash 还会在首次启动时应用种子文件。种子从 .emdash/seed.json、package.json#emdash.seed 中的路径或 seed/seed.json 读取,并在编译时内联到构建中。
基于环境的配置
每个环境使用不同的数据库:
import { sqlite, libsql, postgres } from "emdash/db";
import { d1 } from "@emdash-cms/cloudflare";
const database = import.meta.env.PROD ? d1({ binding: "DB" }) : sqlite({ url: "file:./data.db" });
export default defineConfig({
integrations: [emdash({ database })],
});
选择也可以基于环境变量而非构建模式:
const database = process.env.DATABASE_URL
? postgres({ connectionString: process.env.DATABASE_URL })
: sqlite({ url: "file:./data.db" });