- CMS
- 后端
- 前端
- 插件系统
【免费下载链接】emdash
EmDash is a full-stack TypeScript CMS based on Astro; the spiritual successor to WordPress
本文以 EmDash 官方构建指南中的 Configuration 章节为骨架,系统讲解基于 Astro 的全栈 TypeScript CMS —— EmDash 的站点配置体系。你将掌握
astro.config.mjs中数据库、存储、站点 URL、插件的完整配置方法,理解 Node.js 自托管与 Cloudflare D1/R2 两种部署形态的差异,学会通过live.config.ts注册内容集合、用npx emdash types生成类型,并了解反向代理场景下siteUrl与security.allowedDomains的配合原理。文中所有结论均对照当前仓库源码(packages/core/src)给出依据。
配置文件的整体脉络
一个 EmDash 站点的配置由四类文件协同完成:
| 文件 | 职责 | 说明 |
|---|---|---|
astro.config.mjs | 注册emdash()集成 | 数据库、存储、插件、站点 URL 等核心配置入口 |
src/live.config.ts | 注册内容集合 | 每站必有的样板文件,通过emdashLoader()暴露所有内容类型 |
emdash-env.d.ts | 内容类型声明 | 开发服务器启动时自动生成,为集合提供 TypeScript 类型 |
package.json | 依赖声明 | 区分 Node.js 与 Cloudflare 两种部署形态 |
其中astro.config.mjs中的emdash()集成是整个系统的枢纽。从源码看,它会在 Astro 的astro:config:setup阶段完成一系列注入工作:注入/_emdash/admin管理后台路由与/_emdash/api/*REST 接口、配置中间件链(middleware)、覆写图片端点以支持存储直读,并自动为@astrojs/react缺失场景输出告警(参见 packages/core/src/astro/integration/index.ts)。如果你没有在integrations中注册@astrojs/react(),管理后台会卡在 "Loading EmDash..." 界面而无法水合,构建时集成会打印明确的警告信息。
astro.config.mjs:Node.js 本地开发与自托管
本地开发或 Node.js 自托管是最简单的形态。官方推荐的完整配置如下:
import node from "@astrojs/node"; import react from "@astrojs/react"; import { defineConfig } from "astro/config"; import emdash, { local } from "emdash/astro"; import { sqlite } from "emdash/db"; export default defineConfig({ output: "server", adapter: node({ mode: "standalone" }), image: { layout: "constrained", responsiveStyles: true, }, integrations: [ react(), emdash({ database: sqlite({ url: "file:./data.db" }), storage: local({ directory: "./uploads", baseUrl: "/_emdash/api/media/file", }), }), ], devToolbar: { enabled: false }, });几个关键点需要说明:
output: "server"与 Node adapter:EmDash 是服务端渲染 CMS,必须配合@astrojs/node的standalone模式运行,构建产物位于dist/server/entry.mjs,可通过node ./dist/server/entry.mjs启动(见 templates/blank/package.json 中的start脚本)。database: sqlite({ url }):使用 Node 内置node:sqlite方言,本地开发与 Node 部署均适用。从 packages/core/src/db/adapters.ts 的实现看,sqlite()返回一个可序列化的 descriptor,运行期再按需加载emdash/db/sqlite入口;注意该方言要求 Node.js 22.16 及以上版本。除 SQLite 外,同一文件还提供libsql({ url, authToken })(Turso)与postgres({ connectionString })两种适配器,可按部署目标替换。storage: local({ directory, baseUrl }):本地文件系统存储,用于开发与测试,不支持签名上传 URL。baseUrl指向媒体代理路由/_emdash/api/media/file。local()与s3()(兼容 AWS S3 / R2 S3 API / MinIO)都定义在 packages/core/src/astro/storage/adapters.ts 中。- 默认值:即使不配置
storage,集成也会使用默认的本地存储(目录./.emdash/uploads,baseUrl 同上),见 packages/core/src/astro/integration/index.ts。 - 图片配置:
image.layout: "constrained"配合responsiveStyles: true启用响应式图片。集成会自动向 Astro 的image.remotePatterns注入存储公开 URL 与本站点源,并把图片端点替换为存储直读的包装端点,使媒体字节不经 HTTP 回源即可优化(Cloudflare Access 场景下尤其关键)。如果通过images: false显式关闭,媒体将退化为普通<img>渲染。
可选配置项补充
在emdash({ ... })中还可以追加以下常用项(类型定义见 packages/core/src/astro/integration/runtime.ts):
maxUploadSize:媒体上传上限(字节),默认 52,428,800(50 MB),如emdash({ maxUploadSize: 100 * 1024 * 1024 })。migrations:数据库迁移策略,默认auto,运行期自动执行待应用迁移。fonts:管理后台字体,默认注入 Noto Sans(构建时下载自托管),可通过{ scripts: ["arabic", "japanese"] }扩展脚本覆盖,或设false完全禁用。admin:后台白标配置,可覆写 logo、站点名与 favicon。toolbar:公开页编辑器工具栏投递方式,"server"(默认)/"client"/false。objectCache:可选的对象缓存后端(内存或 Cloudflare KV),用于缓解 D1/SQLite 读压力。
反向代理场景:allowedDomains 与 siteUrl
当站点位于 TLS 终结的反向代理之后时,Astro.url拿到的是内部地址(如http://localhost:4321)而非公网地址(如https://mysite.example.com)。这会导致 Passkey(WebAuthn)、CSRF、OAuth、重定向等机制全部失效,因为这些安全流程都以浏览器地址栏里的 origin 为准。
第一步:声明允许的公网主机名。通过 Astro 的security.allowedDomains让 Astro 依据X-Forwarded-*请求头重建 URL:
export default defineConfig({ // ... security: { allowedDomains: ["mysite.example.com"], }, });在开发模式下,还需同步配置vite.server.allowedHosts,否则 Vite 会拒绝代理转发过来的Host头。
源码细节:当你在
emdash()配置了siteUrl时,集成会自动向security.allowedDomains注入对应 hostname(见 packages/core/src/astro/integration/index.ts);同时会把 Astro 内置的security.checkOrigin关闭,因为 EmDash 的 CSRF 层(checkPublicCsrf)支持双 origin 校验——同时接受内部 origin 与getPublicOrigin()解析出的公网 origin,从而兼容 Docker 等“域名在容器启动时才可知”的部署方式。
第二步:设置siteUrl。如果重建出的 URL 与浏览器仍不一致(TLS 终结场景很常见),直接在集成中显式声明:
emdash({ siteUrl: "https://mysite.example.com", // ... });也可以使用环境变量注入(容器部署特别方便):
EMDASH_SITE_URL=https://mysite.example.com # or: SITE_URL=https://mysite.example.comsiteUrl取代了旧的passkeyPublicOrigin(后者只修复 Passkey 一个问题),现在它统一作用于:Passkey、CSRF origin 匹配、OAuth 重定向、登录重定向、MCP 发现、快照导出、sitemap、robots.txt 以及 JSON-LD 结构化数据。
从 packages/core/src/astro/integration/index.ts 的实现可以看到两点约束:
siteUrl必须是http或https协议,否则启动即报错;- 它会被规范化为origin 形态(不含路径)存储,这是安全不变量 L-1 的一部分。
环境变量回退的优先级为EMDASH_SITE_URL>SITE_URL> 请求 URL 的 origin(运行期由getPublicOrigin()解析,见 packages/core/src/api/site-url.ts)。正因为回退发生在运行期而非构建期,Docker 镜像可以在构建时不含域名、启动时再通过环境变量注入。
推荐做法:TLS 在前端终结时,用astro dev --host 127.0.0.1(回环地址)启动开发服务器通常就足够——代理能在本地访问到 dev server,而siteUrl与浏览器中的 HTTPS origin 保持一致,无需把 Node 端口暴露到局域网。
Cloudflare 部署:D1 + R2
在 Cloudflare Workers 上运行需要替换数据库与存储适配器,并配置wrangler.jsonc绑定:
import cloudflare from "@astrojs/cloudflare"; import react from "@astrojs/react"; import { d1, r2 } from "@emdash-cms/cloudflare"; import { defineConfig } from "astro/config"; import emdash from "emdash/astro"; export default defineConfig({ output: "server", adapter: cloudflare(), image: { layout: "constrained", responsiveStyles: true, }, integrations: [ react(), emdash({ database: d1({ binding: "DB", session: "auto" }), storage: r2({ binding: "MEDIA" }), }), ], devToolbar: { enabled: false }, });对应wrangler.jsonc必须声明 D1 数据库与 R2 桶的绑定:
{ "name": "my-site", "compatibility_date": "2026-02-24", "compatibility_flags": ["nodejs_compat"], "assets": { "directory": "./dist" }, "d1_databases": [ { "binding": "DB", "database_name": "my-site", }, ], "r2_buckets": [ { "binding": "MEDIA", "bucket_name": "my-site-media", }, ], }需要留意的差异点:
d1({ binding: "DB", session: "auto" })中的session: "auto"表示按需启用 D1 会话(read-replica session)支持——DatabaseDescriptor中的supportsRequestScope标志会要求运行期入口导出createRequestScopedDb,用于每请求级数据库句柄(见 packages/core/src/db/adapters.ts)。- R2 存储通过 Worker 绑定访问,无需显式凭据;
@emdash-cms/cloudflare是 Cloudflare 专属适配器包(其对应 Node 端仓库示例可参考 demos/cloudflare 目录)。 compatibility_flags中的nodejs_compat是运行 EmDash 运行时的常规要求,具体以你所使用的版本为准。
Plugins:在 astro.config.mjs 中注册插件
EmDash 的插件体系通过emdash()的plugins数组接入,官方示例使用审计日志插件:
import auditLog from "@emdash-cms/plugin-audit-log"; emdash({ database: sqlite({ url: "file:./data.db" }), storage: local({ directory: "./uploads", baseUrl: "/_emdash/api/media/file" }), plugins: [auditLog], }),插件的PluginDescriptor结构(见 packages/core/src/astro/integration/runtime.ts)包含id、version、entrypoint、options、adminEntry(后台 React 组件入口)、componentsEntry(站点侧 Astro 渲染组件)等字段。两种格式需要区分:
standard格式:通过definePlugin({ hooks, routes })导出,可运行在plugins: [](进程内)或sandboxed: [](V8 isolate 沙箱)中,适合发布到插件注册表的三方插件;native格式:通过createPlugin(options)返回ResolvedPlugin,只能在plugins: []中运行,不能沙箱化或发布到市场。
沙箱插件通过sandboxed: []声明,并需要配套sandboxRunner(Cloudflare 场景使用@emdash-cms/cloudflare的sandbox())。集成启动时会校验插件格式与放置位置的匹配关系,例如 standard 插件声明adminEntry或 native 插件被放入sandboxed: []都会直接抛错(见 packages/core/src/astro/integration/index.ts)。仓库自带的插件示例可在 packages/plugins 目录中查看(audit-log、webhook-notifier、api-test 等)。
live.config.ts:注册内容集合
每个 EmDash 站点都必须有src/live.config.ts文件,且内容是固定的样板:
import { defineLiveCollection } from "astro:content"; import { emdashLoader } from "emdash/runtime"; export const collections = { _emdash: defineLiveCollection({ loader: emdashLoader() }), };该文件将 EmDash 的实时内容集合注册进 Astro 的 content layer。所有内容类型都通过单一的_emdash集合对外服务,查询具体类型时使用getEmDashCollection("posts")等 API。blank 模板中该文件与上文完全一致(见 templates/blank/src/live.config.ts)。
emdash-env.d.ts 与类型生成
emdash-env.d.ts位于项目根目录,由开发服务器在启动时自动生成,为你的内容集合提供 TypeScript 类型,并且会被tsconfig.json包含。自动生成内容的形态如下(以Post集合为例):
/// <reference types="emdash/locals" /> import type { PortableTextBlock } from "emdash"; export interface Post { id: string; slug: string | null; status: string; title: string; featured_image?: { id: string; src?: string; alt?: string; width?: number; height?: number; }; content?: PortableTextBlock[]; excerpt?: string; createdAt: Date; updatedAt: Date; publishedAt: Date | null; } declare module "emdash" { interface EmDashCollections { posts: Post; } }集合 schema 发生变更时,dev server 会自动重新生成该文件。你也可以手动触发类型生成:
# From local dev server (writes emdash-env.d.ts at project root) npx emdash types # From remote instance npx emdash types --url https://my-site.pages.dev # Custom output path npx emdash types --output src/types/cms.ts从 packages/core/src/cli/commands/types.ts 的实现看,该命令的核心流程是:先通过 HTTP 从 EmDash 实例拉取 JSON schema(schemaExport()),再请求生成 TypeScript 类型(schemaTypes()),随后写入输出文件。除了类型文件之外,CLI还会在输出目录旁写入.emdash/schema.json,保存原始 schema 供工具链使用(自定义输出路径时 schema.json 会写到对应目录,如src/types/schema.json)。生成成功后会打印集合数量、类型文件路径与 schema 版本。
package.json:两种部署形态的依赖差异
Node.js 站点核心依赖如下:
{ "dependencies": { "astro": "^6.0.0", "emdash": "workspace:*", "@astrojs/node": "^9.0.0", "@astrojs/react": "^4.0.0", "react": "^18.0.0", "react-dom": "^18.0.0" } }Cloudflare 形态则将@astrojs/node替换为@astrojs/cloudflare,并新增@emdash-cms/cloudflare(提供d1、r2、access、sandbox等适配器)。blank 模板的完整依赖清单见 templates/blank/package.json,其中各依赖版本使用 pnpm workspace 的catalog:统一管理。
Dev Server:启动、类型刷新与首次设置
pnpm dev # Start the Astro dev server npx emdash types # Refresh types from the running site启动时的运行期行为(结合 packages/core/src/astro/integration/index.ts 的astro:server:setup钩子与运行期迁移逻辑):
- 首次请求自动迁移:运行期会执行待应用的数据库迁移(pending migrations)。
- 空库自动播种:当数据库为空且 setup 尚未完成时,会应用内置的 seed 数据。
- 类型自动生成:dev server 监听端口后,立即触发首次
emdash-env.d.ts生成,并在 schema 变更时通过防抖刷新钩子(createDebouncedTypegenRefresh)自动重写文件。 - 管理后台地址:
http://localhost:4321/_emdash/admin。首次运行会进入 setup 向导,创建管理员账户。 - 开发快捷入口:dev 模式下会打印
/_emdash/api/setup/dev-bypass链接,可跳过 Passkey 设置直接以开发管理员身份登录(该端点在生产环境返回 403);同时会打印管理后台与 MCP server(/_emdash/api/mcp)的绝对可点击地址。
至此,一个 EmDash 站点从集成注册、数据存储、类型系统到部署形态的完整配置链路已经清晰:astro.config.mjs定义运行底座,live.config.ts打通内容层,emdash-env.d.ts提供类型保障,package.json决定部署形态。无论是本地 Node 自托管、容器化反代部署还是 Cloudflare Workers 边缘运行,都可以在这一套配置体系内完成切换。
- CMS
- 后端
- 前端
- 插件系统
【免费下载链接】emdash
EmDash is a full-stack TypeScript CMS based on Astro; the spiritual successor to WordPress
相关推荐
EmDash 站点配置完全指南:从 astro.config.mjs 到类型生成
EmDash 站点配置完全指南:从 astro.config.mjs 到类型生成 EmDash 是基于 Astro 构建的全栈 TypeScript CMS,站
CMS后端前端插件系统EmDash 站点配置完全指南:从 astro.config.mjs 到类型生成与多环境部署
EmDash 站点配置完全指南:从 astro.config.mjs 到类型生成与多环境部署 EmDash 是一个构建于 Astro 之上的全栈 TypeScr
CMS后端前端插件系统AI文本生成革命:零基础打造专属智能助手的终极指南 🚀
AI文本生成革命:零基础打造专属智能助手的终极指南 🚀 想要在本地运行强大的AI语言模型,打造完全私有的智能助手吗?TextGen正是你需要的开源桌面应用程序
CMS后端前端插件系统
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考