このページで分かること

  • astro.config.mjs で指定するEmDashのインテグレーションの設定項目(databasestoragepluginssandboxRunnerauthsiteUrl など)
  • データベース、ストレージ、オブジェクトキャッシュ、認証とサンドボックス、メディアプロバイダー、Astroのキャッシュの各アダプター
  • src/live.config.ts のローダー、環境変数、package.jsonemdash の項目、TypeScriptの設定
難易度
上級
読む時間
24分
このページの目次

EmDashの主な設定は astro.config.mjs にあり、src/live.config.ts はコンテンツのローダーを登録します。デプロイ先ごとに異なる値は、環境変数から渡すこともできます。package.json の小さなメタデータのブロックは、テンプレートの表示名と、従来のローカルCLIの手順に使われます。

Astroインテグレーション

astro.config.mjs で、EmDashをAstroのインテグレーションとして設定します。

astro.config.mjs
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

必須。 データベースアダプターの設定です。次のアダプターから1つを選びます。

// 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 (import from @emdash-cms/cloudflare)
database: d1({ binding: "DB" });

詳しくはデータベースの選択肢を参照してください。

migrations

任意。 EmDash内部のデータベースマイグレーションを、実行時にどう扱うかを制御します。このオプションを省略すると { runtime: "auto" } になります。

migrations: {
	runtime: "check", // "auto" | "check" | "manual"
	dev: "auto",     // optional development override
}

auto は保留中のマイグレーションを確認して適用します。check は、実行中のビルドが把握しているマイグレーションが保留中の場合に503を返します。manual は実行時にマイグレーションのクエリを一切発行しません。EMDASH_MIGRATIONS_MODE は、実際に使われる実行時のモードを上書きします。check または manual を使う前に、コアデータベースマイグレーションの管理を参照してください。

storage

任意。 メディアストレージのアダプターの設定です。このオプションを省略すると、EmDashはファイルを ./.emdash/uploads に保存し、/_emdash/api/media/file から配信します。既定のローカルディレクトリーが適さない場合は、アダプターを選びます。

// Local filesystem (development)
storage: local({
	directory: "./uploads",
	baseUrl: "/_emdash/api/media/file",
});

// R2 binding (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
});

詳しくはストレージの選択肢を参照してください。

images

任意。 保存したメディアを、Astroの画像最適化と連携させるかどうかを制御します。既定値は true です。

有効にすると、EmDashはAstroの画像エンドポイントをラップし、<Image>getImage() が、設定したストレージアダプターから元画像のバイト列を直接読み込めるようにします。これは、元のメディアのURLがCloudflare Accessで保護されている場合にも動作します。別の画像サービスがメディアを扱う場合や、すべての画像をEmDashのエンドポイントのラッパーを通さずに描画する場合は、images: false を設定します。

emdash({
	images: false,
});

mediaProviders

任意。 メディアライブラリーにメディアサービスを追加します。ストレージを使うローカルのプロバイダーは自動的に使える状態のまま残ります。この配列の記述子(descriptor)1つごとに、編集者がメディアを閲覧またはアップロードできる場所が1つ追加されます。

次の例では、Cloudflare ImagesとCloudflare Streamを追加します。

astro.config.mjs
import { cloudflareImages, cloudflareStream } from "@emdash-cms/cloudflare";

emdash({
	mediaProviders: [cloudflareImages({}), cloudflareStream({})],
});

プロバイダーの認証情報は実行時に解決されます。上の例の空の設定では、cloudflareImages(config)cloudflareStream(config) のアダプターの節で説明している、Cloudflareの既定の環境変数を使います。バインディングと描画の設定については、メディアライブラリー:メディアプロバイダーを参照してください。

objectCache

任意。 コンテンツと設定の取得結果をキー・バリューストアにキャッシュし、リクエストのたびにデータベースへクエリを発行せずに読み込みに応答できるようにします。省略すると無効です。次のアダプターから1つを選びます。

// Cloudflare KV (shared across all isolates)
import { kvCache } from "@emdash-cms/cloudflare";
objectCache: kvCache({ binding: "CACHE" });

// In-memory (Node.js / development)
import { memoryCache } from "emdash/astro";
objectCache: memoryCache();

設定方法とオプションについては、オブジェクトキャッシュを参照してください。

