EmDash 透過兩個檔案進行設定:整合設定檔 astro.config.mjs 和內容集合設定檔 src/live.config.ts。
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" });
詳情請參閱資料庫選項。
storage
必需。 媒體儲存配接器設定。選擇一個配接器:
// 本地檔案系統(開發環境)
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", // 可選
});
詳情請參閱儲存選項。
plugins
可選。 EmDash 外掛陣列。以下範例註冊了一個外掛:
import seoPlugin from "@emdash-cms/plugin-seo";
plugins: [seoPlugin()];
fonts
可選。 管理介面字型設定。
預設情況下,EmDash 透過 Astro 字型 API 載入 Noto Sans。字型在建置時從 Google 下載並自主託管,因此沒有執行時 CDN 請求。基礎字型涵蓋拉丁文、西里爾文、希臘文、梵文和越南文字。
要新增對其他書寫系統的支援,請傳遞指令碼名稱。以下範例新增了阿拉伯語和日語:
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 的內建登入使用密鑰;設定 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 | — | 應用程式受眾(AUD)標籤。在 Workers 上,優先使用 audienceEnvVar。 |
audienceEnvVar | string | "CF_ACCESS_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 協定網路)。無需環境變數。接受{ allowedDIDs, allowedHandles, defaultRole }。參見 Atmosphere 登入指南。
第三方套件可以使用相同的 AuthProviderDescriptor 形式註冊自己的提供者 — 參見登入提供者。
siteUrl
可選。 站點的公開瀏覽器面向來源(配置 + 主機 + 可選連接埠,無路徑)。
在 TLS 終止反向代理後面,Astro.url 傳回內部位址(http://localhost:4321)而不是公開位址(https://cms.example.com)。這會破壞密鑰、CSRF 來源比對、OAuth 重新導向、登入重新導向、MCP 發現、快照匯出、網站地圖、robots.txt 和 JSON-LD 結構化資料。設定 siteUrl 可一次性修復所有這些問題。
整合在載入時驗證此值:它必須是具有 http: 或 https: 通訊協定的有效 URL,並標準化為來源(移除路徑)。
以下範例設定公開來源:
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 的容器部署很有用。
多來源密鑰驗證
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-*。為使用者存取的主機名稱(和配置)設定 security.allowedDomains。在 astro dev 中,新增相符的 vite.server.allowedHosts,以便 Vite 接受代理的 Host 標頭。
優先修復 allowedDomains(和轉送的標頭);當重建的 URL 仍然與瀏覽器來源不同時(典型情況是 TLS 在前端終止,上游請求保持 http://)使用 siteUrl。
在前端有 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
可選。 允許的最大媒體檔案上傳大小(以位元組為單位)。適用於直接分段上傳和簽章 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。
資料庫配接器
從 emdash/db 匯入配接器:
import { sqlite, libsql, postgres } from "emdash/db";
sqlite(config)
使用 better-sqlite3 的 SQLite 資料庫。以下範例連線到本機檔案:
| 選項 | 類型 | 描述 |
|---|---|---|
url | string | 帶 file: 前綴的檔案路徑 |
sqlite({ url: "file:./data.db" });
libsql(config)
libSQL 資料庫。以下範例連線到遠端 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 | "__em_d1_bookmark" | 工作階段書籤的 cookie 名稱 |
以下範例顯示基本繫結和啟用讀取副本的繫結:
// 基本
d1({ binding: "DB" });
// 使用讀取副本
d1({ binding: "DB", session: "auto" });
當 session 為 "auto" 或 "primary-first" 時,EmDash 使用 D1 Sessions API 將讀取查詢路由到附近的副本。經過身分驗證的使用者獲得基於書籤的讀後寫一致性。詳情請參閱資料庫選項 — 讀取副本。
儲存配接器
從 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 相容儲存。所有設定欄位都是可選的:從 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) |
以下範例從環境解析所有欄位,混合設定和環境,或明確傳遞每個欄位:
// 所有欄位來自 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({...})。詳情請參閱儲存選項。
即時集合
在 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_URL | 用於架構同步的遠端 EmDash URL |
使用以下命令產生加密金鑰:
npx emdash secrets generate
package.json 設定
範本和站點可以在 package.json 的 emdash 金鑰下宣告可選中繼資料:
{
"emdash": {
"label": "My Blog Template",
"seed": ".emdash/seed.json",
"url": "https://my-site.pages.dev"
}
}
| 選項 | 描述 |
|---|---|
label | 用於顯示的範本名稱 |
seed | 種子 JSON 檔案的路徑 |
url | 用於架構同步的遠端 URL |
TypeScript 設定
EmDash 在 .emdash/types.ts 中產生類型。將路徑別名新增到您的 tsconfig.json:
{
"compilerOptions": {
"paths": {
"@emdash-cms/types": ["./.emdash/types.ts"]
}
}
}
使用以下命令產生類型:
npx emdash types