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