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
필수. 데이터베이스 어댑터 구성. 어댑터를 선택합니다:
database: sqlite({ url: "file:./data.db" });
database: postgres({ connectionString: process.env.DATABASE_URL });
database: libsql({ url: process.env.LIBSQL_DATABASE_URL, authToken: process.env.LIBSQL_AUTH_TOKEN });
database: d1({ binding: "DB" });
storage
필수. 미디어 스토리지 어댑터 구성:
storage: local({ directory: "./uploads", baseUrl: "/_emdash/api/media/file" });
storage: r2({ binding: "MEDIA", publicUrl: "https://pub-xxxx.r2.dev" });
storage: s3()
objectCache
선택. 콘텐츠 및 구성 쿼리 결과를 키/값 저장소에 캐시합니다:
import { kvCache } from "@emdash-cms/cloudflare";
objectCache: kvCache({ binding: "CACHE" });
import { memoryCache } from "emdash/astro";
objectCache: memoryCache();
plugins
선택. EmDash 플러그인 배열:
import seoPlugin from "@emdash-cms/plugin-seo";
plugins: [seoPlugin()];
fonts
선택. 관리자 UI 폰트 구성.
기본적으로 EmDash는 Astro Font API를 통해 Noto Sans를 로드합니다. 추가 문자 체계 지원을 위해 스크립트 이름을 전달합니다:
emdash({
fonts: {
scripts: ["arabic", "japanese"],
},
})
폰트 주입을 완전히 비활성화하고 시스템 폰트를 사용하려면 false로 설정합니다:
emdash({ fonts: false })
auth
선택. 인증 어댑터. EmDash의 기본 로그인은 패스키를 사용합니다. auth를 설정하면 외부 공급자로 교체됩니다:
import { access } from "@emdash-cms/cloudflare";
emdash({
auth: access({
teamDomain: "myteam.cloudflareaccess.com",
audience: "your-app-audience-tag",
roleMapping: { Admins: 50, Editors: 40 },
}),
});
authProviders
선택. 플러그형 로그인 공급자 배열:
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 계정 로그인. 환경 변수 불필요.
siteUrl
선택. 사이트의 공개 브라우저 대면 오리진 (스키마 + 호스트 + 선택적 포트, 경로 없음).
TLS 종단 리버스 프록시 뒤에서 Astro.url은 공개 주소 대신 내부 주소를 반환합니다. siteUrl을 설정하여 패스키, CSRF, OAuth 리다이렉트 등을 모두 수정합니다.
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({ directory: "./uploads", baseUrl: "/_emdash/api/media/file" }),
siteUrl: "https://cms.example.com",
});
멀티 오리진 패스키 검증
allowedOrigins로 추가 허용 오리진을 선언합니다:
emdash({
siteUrl: "https://example.com",
allowedOrigins: ["https://preview.example.com"],
})
리버스 프록시 설정
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 해석을 위해 신뢰할 헤더:
emdash({
trustedProxyHeaders: ["x-real-ip"],
});
maxUploadSize
선택. 미디어 파일 업로드 최대 허용 크기(바이트). 기본값 52_428_800 (50 MB):
emdash({
maxUploadSize: 100 * 1024 * 1024, // 100 MB
});
toolbar
선택. 에디터 도구 모음 전달 방법을 제어합니다. 기본값 "server".
| 값 | 동작 |
|---|---|
"server" (기본값) | 도구 모음이 인증된 에디터의 각 HTML 응답에 서버 사이드로 주입됩니다. |
"client" | 공개 HTML은 모든 방문자에게 동일합니다. 작은 부트스트랩 스크립트가 “Edit” 필을 표시합니다. |
false | 도구 모음이나 부트스트랩 스크립트를 렌더링하지 않습니다. |
experimental
선택. 마이너 릴리스에서 동작이 변경될 수 있는 옵트인 기능.
experimental.registry
선택. 관리자 대시보드의 플러그인 브라우즈 및 설치 흐름을 페더레이트된 플러그인 레지스트리로 향하게 합니다.
emdash({
sandboxRunner: "@emdash-cms/sandbox-cloudflare",
experimental: {
registry: {
aggregatorUrl: "https://registry.emdashcms.com",
acceptLabelers: "did:plc:emdashverification",
policy: {
minimumReleaseAge: "48h",
minimumReleaseAgeExclude: ["did:plc:yourfirstpartydid"],
},
},
},
});
데이터베이스 어댑터
import { sqlite, libsql, postgres } from "emdash/db";
sqlite(config)
| 옵션 | 타입 | 설명 |
|---|---|---|
url | string | file: 접두사가 있는 파일 경로 |
libsql(config)
| 옵션 | 타입 | 설명 |
|---|---|---|
url | string | 데이터베이스 URL |
authToken | string | 인증 토큰 (로컬 파일에서는 선택 사항) |
postgres(config)
| 옵션 | 타입 | 설명 |
|---|---|---|
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) |
d1(config)
Cloudflare D1. @emdash-cms/cloudflare에서 임포트.
| 옵션 | 타입 | 기본값 | 설명 |
|---|---|---|---|
binding | string | — | wrangler.jsonc의 D1 바인딩 이름 |
session | string | "disabled" | 읽기 복제 모드 |
bookmarkCookie | string | "__em_d1_bookmark" | 세션 북마크 쿠키 이름 |
스토리지 어댑터
import emdash, { local, s3 } from "emdash/astro";
import { r2 } from "@emdash-cms/cloudflare";
local(config)
| 옵션 | 타입 | 설명 |
|---|---|---|
directory | string | 디렉토리 경로 |
baseUrl | string | 파일 제공 기본 URL |
r2(config)
| 옵션 | 타입 | 설명 |
|---|---|---|
binding | string | R2 바인딩 이름 |
publicUrl | string | 선택적 공개 URL |
s3(config?)
S3 호환 스토리지. 모든 필드는 선택 사항입니다.
전제 조건: @aws-sdk/client-s3와 @aws-sdk/s3-request-presigner를 설치하세요.
| 옵션 | 타입 | 설명 |
|---|---|---|
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) |
오브젝트 캐시 어댑터
kvCache(config)
Cloudflare KV 백엔드.
kvCache({ binding: "CACHE", defaultTtl: 3600, revalidate: 1000, timeout: 2000, keyPrefix: "em" })
memoryCache(config?)
Node.js용 인프로세스 백엔드.
memoryCache({ defaultTtl: 3600, revalidate: 1000, maxEntries: 1000, keyPrefix: "em" })
라이브 컬렉션
import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";
export const collections = {
_emdash: defineLiveCollection({
loader: emdashLoader(),
}),
};
환경 변수
| 변수 | 설명 |
|---|---|
EMDASH_SITE_URL | 공개 브라우저 대면 오리진 |
EMDASH_ALLOWED_ORIGINS | 패스키 검증에서 허용되는 추가 오리진 |
EMDASH_DATABASE_URL | 데이터베이스 URL 재정의 |
EMDASH_ENCRYPTION_KEY | 플러그인 시크릿 저장 시 암호화 키 |
EMDASH_PREVIEW_SECRET | 프리뷰 HMAC 시크릿 선택적 재정의 |
EMDASH_IP_SALT | 댓글 작성자 IP 해시 솔트 선택적 재정의 |
EMDASH_AUTH_SECRET | 레거시. IP 솔트 소스로 사용 |
EMDASH_TURNSTILE_SECRET_KEY | Cloudflare Turnstile 시크릿 키 |
EMDASH_URL | 스키마 동기화용 원격 EmDash URL |
npx emdash secrets generate
package.json 구성
{
"emdash": {
"label": "My Blog Template",
"seed": ".emdash/seed.json",
"url": "https://my-site.pages.dev"
}
}
TypeScript 구성
{
"compilerOptions": {
"paths": {
"@emdash-cms/types": ["./.emdash/types.ts"]
}
}
}
npx emdash types