Referencia de configuración
EmDash se configura mediante dos archivos: astro.config.mjs para la integración y src/live.config.ts para las colecciones de contenido.
Integración de Astro
Sección titulada «Integración de Astro»Configura EmDash como una integración de Astro:
import { defineConfig } from "astro/config";import emdash, { local, r2, s3 } from "emdash/astro";import { sqlite, libsql, d1 } from "emdash/db";
export default defineConfig({ integrations: [ emdash({ database: sqlite({ url: "file:./data.db" }), storage: local({ directory: "./uploads", baseUrl: "/_emdash/api/media/file", }), plugins: [], }), ],});Opciones de Integración
Sección titulada «Opciones de Integración»database
Sección titulada «database»Obligatorio. Configuración del adaptador de base de datos.
// SQLite (Node.js)database: sqlite({ url: "file:./data.db" });
// PostgreSQLdatabase: postgres({ connectionString: process.env.DATABASE_URL });
// libSQLdatabase: libsql({ url: process.env.LIBSQL_DATABASE_URL, authToken: process.env.LIBSQL_AUTH_TOKEN,});
// Cloudflare D1 (importar desde @emdash-cms/cloudflare)database: d1({ binding: "DB" });Consulta Opciones de Base de Datos para más detalles.
storage
Sección titulada «storage»Obligatorio. Configuración del adaptador de almacenamiento de medios.
// Sistema de archivos local (desarrollo)storage: local({ directory: "./uploads", baseUrl: "/_emdash/api/media/file",});
// Enlace R2 (Cloudflare Workers)storage: r2({ binding: "MEDIA", publicUrl: "https://pub-xxxx.r2.dev", // opcional});
// Compatible con S3 (cualquier plataforma)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", // opcional, valor por defecto: "auto" publicUrl: "https://cdn.example.com", // opcional});Consulta Opciones de Almacenamiento para más detalles.
plugins
Sección titulada «plugins»Opcional. Array de plugins de EmDash.
import seoPlugin from "@emdash-cms/plugin-seo";
plugins: [seoPlugin()];Opcional. Configuración de autenticación.
auth: { // Configuración de auto-registro selfSignup: { domains: ["example.com"], defaultRole: 20, // Contributor },
// Proveedores OAuth oauth: { github: { clientId: process.env.GITHUB_CLIENT_ID, clientSecret: process.env.GITHUB_CLIENT_SECRET, }, google: { clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, }, },
// Configuración de la sesión session: { maxAge: 30 * 24 * 60 * 60, // 30 days sliding: true, // Reset expiry on activity },
// O usa Cloudflare Access (modo exclusivo) cloudflareAccess: { teamDomain: "myteam.cloudflareaccess.com", audience: "your-app-audience-tag", autoProvision: true, defaultRole: 30, syncRoles: false, roleMapping: { "Admins": 50, "Editors": 40, }, },}auth.selfSignup
Sección titulada «auth.selfSignup»Permite a los usuarios registrarse por sí mismos si su dominio de correo electrónico está permitido.
| Opción | Tipo | Por defecto | Descripción |
|---|---|---|---|
domains | string[] | [] | Dominios de correo permitidos |
defaultRole | number | 20 | Rol para auto-registros |
selfSignup: { domains: ["example.com", "acme.org"], defaultRole: 20, // Contributor}auth.oauth
Sección titulada «auth.oauth»Configura proveedores de inicio de sesión OAuth.
oauth: { github: { clientId: process.env.GITHUB_CLIENT_ID, clientSecret: process.env.GITHUB_CLIENT_SECRET, }, google: { clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, },}auth.session
Sección titulada «auth.session»Configuración de sesión.
| Opción | Tipo | Por defecto | Descripción |
|---|---|---|---|
maxAge | number | 2592000 (30d) | Duración de la sesión en segundos |
sliding | boolean | true | Reiniciar expiración con actividad |
auth.cloudflareAccess
Sección titulada «auth.cloudflareAccess»Usa Cloudflare Access como proveedor de autenticación en lugar de passkeys.
| Opción | Tipo | Por defecto | Descripción |
|---|---|---|---|
teamDomain | string | requerido | Tu dominio de equipo de Access |
audience | string | requerido | Etiqueta de Audiencia de Aplicación (AUD) |
autoProvision | boolean | true | Crear usuarios en el primer inicio de sesión |
defaultRole | number | 30 | Rol por defecto para nuevos usuarios |
syncRoles | boolean | false | Actualizar rol en cada inicio de sesión |
roleMapping | object | — | Mapear grupos del IdP a roles |
passkeyPublicOrigin
Sección titulada «passkeyPublicOrigin»Opcional. Proporciona un origen completo orientado al navegador (esquema + host + puerto opcional, sin ruta) para que el rpId y origin de WebAuthn coincidan con lo que el navegador del usuario envía en clientData.origin.
Por defecto, las passkeys siguen Astro.url / request.url. Detrás de un proxy inverso que termina TLS, la aplicación a menudo aún ve http:// en el salto interno mientras la pestaña está en https://, o el host reconstruido no coincide con el nombre público — lo que rompe la verificación de passkeys. Establece passkeyPublicOrigin al origen que los usuarios escriben en la barra de direcciones (por ejemplo https://cms.example.com o https://cms.example.com:8443).
La integración valida este valor al cargar: debe ser una URL válida con protocolo http: o https: y se normaliza a origin.
emdash({ database: sqlite({ url: "file:./data.db" }), storage: local({ directory: "./uploads", baseUrl: "/_emdash/api/media/file", }), passkeyPublicOrigin: "https://cms.example.com",});Proxy inverso y passkeys
Sección titulada «Proxy inverso y passkeys»Astro solo refleja X-Forwarded-* cuando el host público está permitido. Configura security.allowedDomains para el nombre de host (y esquemas) que tus usuarios visitan. En astro dev, añade vite.server.allowedHosts coincidentes para que Vite acepte el encabezado Host del proxy.
Prefiere arreglar primero allowedDomains (y los encabezados reenviados); usa passkeyPublicOrigin cuando la URL reconstruida aún diverge del origen del navegador (típico cuando TLS se termina al frente y la solicitud ascendente permanece http://).
Con TLS al frente, vincular el servidor de desarrollo al loopback (astro dev --host 127.0.0.1) a menudo es suficiente: el proxy se conecta localmente mientras passkeyPublicOrigin coincide con el origen HTTPS público.
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", }), passkeyPublicOrigin: "https://cms.example.com", }), ],});Adaptadores de Base de Datos
Sección titulada «Adaptadores de Base de Datos»Importa desde emdash/db:
import { sqlite, libsql, postgres, d1 } from "emdash/db";sqlite(config)
Sección titulada «sqlite(config)»Base de datos SQLite usando better-sqlite3.
| Opción | Tipo | Descripción |
|---|---|---|
url | string | Ruta del archivo con prefijo file: |
sqlite({ url: "file:./data.db" });libsql(config)
Sección titulada «libsql(config)»Base de datos libSQL.
| Opción | Tipo | Descripción |
|---|---|---|
url | string | URL de la base de datos |
authToken | string | Token de autenticación (opcional para archivos locales) |
libsql({ url: process.env.LIBSQL_DATABASE_URL, authToken: process.env.LIBSQL_AUTH_TOKEN,});postgres(config)
Sección titulada «postgres(config)»Base de datos PostgreSQL con agrupación de conexiones.
| Opción | Tipo | Descripción |
|---|---|---|
connectionString | string | URL de conexión PostgreSQL |
host | string | Host de la base de datos |
port | number | Puerto de la base de datos |
database | string | Nombre de la base de datos |
user | string | Usuario de la base de datos |
password | string | Contraseña de la base de datos |
ssl | boolean | Habilitar SSL |
pool.min | number | Tamaño mínimo del pool (por defecto: 0) |
pool.max | number | Tamaño máximo del pool (por defecto: 10) |
postgres({ connectionString: process.env.DATABASE_URL });d1(config)
Sección titulada «d1(config)»Base de datos Cloudflare D1. Importa desde @emdash-cms/cloudflare.
| Opción | Tipo | Valor por defecto | Descripción |
|---|---|---|---|
binding | string | — | Nombre del binding D1 desde wrangler.jsonc |
session | string | "disabled" | Modo de réplica de lectura: "disabled", "auto", o "primary-first" |
bookmarkCookie | string | "__ec_d1_bookmark" | Nombre de la cookie para marcadores de sesión |
// Basicd1({ binding: "DB" });
// Con réplicas de lecturad1({ binding: "DB", session: "auto" });Cuando session es "auto" o "primary-first", EmDash utiliza la API de Sesiones de D1 para dirigir consultas de lectura a réplicas cercanas. Los usuarios autenticados obtienen consistencia de lectura-tus-escrituras basada en marcadores. Consulta Opciones de Base de Datos — Réplicas de Lectura para más detalles.
Adaptadores de Almacenamiento
Sección titulada «Adaptadores de Almacenamiento»Importa desde emdash/astro:
import emdash, { local, r2, s3 } from "emdash/astro";local(config)
Sección titulada «local(config)»Almacenamiento en sistema de archivos local.
| Opción | Tipo | Descripción |
|---|---|---|
directory | string | Ruta del directorio |
baseUrl | string | URL base para servir archivos |
local({ directory: "./uploads", baseUrl: "/_emdash/api/media/file",});r2(config)
Sección titulada «r2(config)»Binding de Cloudflare R2.
| Opción | Tipo | Descripción |
|---|---|---|
binding | string | Nombre del binding R2 |
publicUrl | string | URL pública opcional |
r2({ binding: "MEDIA", publicUrl: "https://pub-xxxx.r2.dev",});s3(config)
Sección titulada «s3(config)»Almacenamiento compatible con S3.
| Opción | Tipo | Descripción |
|---|---|---|
endpoint | string | URL del endpoint S3 |
bucket | string | Nombre del bucket |
accessKeyId | string | Clave de acceso |
secretAccessKey | string | Clave secreta |
region | string | Región (por defecto: "auto") |
publicUrl | string | URL de CDN opcional |
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",});Colecciones en Vivo
Sección titulada «Colecciones en Vivo»Configura el cargador de EmDash en src/live.config.ts:
import { defineLiveCollection } from "astro:content";import { emdashLoader } from "emdash/runtime";
export const collections = { _emdash: defineLiveCollection({ loader: emdashLoader(), }),};Opciones del Cargador
Sección titulada «Opciones del Cargador»La función emdashLoader() acepta configuración opcional:
emdashLoader({ // De momento no hay opciones; se reserva para usos futuros});Variables de Entorno
Sección titulada «Variables de Entorno»EmDash respeta estas variables de entorno:
| Variable | Descripción |
|---|---|
EMDASH_DATABASE_URL | Anula la URL de la base de datos |
EMDASH_AUTH_SECRET | Secreto para autenticación con claves de acceso |
EMDASH_PREVIEW_SECRET | Secreto para generación de tokens de vista previa |
EMDASH_URL | URL remota de EmDash para sincronización de esquema |
Genera un secreto de autenticación con:
npx emdash auth secretConfiguración de package.json
Sección titulada «Configuración de package.json»Configuración opcional en package.json:
{ "emdash": { "label": "Plantilla de blog", "description": "Una plantilla de blog limpia y minimalista", "seed": ".emdash/seed.json", "url": "https://my-site.pages.dev", "preview": "https://emdash-blog.pages.dev" }}| Opción | Descripción |
|---|---|
label | Nombre de la plantilla para mostrar |
description | Descripción de la plantilla |
seed | Ruta al archivo JSON de semilla |
url | URL remota para sincronización de esquema |
preview | URL del sitio de demostración para vista previa de plantilla |
Configuración de TypeScript
Sección titulada «Configuración de TypeScript»EmDash genera tipos en .emdash/types.ts. Añade a tu tsconfig.json:
{ "compilerOptions": { "paths": { "@emdash-cms/types": ["./.emdash/types.ts"] } }}Genera tipos con:
npx emdash types