配置参考

本页内容

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",
	},
});

执行顺序如下:

  1. 外层中间件运行至 await next()。
  2. EmDash 初始化运行时与数据库,然后运行初始化、认证与请求上下文中间件。
  3. Astro 路由渲染。
  4. EmDash 应用响应变更,包括可视化编辑 HTML 以及安全/计时相关头。
  5. 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"],
		},
	},
});
选项类型描述
aggregatorUrlstring注册表服务的基础 URL。生产环境请使用 HTTPS。
acceptLabelersstring可选,逗号分隔的审核服务去中心化标识符(DID),表示请求接受的标注方。DID 是稳定的 Atmosphere 账户标识符。此设置不能覆盖注册表服务自身的策略。
policy.minimumReleaseAgestring | number暂缓比此年龄更新的发布。可为时长字符串("48h"、"7d")或秒数。
policy.minimumReleaseAgeExcludestring[]免于暂缓的发布者 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() 的选项:

选项类型默认值描述
teamDomainstring必填Cloudflare Access 团队域名
audiencestring—应用 Audience (AUD) 标签。在 Workers 上优先使用 audienceEnvVar。
audienceEnvVarstring"CF_ACCESS_AUDIENCE"运行时读取 audience 标签的环境变量
autoProvisionbooleantrue首次登录时创建 EmDash 用户
defaultRolenumber30未匹配 roleMapping 的用户的角色级别(见用户角色)
syncRolesbooleanfalse每次登录重新应用 roleMapping,而非仅在首次开通时
roleMappingobject—将 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",
	},
});
选项类型描述
logostring登录页与侧栏的 logo URL 或路径
siteNamestring侧栏与浏览器标题中显示的名称
faviconstring管理页面的 favicon URL 或路径

toolbar

可选。 控制编辑工具栏(公开页面上的浮动胶囊按钮)的交付方式。默认为 "server"。

值行为
"server"(默认)工具栏在服务端注入到为已认证编辑者渲染的每个 HTML 响应中。
"client"公开 HTML 对所有访客相同。小型引导脚本在已登录管理后台的浏览器中显示「编辑」胶囊;点击后验证会话并以 _edit 查询参数重新加载页面,该响应始终全新渲染(从不缓存)并含完整工具栏。
false从不渲染工具栏或引导脚本。
emdash({
	toolbar: "client",
})

当公开 HTML 经共享缓存提供(Cloudflare Cache Everything / Workers Cache、Fastly、Varnish 等)时使用 "client"。服务端注入时,若匿名访客先预热缓存,编辑者浏览公开站点会得到无工具栏的匿名缓存变体,工具栏随缓存状态出现或消失。客户端模式不向可共享 HTML 注入会话相关内容,缓存保持完全有效且工具栏可靠。

"client" 模式说明:

  • 未登录访客打开共享的 ?_edit URL 会重定向到规范 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 数据库。以下示例连接本地文件:

选项类型描述
urlstring带 file: 前缀的文件路径
sqlite({ url: "file:./data.db" });

libsql(config)

libSQL 数据库。以下示例连接远程 libSQL 数据库:

选项类型描述
urlstring数据库 URL
authTokenstring运行时认证令牌(本地文件可选)
migrationAuthTokenEnvstring迁移 token 变量名(默认 TURSO_AUTH_TOKEN)
libsql({
	url: process.env.LIBSQL_DATABASE_URL,
	authToken: process.env.LIBSQL_AUTH_TOKEN,
});

postgres(config)

带连接池的 PostgreSQL 数据库。

选项类型描述
connectionStringstringPostgreSQL 连接 URL
hoststring数据库主机
portnumber数据库端口
databasestring数据库名
userstring数据库用户
passwordstring数据库密码
sslboolean启用 SSL
pool.minnumber连接池最小大小(默认:0)
pool.maxnumber连接池最大大小(默认:10)
pool.connectionTimeoutMillisnumber最大连接等待(pg 默认:0,无超时)
pool.idleTimeoutMillisnumber空闲客户端生命周期(pg 默认:10,000 ms)
migrationConnectionStringEnvstring迁移连接字符串变量名(默认 DATABASE_URL)