middleware.outer

任意。 EmDashのミドルウェア全体の外側に、Astroのミドルウェアのモジュールを登録します。インテグレーションはこのモジュールをAstroの order: "pre" で登録するため、src/middleware.ts で定義したミドルウェアよりも先に実行されます。キャッシュにヒットしたときにランタイムとデータベースの初期化を避ける必要がある、リクエストの入口での判定や、レスポンス全体のキャッシュに使います。EmDashが最終的に出力するHTMLに応じて決まるレスポンスヘッダーにも使えます。

astro.config.mjs
emdash({
	middleware: {
		outer: "./src/outer-middleware.ts",
	},
});

実行順序は次のとおりです。

  1. 外側のミドルウェアが await next() まで実行されます。
  2. EmDashがランタイムとデータベースを初期化し、セットアップ、認証、リクエストコンテキストのミドルウェアを実行します。
  3. Astroのルートが描画されます。
  4. EmDashがレスポンスに変更を加えます。これには、ビジュアル編集用のHTMLと、セキュリティ・タイミングのヘッダーが含まれます。
  5. next() が、その最終的なレスポンスを外側のミドルウェアに返します。

next() を呼ぶ前のミドルウェアでは、通常のAstroのリクエストとプラットフォームの実行コンテキストを使えますが、locals.emdashlocals.user、データベース、リクエスト単位のEmDashの状態は使えません。早い段階で Response を返すとEmDashは完全にスキップされるため、そのレスポンスには必要なセキュリティとキャッシュのヘッダーをすべて含める必要があります。next() が値を返したあとは、CSPのnonceの確定、本文全体のキャッシュ、Content-Length の設定を安全に実行できます。ミドルウェアが本文を変更する場合は、既存の Content-Length ヘッダーを削除するか、計算し直してください。

このフックは、NodeとCloudflareのどちらでもAstroのミドルウェアAPIを使います。次の最小限のCloudflare Cache APIの例は、匿名ユーザー向けのHTMLレスポンスだけをキャッシュし、キャッシュにヒットした場合はEmDashの初期化より前に返します。

src/outer-middleware.ts
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のプレイグラウンドが使うミドルウェアを有効にします。このミドルウェアは、セッションごとに書き込み可能なDurable Objectのデータベースを作成し、設定したシードを適用し、通常のEmDashのミドルウェアが実行される前に、訪問者を匿名の管理者としてサインインさせます。

astro.config.mjs
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を使い、分離されたランタイムで動く、サンドボックス対応のプラグインの配列です。ネイティブ型プラグインはここに指定しないでください。ネイティブのコードは、サンドボックスが提供しないプロセスやフレームワークへのアクセスに依存している場合があります。

astro.config.mjs
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 が設定されていて sandboxfalse でなければ、EmDashは https://registry.emdashcms.com を使います。

registry: false を設定すると、レジストリでのプラグインの検索と、レジストリからインストールしたプラグインが無効になります。サンドボックスランナーは、sandboxed に指定したプラグインと、従来のMarketplaceのプラグインのために引き続き使えます。

astro.config.mjs
emdash({
	sandboxRunner: sandbox(),
	registry: false,
});

レジストリサービスのURLは文字列で渡します。サイトでモデレーションの提供元やリリースからの経過期間のポリシーが必要な場合は、オブジェクトを使います。次の例ではオブジェクトの形式を使っています。

astro.config.mjs
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"],
		},
	},
});
オプション 説明
aggregatorUrl string レジストリサービスのベースURLです。本番ではHTTPSを使います。
acceptLabelers string 任意。リクエストで受け入れるモデレーションサービスの分散型識別子(DID)を、カンマ区切りで指定します。DIDは、Atmosphereアカウントの変わらない識別子です。この設定で、レジストリサービスのポリシーを上書きすることはできません。
policy.minimumReleaseAge string | number この期間より新しいリリースを保留します。期間の文字列("48h""7d")または秒数で指定します。
policy.minimumReleaseAgeExclude string[] 保留の対象外にする公開者のDID、または <did>/<plugin-slug> の組です。

