EmDash 站点配置指南:从 astro.config.mjs 到类型生成的完整实战手册
2026/9/23 13:11:46 网站建设 项目流程
  • CMS
  • 后端
  • 前端
  • 插件系统

【免费下载链接】emdash

EmDash is a full-stack TypeScript CMS based on Astro; the spiritual successor to WordPress

项目地址:https://gitcode.com/gh_mirrors/emdas/emdash
点击查看免费下载

本文以 EmDash 官方构建指南中的 Configuration 章节为骨架,系统讲解基于 Astro 的全栈 TypeScript CMS —— EmDash 的站点配置体系。你将掌握astro.config.mjs中数据库、存储、站点 URL、插件的完整配置方法,理解 Node.js 自托管与 Cloudflare D1/R2 两种部署形态的差异,学会通过live.config.ts注册内容集合、用npx emdash types生成类型,并了解反向代理场景下siteUrlsecurity.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/nodestandalone模式运行,构建产物位于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 }):本地文件系统存储,用于开发与测试,不支持签名上传 URLbaseUrl指向媒体代理路由/_emdash/api/media/filelocal()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.com

siteUrl取代了旧的passkeyPublicOrigin(后者只修复 Passkey 一个问题),现在它统一作用于:Passkey、CSRF origin 匹配、OAuth 重定向、登录重定向、MCP 发现、快照导出、sitemap、robots.txt 以及 JSON-LD 结构化数据。

从 packages/core/src/astro/integration/index.ts 的实现可以看到两点约束:

  1. siteUrl必须是httphttps协议,否则启动即报错;
  2. 它会被规范化为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)包含idversionentrypointoptionsadminEntry(后台 React 组件入口)、componentsEntry(站点侧 Astro 渲染组件)等字段。两种格式需要区分:

  • standard格式:通过definePlugin({ hooks, routes })导出,可运行在plugins: [](进程内)或sandboxed: [](V8 isolate 沙箱)中,适合发布到插件注册表的三方插件;
  • native格式:通过createPlugin(options)返回ResolvedPlugin,只能在plugins: []中运行,不能沙箱化或发布到市场。

沙箱插件通过sandboxed: []声明,并需要配套sandboxRunner(Cloudflare 场景使用@emdash-cms/cloudflaresandbox())。集成启动时会校验插件格式与放置位置的匹配关系,例如 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(提供d1r2accesssandbox等适配器)。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钩子与运行期迁移逻辑):

  1. 首次请求自动迁移:运行期会执行待应用的数据库迁移(pending migrations)。
  2. 空库自动播种:当数据库为空且 setup 尚未完成时,会应用内置的 seed 数据。
  3. 类型自动生成:dev server 监听端口后,立即触发首次emdash-env.d.ts生成,并在 schema 变更时通过防抖刷新钩子(createDebouncedTypegenRefresh)自动重写文件。
  4. 管理后台地址http://localhost:4321/_emdash/admin。首次运行会进入 setup 向导,创建管理员账户。
  5. 开发快捷入口: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

项目地址:https://gitcode.com/gh_mirrors/emdas/emdash
点击查看免费下载

相关推荐

上一篇:PDF补丁丁深度应用指南:5个高效技巧让PDF处理效率翻倍
下一篇:uvw定时器与事件循环深度解析:掌握异步编程的核心机制

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询