以下示例使用连接字符串连接:

postgres({ connectionString: process.env.DATABASE_URL });

d1(config)

Cloudflare D1 数据库。从 @emdash-cms/cloudflare 导入。

选项类型默认值描述
bindingstring—wrangler.jsonc 中的 D1 绑定名
sessionstring"disabled"读复制模式:"disabled"、"auto" 或 "primary-first"
bookmarkCookiestring"__em_d1_bookmark"会话 bookmark 的 Cookie 名
coalescebooleanfalse在同一事件循环回合批量并发读;需要非 "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 导入此适配器。

选项类型默认值描述
bindingstring"HYPERDRIVE"禁用查询缓存的主 Hyperdrive 绑定
cachedBindingstring—可选的第二绑定,启用缓存,供匿名公开读
preferUncachedAfterWriteMsnumber60_000设置 cachedBinding 后,内容写入后公开读使用主库的时长
migrationConnectionStringEnvstring派自主绑定含 emdash migrate 所用直连 PostgreSQL URL 的环境变量
maxnumber5单个 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 导入此适配器。

选项类型默认值描述
bindingstring必填EmDashDB 类的 Durable Object 命名空间绑定
namestring"emdash"单例对象名;仅在为同一绑定隔离多个数据库时更改
sessionstring"disabled""auto" 将匿名读路由到副本,写路由到主库
bookmarkCookiestring"__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)

本地文件系统存储。以下示例从本地目录提供上传文件:

选项类型描述
directorystring目录路径
baseUrlstring提供文件服务的基础 URL
local({
	directory: "./uploads",
	baseUrl: "/_emdash/api/media/file",
});

r2(config)

Cloudflare R2 绑定。以下示例使用带公开 URL 的 R2 绑定:

选项类型描述
bindingstringR2 绑定名
publicUrlstring可选公开 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 兼容存储。

选项类型描述
endpointstringS3 端点 URL(S3_ENDPOINT)
bucketstring存储桶名(S3_BUCKET)
accessKeyIdstring访问密钥(S3_ACCESS_KEY_ID)
secretAccessKeystring秘密密钥(S3_SECRET_ACCESS_KEY)
regionstring区域,默认 "auto"(S3_REGION)
publicUrlstring可选 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,用于浏览、上传、删除与交付图片资源。

选项类型默认值描述
accountIdstring来自 CF_ACCOUNT_IDCloudflare 账户 ID
accountIdEnvVarstring"CF_ACCOUNT_ID"省略 accountId 时使用的变量
accountHashstring来自 CF_IMAGES_ACCOUNT_HASH交付 URL 中使用的账户哈希
accountHashEnvVarstring"CF_IMAGES_ACCOUNT_HASH"省略 accountHash 时使用的变量
apiTokenstring来自 CF_IMAGES_TOKEN具备 Cloudflare Images 读写权限的令牌
apiTokenEnvVarstring"CF_IMAGES_TOKEN"省略 apiToken 时使用的变量
deliveryDomainstringimagedelivery.net自定义图片交付主机名
defaultVariantstring"public"用于展示的图片变体
mediaProviders: [cloudflareImages({ defaultVariant: "public" })];

cloudflareStream(config)

添加 Cloudflare Stream,用于浏览、搜索、上传、删除与播放视频资源。

选项类型默认值描述
accountIdstring来自 CF_ACCOUNT_IDCloudflare 账户 ID
accountIdEnvVarstring"CF_ACCOUNT_ID"省略 accountId 时使用的变量
apiTokenstring来自 CF_STREAM_TOKEN具备 Cloudflare Stream 读写权限的令牌
apiTokenEnvVarstring"CF_STREAM_TOKEN"省略 apiToken 时使用的变量
customerSubdomainstringCloudflare 默认值自定义 Stream 交付主机名
controlsbooleantrue显示播放器控件
autoplaybooleanfalse自动开始播放
loopbooleanfalse循环播放
mutedbooleanfalse,或在 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_KEYCloudflare 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用于展示的模板名称
schemaemdash init 读取的可选 SQL 架构文件
seedseed 数据 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