リリースからの経過期間のポリシーがパッケージの最初のリリースを対象外にするのは、保持されているリリースが1つであるとレジストリが報告し、かつレジストリがそのパッケージを継続して観測してきたことを確認できた場合だけです。あとから取り込まれた(backfilled)パッケージ、以前のリリースが削除されたパッケージ、履歴の証拠がないパッケージでは、保留は有効なままです。公開者とパッケージを明示して対象外にした場合は、履歴に関係なく適用されます。

インストールの流れと信頼モデルについては、プラグインレジストリを参照してください。

marketplace

非推奨。 従来のMarketplaceからインストールしたプラグインを更新するために使うベースURLです。管理画面には、Marketplaceの閲覧と新規インストールは表示されません。このオプションを設定している間は、既存のMarketplaceのプラグインの更新とアンインストールを引き続きできます。

emdash({
	marketplace: "https://marketplace.emdashcms.com",
	sandboxRunner: sandbox(),
});

本番のURLにはHTTPSを使う必要があります。HTTPを使えるのは、開発中の localhost127.0.0.1 だけです。このオプションは、すべてのMarketplaceのプラグインを置き換えるかアンインストールするまで残し、その後に削除します。手順全体はMarketplaceからの移行に従ってください。

fonts

任意。 管理画面のUIのフォントの設定です。

既定では、EmDashはAstro Font APIを使ってNoto Sansを読み込みます。フォントはビルド時にGoogleからダウンロードされ、自分のサイトから配信されるため、実行時にCDNへのリクエストは発生しません。基本のフォントは、ラテン文字、キリル文字、ギリシャ文字、デーヴァナーガリー文字、ベトナム語の文字に対応しています。

ほかの文字体系に対応させるには、文字体系の名前を渡します。次の例では、アラビア文字と日本語を追加します。

emdash({
  fonts: {
    scripts: ["arabic", "japanese"],
  },
})

使える文字体系は、arabicarmenianbengalichinese-simplifiedchinese-traditionalchinese-hongkongdevanagariethiopicfarsigeorgiangujaratigurmukhihebrewjapanesekannadakhmerkoreanlaomalayalammyanmaroriyasinhalatamilteluguthaitibetan です。

それぞれの文字体系は、Google Fontsの対応するNoto Sansのバリエーションに対応します(例:"arabic" はNoto Sans Arabicを読み込みます)。すべてのフォントフェイスは1つの font-family 名を共有し、unicode-range を使うため、ブラウザーはページ上の文字に必要なファイルだけをダウンロードします。

フォントの挿入を完全に無効にしてシステムフォントを使うには、false を設定します。

emdash({
	fonts: false,
})

管理画面のCSSは、CSS変数 --font-emdash を使います。この変数は、上記のフォントの設定によって自動的に設定されます。

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 アプリケーションのAudience(AUD)タグ。Workersでは audienceEnvVar を優先して使います。
audienceEnvVar string "CF_ACCESS_AUDIENCE" 実行時に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_IDEMDASH_OAUTH_GITHUB_CLIENT_SECRET(または接頭辞なしの代替の変数)を読み込みます。
  • google()EMDASH_OAUTH_GOOGLE_CLIENT_IDEMDASH_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

任意。 ブラウザーから見たサイトの公開オリジン(スキーム+ホスト+任意でポート。パスは含めない)です。

