EmDash 通过两个文件进行配置:用于集成的 astro.config.mjs 和用于内容集合的 src/live.config.ts。
Astro 集成
将 EmDash 配置为 Astro 集成:
import { defineConfig } from "astro/config";
import emdash, { local, r2, s3 } from "emdash/astro";
import { sqlite, libsql, d1 } 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" });
详情参见数据库选项。
storage
必需。 媒体存储适配器配置。
// 本地文件系统(开发环境)
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
});
// R2 绑定(Cloudflare Workers)
storage: r2({
binding: "MEDIA",
publicUrl: "https://pub-xxxx.r2.dev", // optional
});
// S3-compatible (any platform) — all fields from S3_* environment variables
storage: s3()
// Or with explicit values
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", // optional, default: "auto"
publicUrl: "https://cdn.example.com", // optional
});
详情参见存储选项。
plugins
可选。 EmDash 插件数组。
import seoPlugin from "@emdash-cms/plugin-seo";
plugins: [seoPlugin()];
auth
可选。 认证配置。
auth: {
// 自助注册配置
selfSignup: {
domains: ["example.com"],
defaultRole: 20, // 贡献者
},
// OAuth 提供商
oauth: {
github: {
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
},
google: {
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
},
},
// 会话配置
session: {
maxAge: 30 * 24 * 60 * 60, // 30 天
sliding: true, // 活动时重置过期时间
},
// 或使用 Cloudflare Access(排他模式)
cloudflareAccess: {
teamDomain: "myteam.cloudflareaccess.com",
audience: "your-app-audience-tag",
autoProvision: true,
defaultRole: 30,
syncRoles: false,
roleMapping: {
"Admins": 50,
"Editors": 40,
},
},
}
auth.selfSignup
如果用户的邮箱域名在允许列表中,则允许用户自助注册。
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
domains | string[] | [] | 允许的邮箱域名 |
defaultRole | number | 20 | 自助注册的角色 |
selfSignup: {
domains: ["example.com", "acme.org"],
defaultRole: 20, // 贡献者
}
auth.oauth
配置 OAuth 登录提供商。
oauth: {
github: {
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
},
google: {
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
},
}
auth.session
会话配置。
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
maxAge | number | 2592000(30 天) | 会话生命周期(秒) |
sliding | boolean | true | 活动时重置过期时间 |
auth.cloudflareAccess
使用 Cloudflare Access 作为认证提供商,替代通行密钥。
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
teamDomain | string | 必填 | 你的 Access 团队域名 |
audience | string | 必填 | 应用程序 Audience (AUD) 标签 |
autoProvision | boolean | true | 首次登录时创建用户 |
defaultRole | number | 30 | 新用户的默认角色 |
syncRoles | boolean | false | 每次登录时更新角色 |
roleMapping | object | — | 将 IdP 组映射到角色 |
siteUrl
可选。 站点面向浏览器的公共源(协议 + 主机 + 可选端口,不含路径)。
在 TLS 终止反向代理后面,Astro.url 返回内部地址(http://localhost:4321)而非公共地址(https://cms.example.com)。这会导致通行密钥、CSRF 源匹配、OAuth 重定向、登录重定向、MCP 发现、快照导出、sitemap、robots.txt 和 JSON-LD 结构化数据出错。设置 siteUrl 即可一次性修复所有这些问题。
集成在加载时会验证此值:它必须是带有 http: 或 https: 协议的有效 URL,并被标准化为 origin(路径会被去除)。
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 在运行时设置的容器部署非常有用。
反向代理设置
Astro 仅在允许公共主机时才反映 X-Forwarded-*。为用户访问的主机名(和协议)配置 security.allowedDomains。在 astro dev 中,添加匹配的 vite.server.allowedHosts 使 Vite 接受代理的 Host 头。
优先修复 allowedDomains(和转发的头信息);当重建的 URL 仍然偏离浏览器源时(通常是 TLS 在前面终止而上游请求保持 http:// 的情况),使用 siteUrl。
在有前端 TLS 的情况下,将开发服务器绑定到回环地址(astro dev --host 127.0.0.1)通常就够了:代理在本地连接,而 siteUrl 匹配公共 HTTPS 源。
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",
}),
],
});
数据库适配器
从 emdash/db 导入:
import { sqlite, libsql, postgres, d1 } from "emdash/db";
sqlite(config)
使用 better-sqlite3 的 SQLite 数据库。
| 选项 | 类型 | 描述 |
|---|---|---|
url | string | 带有 file: 前缀的文件路径 |
sqlite({ url: "file:./data.db" });
libsql(config)
libSQL 数据库。
| 选项 | 类型 | 描述 |
|---|---|---|
url | string | 数据库 URL |
authToken | string | 认证令牌(本地文件可选) |
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) |
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 | "__ec_d1_bookmark" | 会话书签的 Cookie 名称 |
// 基础用法
d1({ binding: "DB" });
// 使用只读副本
d1({ binding: "DB", session: "auto" });
当 session 为 "auto" 或 "primary-first" 时,EmDash 使用 D1 Sessions API 将读取查询路由到附近的副本。已认证用户可获得基于书签的”读己所写”一致性。详情参见数据库选项 — 只读副本。
存储适配器
从 emdash/astro 导入:
import emdash, { local, r2, s3 } from "emdash/astro";
local(config)
本地文件系统存储。
| 选项 | 类型 | 描述 |
|---|---|---|
directory | string | 目录路径 |
baseUrl | string | 提供文件的基础 URL |
local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
});
r2(config)
Cloudflare R2 绑定。
| 选项 | 类型 | 描述 |
|---|---|---|
binding | string | R2 绑定名称 |
publicUrl | string | 可选的公共 URL |
r2({
binding: "MEDIA",
publicUrl: "https://pub-xxxx.r2.dev",
});
s3(config?)
S3 兼容存储。所有配置字段都是可选的:s3({...}) 中省略的字段会在 Node 进程启动时从对应的 S3_* 环境变量解析。显式值始终优先。
前提条件: 在你的项目中安装 @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) |
// All fields from S3_* environment variables (Node container deployments)
s3()
// Mix: CDN from config, rest from environment
s3({ publicUrl: "https://cdn.example.com" })
// All explicit (unchanged from before)
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 handler 的 env 参数暴露,而非 process.env,因此 S3_* 环境变量不会被读取。Workers 部署应使用 r2(config) 适配器,或向 s3({...}) 传入显式值。详情参见存储选项。
Live Collections
在 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_DATABASE_URL | 覆盖数据库 URL |
EMDASH_AUTH_SECRET | 通行密钥认证的密钥 |
EMDASH_PREVIEW_SECRET | 预览令牌生成的密钥 |
EMDASH_URL | 用于模式同步的远程 EmDash URL |
使用以下命令生成认证密钥:
npx emdash auth secret
package.json 配置
package.json 中的可选配置:
{
"emdash": {
"label": "My Blog Template",
"description": "A clean, minimal blog template",
"seed": ".emdash/seed.json",
"url": "https://my-site.pages.dev",
"preview": "https://emdash-blog.pages.dev"
}
}
| 选项 | 描述 |
|---|---|
label | 用于显示的模板名称 |
description | 模板描述 |
seed | 种子 JSON 文件路径 |
url | 用于模式同步的远程 URL |
preview | 模板预览的演示站点 URL |
TypeScript 配置
EmDash 在 .emdash/types.ts 中生成类型。添加到 tsconfig.json:
{
"compilerOptions": {
"paths": {
"@emdash-cms/types": ["./.emdash/types.ts"]
}
}
}
使用以下命令生成类型:
npx emdash types