TLSを終端するリバースプロキシの背後では、Astro.url は公開アドレス(https://cms.example.com)ではなく内部アドレス(http://localhost:4321)を返します。その結果、パスキー、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_URLSITE_URL の順に確認します。これは、公開URLを実行時に設定するコンテナーのデプロイで便利です。

Cloudflare Workersでは、環境変数による代替の読み込みは process.env を参照しますが、process.env は互換性フラグ nodejs_compat_populate_process_env を有効にしない限り空です。Workersで設定オプションの代わりに環境変数を使うには、両方を設定します。

// wrangler.jsonc
{
	"compatibility_flags": ["nodejs_compat", "nodejs_compat_populate_process_env"],
	"vars": { "EMDASH_SITE_URL": "https://cms.example.com" },
}

allowedOrigins

任意。 複数のホスト名で利用できるデプロイについて、パスキーの検証で追加で受け入れるブラウザーのオリジンです。

siteUrl は、正規のオリジンを1つだけ定義します。同じEmDashのデプロイに、登録可能な親ドメインを共有する複数のホスト名(例:https://example.comhttps://preview.example.com)でアクセスできる場合、パスキーの検証は、オリジンが siteUrl と完全に一致しないアサーションを拒否します。WebAuthnでは、同じ rpId のもとにあるサブドメインをまたいでパスキーを有効にできるにもかかわらず、拒否されます。

追加で受け入れるオリジンは、astro.config.mjsallowedOrigins、または環境変数 EMDASH_ALLOWED_ORIGINS のどちらかで宣言します。rpId の元になるのは、引き続き正規の siteUrl です。ここに列挙したオリジンは、検証の時点で受け入れられます。2つの指定元は実行時に統合されるため、変わらないオリジン(バージョン管理され、コードレビューを経たもの)は設定ファイルで宣言し、環境ごとの追加分(例:一時的なPRのプレビュー)は環境変数で加えられます。

次の例では、設定ファイルで追加のオリジンを1つ宣言します。

astro.config.mjs
emdash({
	siteUrl: "https://example.com",
	allowedOrigins: ["https://preview.example.com"],
})

同じ値を環境変数から渡すこともできます。

.env / wrangler.jsonc / Docker env
EMDASH_SITE_URL=https://example.com
EMDASH_ALLOWED_ORIGINS=https://preview.example.com,https://staging.example.com
検証

ブラウザーが決して受け入れない、意味のない設定を防ぐため、EmDashはこれらの値を検証します。

  • 各要素は、解析可能な http: または https: のURLで、ホスト名の末尾にドットがなく、空のラベルを含まない必要があります。
  • allowedOrigins が空でない場合、siteUrl が(どちらかの指定元で)設定されている必要があり、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.allowedOriginsconfig.siteUrl の両方が astro.config.mjs にある場合です。コードの入力ミスはビルドの失敗になります。
  • 最初のパスキー検証時:どちらかの値が EMDASH_ALLOWED_ORIGINS または EMDASH_SITE_URL から来ている場合です。環境変数の不一致は、最初の検証の試行で500として表面化します。

リバースプロキシの設定

Astroは、公開ホストが許可されている場合にだけ X-Forwarded-* を反映します。ユーザーがアクセスするホスト名(とスキーム)を security.allowedDomains に設定します。astro dev では、Viteがプロキシの Host ヘッダーを受け入れるよう、対応する vite.server.allowedHosts を追加します。

まず allowedDomains(と転送ヘッダー)を正しく設定します。それでも組み立て直されたURLがブラウザーのオリジンとなお異なる場合(前段でTLSが終端され、上流へのリクエストが http:// のままの場合によく起こります)は、siteUrl を使います。

前段でTLSを終端する場合、開発サーバーをループバックにバインドする(astro dev --host 127.0.0.1)だけで足りることがよくあります。プロキシはローカルで接続し、siteUrl は公開HTTPSのオリジンと一致します。

プロキシがクライアントのIPのヘッダーを書き込む場合は、trustedProxyHeaders を設定します。これにより、EmDashのレート制限が、すべてのリクエストを共有の「unknown」キーにまとめるのではなく、実際のクライアントのIPを使えるようになります。

次の設定では、リバースプロキシを使うデプロイ向けに、allowedDomainsvite.server.allowedHostssiteUrl をまとめて設定します。

astro.config.mjs (excerpt)
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などの背後にあるセルフホストのデプロイでは、プロキシが書き込むヘッダーをこのオプションに設定します。これにより、レート制限がすべてのリクエストを「unknown」として扱うのではなく、実際のクライアントのIPごとに集計できるようになります。

次の例では、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(50MB)です。次の例では、上限を100MBに引き上げます。

emdash({
	database: sqlite({ url: "file:./data.db" }),
	storage: local({
		directory: "./uploads",
		baseUrl: "/_emdash/api/media/file",
	}),
	maxUploadSize: 100 * 1024 * 1024, // 100 MB
});
説明
number(バイト) 正の有限な整数である必要があります
省略 既定値の50MBになります

設定した上限を超えるアップロードは、直接アップロードでは 413 Payload Too Large のレスポンスで、署名付きURLでは 400 Validation Error で拒否されます。

admin

任意。 管理画面のEmDashのブランド表示を置き換えます。これらの値は、公開サイトのタイトル、ロゴ、ファビコンを変更しません。

emdash({
	admin: {
		logo: "/images/agency-logo.webp",
		siteName: "Agency CMS",
		favicon: "/favicon.ico",
	},
});
オプション 説明
logo string ログインページとサイドバーに表示するロゴのURLまたはパス
siteName string サイドバーとブラウザーのタイトルに表示する名前
favicon string 管理画面のページのファビコンのURLまたはパス

toolbar

任意。 編集ツールバー(公開ページに浮かんで表示される錠剤型の部品)をどのように配信するかを制御します。既定値は "server" です。

動作
"server"(既定) 認証済みの編集者向けに描画するすべてのHTMLレスポンスに、サーバー側でツールバーを挿入します。
"client" 公開HTMLはすべての訪問者で同じになります。小さな起動用スクリプトが、管理画面にログインしたことのあるブラウザーに「Edit」の錠剤型ボタンを表示します。これを選択するとセッションを確認し、クエリパラメーター _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 によって分岐している場合(例:ログイン済みのユーザー向けの「Admin」のナビゲーションリンク)、その違いはHTMLに残るため、キャッシュは引き続き分かれます。

どのモードでも、ツールバーはブラウザー上で×ボタンから閉じられます(ブラウザーごとに、編集者が次に管理画面を開くまで有効です)。プレビューと編集モードのレスポンスは、常に Cache-Control: private, no-store 付きでサーバー側で描画されます。

experimental

任意。 マイナーリリースで動作や通信の形式が変わったり、削除されたりする可能性がある、明示的に有効にする機能です。各フィールドは個別に有効にします。

experimental.registry

非推奨。 トップレベルの registry オプションを使います。トップレベルのオプションを省略している場合、既存の experimental.registry の設定は引き続き動作します。両方がある場合は、トップレベルの値が優先されます。

次の変更では、既存のレジストリのURLをトップレベルに移します。

astro.config.mjs
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のデータベースです。次の例では、ローカルのファイルに接続します。

オプション 説明
url string file: の接頭辞を付けたファイルパス
sqlite({ url: "file:./data.db" });

libsql(config)

libSQLのデータベースです。次の例では、リモートのlibSQLのデータベースに接続します。

オプション 説明
url string データベースのURL
authToken string 実行時の認証トークン(ローカルのファイルでは任意)
migrationAuthTokenEnv string マイグレーション用のトークンの変数名(既定値は TURSO_AUTH_TOKEN
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)
pool.connectionTimeoutMillis number 接続を待つ最大時間(pgの既定値:0、タイムアウトなし)
pool.idleTimeoutMillis number アイドル状態のクライアントの存続時間(pgの既定値:10,000ms)
migrationConnectionStringEnv string マイグレーション用の接続文字列の変数名(既定値は DATABASE_URL

次の例では、接続文字列で接続します。

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の名前
coalesce boolean false 同じイベントループのターン内で同時に発生した読み取りをまとめて処理します。"disabled" 以外のセッションモードが必要です

次の例は、基本のバインディングと、読み取りレプリカを有効にしたバインディングです。

// Basic
d1({ binding: "DB" });

// With read replicas
d1({ binding: "DB", session: "auto" });

session"auto" または "primary-first" の場合、EmDashはD1 Sessions APIを使って、読み取りのクエリを近くのレプリカに振り分けます。認証済みのユーザーには、ブックマークによって、自分が書き込んだ内容を直後に読める一貫性(read-your-writes)が保証されます。詳しくはデータベースの選択肢:読み取りレプリカを参照してください。

hyperdrive(config?)

Cloudflare Hyperdriveのバインディングを通して使うPostgreSQLです。このアダプターは @emdash-cms/cloudflare からインポートします。

オプション 既定値 説明
binding string "HYPERDRIVE" クエリキャッシュを無効にした、主となるHyperdriveのバインディング
cachedBinding string 任意。匿名ユーザーの公開の読み取りに使う、キャッシュを有効にした2つ目のバインディング
preferUncachedAfterWriteMs number 60_000 cachedBinding を設定している場合に、コンテンツの書き込み後、公開の読み取りが主のバインディングを使う期間
migrationConnectionStringEnv string 主のバインディングから導出 emdash migrate が使う、PostgreSQLへの直接のURLを格納した環境変数
max number 5 1つの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を使う1つのDurable Objectに保存します。このアダプターは @emdash-cms/cloudflare からインポートします。

オプション 既定値 説明
binding string 必須 EmDashDB クラスのDurable Objectの名前空間のバインディング
name string "emdash" シングルトンのオブジェクト名。1つのバインディングの背後で複数のデータベースを分ける場合にだけ変更します
session string "disabled" "auto" は、匿名ユーザーの読み取りをレプリカに、書き込みを主に振り分けます
bookmarkCookie string "__em_do_bookmark" "auto" モードで、自分が書き込んだ内容を直後に読める一貫性のために使うCookie
durableObjects({ binding: "DB_DO", session: "auto" });

レプリカへの振り分けには、互換性フラグ experimentalreplica_routing に加えて、wrangler.jsonc のDurable Objectのクラスとマイグレーションの記述が必要です。

previewDatabase(config)

プレビューのセッションごとに、分離されたスナップショットのデータベースを1つ、Durable Objectの中に作成します。オプションは必須の binding 名だけです。

previewDatabase({ binding: "PREVIEW_DB" });

このアダプターはプレビューの仕組みのためのもので、本番サイトの主データベース用ではありません。

playgroundDatabase(config)

プレイグラウンドのセッションごとに、シードを適用した書き込み可能なデータベースを1つ、Durable Objectの中に作成します。インテグレーションの playground オプションと組み合わせて使います。

playgroundDatabase({ binding: "PLAYGROUND_DB" });

必須の binding は、プレイグラウンドのDurable Objectの名前空間を示します。このアダプターは、使い捨てのデモサイトにだけ使います。

ストレージアダプター

locals3emdash/astro からインポートします。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_* 環境変数から解決されます。明示した値が常に優先されます。

設定ファイルの値と環境変数の値を統合したあと、endpointbucket は必須です。認証情報のどちらか一方を設定した場合は、accessKeyIdsecretAccessKey の両方が必要です。値が足りない場合は、エラーコード MISSING_S3_CONFIG で起動に失敗します。

前提条件: プロジェクトに @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
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では、シークレットと変数は process.env ではなくfetchハンドラーの env パラメーターを通して渡されるため、S3_* 環境変数は読み込まれません。Workersのデプロイでは、r2(config) アダプターを使うか、s3({...}) に値を明示して渡します。詳しくはストレージの選択肢を参照してください。

オブジェクトキャッシュのアダプター

次のどちらかを objectCache オプションに渡します。

kvCache(config)

すべてのisolateで共有されるCloudflare KVのバックエンドです。@emdash-cms/cloudflare からインポートします。

kvCache({
	binding: "CACHE", // KV binding name (required)
	defaultTtl: 3600, // entry TTL in seconds (optional, KV minimum 60)
	revalidate: 1000, // cross-isolate staleness window in ms (optional)
	timeout: 2000, // per-op timeout in ms before a miss (optional, 0 disables)
	keyPrefix: "em", // cache key prefix (optional)
})

memoryCache(config?)

Node.jsと開発用の、プロセス内のバックエンドです。emdash/astro からインポートします。

memoryCache({
	defaultTtl: 3600, // entry TTL in seconds (optional)
	revalidate: 1000, // staleness window in ms (optional)
	maxEntries: 1000, // max cached keys before eviction (optional)
	keyPrefix: "em", // cache key prefix (optional)
})

設定方法と動作については、オブジェクトキャッシュを参照してください。

認証とサンドボックスのアダプター

これらのアダプターは、インテグレーションの authsandboxRunner のオプションに渡す値を返します。

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を audience から、または audienceEnvVar で指定した名前の変数から読み込めます。autoProvisiondefaultRolesyncRolesroleMapping も受け取ります。これらの既定値とロールの動作は、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 からインポートします。

cloudflareImages(config)

画像アセットの閲覧、アップロード、削除、配信のために、Cloudflare Imagesを追加します。

オプション 既定値 説明
accountId string CF_ACCOUNT_ID から CloudflareのアカウントID
accountIdEnvVar string "CF_ACCOUNT_ID" accountId を省略した場合に使う変数
accountHash string CF_IMAGES_ACCOUNT_HASH から 配信URLに使うアカウントのハッシュ
accountHashEnvVar string "CF_IMAGES_ACCOUNT_HASH" accountHash を省略した場合に使う変数
apiToken string CF_IMAGES_TOKEN から Cloudflare Imagesの読み取りと編集の権限を持つトークン
apiTokenEnvVar string "CF_IMAGES_TOKEN" apiToken を省略した場合に使う変数
deliveryDomain string imagedelivery.net 画像を配信する独自のホスト名
defaultVariant string "public" 表示に使う画像のバリアント
mediaProviders: [cloudflareImages({ defaultVariant: "public" })];

cloudflareStream(config)

動画アセットの閲覧、検索、アップロード、削除、再生のために、Cloudflare Streamを追加します。

オプション 既定値 説明
accountId string CF_ACCOUNT_ID から CloudflareのアカウントID
accountIdEnvVar string "CF_ACCOUNT_ID" accountId を省略した場合に使う変数
apiToken string CF_STREAM_TOKEN から Cloudflare Streamの読み取りと編集の権限を持つトークン
apiTokenEnvVar string "CF_STREAM_TOKEN" apiToken を省略した場合に使う変数
customerSubdomain string Cloudflareの既定値 Streamを配信する独自のホスト名
controls boolean true プレーヤーのコントロールを表示する
autoplay boolean false 自動的に再生を開始する
loop boolean false 繰り返し再生する
muted boolean false(autoplayの場合は true 音声をミュートして再生する
mediaProviders: [cloudflareStream({ controls: true })];

必要なバインディングと描画用のコンポーネントについては、メディアライブラリー:メディアプロバイダーを参照してください。

Astroのキャッシュアダプター

cloudflareCache(config?)

この従来のアダプターは、Workers Cache APIにレスポンスを保存し、Cloudflare REST APIを通してキャッシュタグでパージするAstroの cache.provider を返します。

astro.config.mjs
import { cloudflareCache } from "@emdash-cms/cloudflare";

export default defineConfig({
	cache: {
		provider: cloudflareCache(),
	},
});

cacheName(既定値 "emdash")と bookmarkCookie(既定値 "__em_d1_bookmark")に加え、タグによるパージのリクエストのために、zoneId または zoneIdEnvVar と、apiToken または apiTokenEnvVar を受け取ります。既定の変数名は CF_ZONE_IDCF_CACHE_PURGE_TOKEN です。

ライブコレクション

src/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(),
	}),
};

ローダーのオプション

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_KEY Cloudflare Turnstileのシークレットキー(未設定の場合は TURNSTILE_SECRET_KEY を使います)。設定すると、コメントの送信には有効なTurnstileのトークンが必要になります。<CommentForm>turnstileSiteKey プロパティと組み合わせて使います。
EMDASH_URL スキーマの同期に使う、リモートのEmDashのURL

次のコマンドで暗号化キーを生成します。

npx emdash secrets generate

package.jsonの設定

テンプレートとサイトは、package.jsonemdash キーの下に、任意のメタデータを宣言できます。

package.json
{
	"emdash": {
		"label": "My Blog Template",
		"schema": ".emdash/schema.sql",
		"seed": ".emdash/seed.json",
		"url": "https://my-site.pages.dev"
	}
}
オプション 説明
label 表示用のテンプレート名
schema emdash init が読み込む任意のSQLのスキーマ
seed シードのJSONファイルのパス
url 非推奨の emdash dev --types の手順で使うリモートのURL

TypeScriptの設定

ローカルでの開発中、Astroのインテグレーションはプロジェクトのルートに emdash-env.d.ts を生成し、スキーマの変更後に更新します。このファイルは emdash モジュールを拡張するため、標準の getEmDashCollection()getEmDashEntry() のインポートは、パスのエイリアスなしでローカルのコレクションのフィールドの型を推論します。

別の emdash types コマンドは、動作中のローカルまたはリモートのインスタンスからスキーマを取得し、既定では .emdash/types.ts に書き出します。アプリケーションのコードがこの単独の出力を直接インポートする場合にだけ、エイリアスを追加します。

tsconfig.json
{
	"compilerOptions": {
		"paths": {
			"@emdash-cms/types": ["./.emdash/types.ts"]
		}
	}
}

リモートのスキーマから単独の型を生成するには、次のコマンドを実行します。

npx emdash types