Referencia de configuración

En esta página

EmDash se configura a través de dos archivos: astro.config.mjs para la integración y src/live.config.ts para las colecciones de contenido.

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

database

Obligatorio. Configuración del adaptador de base de datos.

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

Consulta Opciones de base de datos para más detalles.

storage

Obligatorio. Configuración del adaptador de almacenamiento multimedia.

// Sistema de archivos local (desarrollo)
storage: local({
	directory: "./uploads",
	baseUrl: "/_emdash/api/media/file",
});

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

Consulta Opciones de almacenamiento para más detalles.

plugins

Opcional. Array de plugins de EmDash.

import seoPlugin from "@emdash-cms/plugin-seo";

plugins: [seoPlugin()];

auth

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 sesión
  session: {
    maxAge: 30 * 24 * 60 * 60, // 30 días
    sliding: true, // Renovar caducidad con actividad
  },
  
  // O usar 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

Permite que los usuarios se auto-registren si su dominio de correo está permitido.

OpciónTipoPor defectoDescripción
domainsstring[][]Dominios de correo permitidos
defaultRolenumber20Rol para auto-registros
selfSignup: {
  domains: ["example.com", "acme.org"],
  defaultRole: 20, // Contributor
}

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

Configuración de sesión.

OpciónTipoPor defectoDescripción
maxAgenumber2592000 (30d)Duración de la sesión en segundos
slidingbooleantrueRenovar caducidad con actividad

auth.cloudflareAccess

Usa Cloudflare Access como proveedor de autenticación en lugar de passkeys.

OpciónTipoPor defectoDescripción
teamDomainstringobligatorioTu dominio de equipo de Access
audiencestringobligatorioEtiqueta Application Audience (AUD)
autoProvisionbooleantrueCrear usuarios en el primer inicio de sesión
defaultRolenumber30Rol por defecto para nuevos usuarios
syncRolesbooleanfalseActualizar rol en cada inicio de sesión
roleMappingobjectMapear grupos del IdP a roles

siteUrl

Opcional. El origen público orientado al navegador del sitio (esquema + host + puerto opcional, sin ruta).

Detrás de un proxy inverso con terminación TLS, Astro.url devuelve la dirección interna (http://localhost:4321) en lugar de la pública (https://cms.example.com). Esto rompe los passkeys, la coincidencia de origen CSRF, las redirecciones OAuth, las redirecciones de inicio de sesión, el descubrimiento MCP, las exportaciones de snapshots, el sitemap, robots.txt y los datos estructurados JSON-LD. Establece siteUrl para corregir todo esto de una vez.

La integración valida este valor al cargar: debe ser una URL válida con protocolo http: o https: y se normaliza a origin (la ruta se elimina).

emdash({
	database: sqlite({ url: "file:./data.db" }),
	storage: local({
		directory: "./uploads",
		baseUrl: "/_emdash/api/media/file",
	}),
	siteUrl: "https://cms.example.com",
});

Cuando siteUrl no está configurado, EmDash comprueba las variables de entorno en orden: EMDASH_SITE_URL, luego SITE_URL. Esto es útil para despliegues en contenedor donde la URL pública se define en tiempo de ejecución.

Configuración del proxy inverso

Astro solo refleja X-Forwarded-* cuando el host público está permitido. Configura security.allowedDomains para el nombre de host (y los esquemas) que usan tus usuarios. En astro dev, añade vite.server.allowedHosts correspondiente para que Vite acepte la cabecera Host del proxy.

Es preferible corregir allowedDomains (y las cabeceras reenviadas) primero; usa siteUrl cuando la URL reconstruida aún diverja del origen del navegador (habitual cuando TLS termina delante y la petición upstream sigue siendo http://).

Con TLS delante, vincular el servidor de desarrollo al loopback (astro dev --host 127.0.0.1) suele ser suficiente: el proxy se conecta localmente mientras siteUrl 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",
			}),
			siteUrl: "https://cms.example.com",
		}),
	],
});

Adaptadores de base de datos

Importar desde emdash/db:

import { sqlite, libsql, postgres, d1 } from "emdash/db";

sqlite(config)

Base de datos SQLite usando better-sqlite3.

OpciónTipoDescripción
urlstringRuta del archivo con prefijo file:
sqlite({ url: "file:./data.db" });

libsql(config)

Base de datos libSQL.

OpciónTipoDescripción
urlstringURL de la base de datos
authTokenstringToken de autenticación (opcional para archivos locales)
libsql({
	url: process.env.LIBSQL_DATABASE_URL,
	authToken: process.env.LIBSQL_AUTH_TOKEN,
});

postgres(config)

Base de datos PostgreSQL con pool de conexiones.

OpciónTipoDescripción
connectionStringstringURL de conexión PostgreSQL
hoststringHost de la base de datos
portnumberPuerto de la base de datos
databasestringNombre de la base de datos
userstringUsuario de la base de datos
passwordstringContraseña de la base de datos
sslbooleanHabilitar SSL
pool.minnumberTamaño mínimo del pool (por defecto: 0)
pool.maxnumberTamaño máximo del pool (por defecto: 10)
postgres({ connectionString: process.env.DATABASE_URL });

d1(config)

Base de datos Cloudflare D1. Importar desde @emdash-cms/cloudflare.

OpciónTipoPor defectoDescripción
bindingstringNombre del binding D1 en wrangler.jsonc
sessionstring"disabled"Modo de replicación de lectura: "disabled", "auto" o "primary-first"
bookmarkCookiestring"__ec_d1_bookmark"Nombre de la cookie para bookmarks de sesión
// Básico
d1({ binding: "DB" });

// Con réplicas de lectura
d1({ binding: "DB", session: "auto" });

Cuando session es "auto" o "primary-first", EmDash usa la API de sesiones D1 para enrutar consultas de lectura a réplicas cercanas. Los usuarios autenticados obtienen consistencia de lectura-tras-escritura basada en bookmarks. Consulta Opciones de base de datos — Réplicas de lectura para más detalles.

Adaptadores de almacenamiento

Importar desde emdash/astro:

import emdash, { local, r2, s3 } from "emdash/astro";

local(config)

Almacenamiento en sistema de archivos local.

OpciónTipoDescripción
directorystringRuta del directorio
baseUrlstringURL base para servir archivos
local({
	directory: "./uploads",
	baseUrl: "/_emdash/api/media/file",
});

r2(config)

Binding de Cloudflare R2.

OpciónTipoDescripción
bindingstringNombre del binding R2
publicUrlstringURL pública opcional
r2({
	binding: "MEDIA",
	publicUrl: "https://pub-xxxx.r2.dev",
});

s3(config?)

Almacenamiento compatible con S3. Todos los campos de configuración son opcionales: cualquier campo omitido en s3({...}) se resuelve desde la variable de entorno S3_* correspondiente cuando el proceso Node se inicia. Los valores explícitos siempre tienen prioridad.

Prerequisito: instala @aws-sdk/client-s3 y @aws-sdk/s3-request-presigner en tu proyecto. El núcleo de EmDash no incluye el AWS SDK. Consulta Opciones de almacenamiento → Almacenamiento compatible con S3 para más detalles.

OpciónTipoDescripción
endpointstringURL del endpoint S3 (S3_ENDPOINT)
bucketstringNombre del bucket (S3_BUCKET)
accessKeyIdstringClave de acceso (S3_ACCESS_KEY_ID)
secretAccessKeystringClave secreta (S3_SECRET_ACCESS_KEY)
regionstringRegión, por defecto "auto" (S3_REGION)
publicUrlstringURL CDN opcional (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 (unchanged from before)
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",
})

La resolución de variables de entorno en tiempo de ejecución es una funcionalidad exclusiva de Node. En Cloudflare Workers, los secretos y variables se exponen a través del parámetro env del manejador fetch, no a través de process.env, por lo que las variables de entorno S3_* no se leen. Los despliegues en Workers deben usar el adaptador r2(config) o pasar valores explícitos a s3({...}). Consulta Opciones de almacenamiento para más detalles.

Live Collections

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

La función emdashLoader() acepta configuración opcional:

emdashLoader({
	// Actualmente sin opciones - reservado para uso futuro
});

Variables de entorno

EmDash respeta estas variables de entorno:

VariableDescripción
EMDASH_SITE_URLOrigen público orientado al navegador (recurre a SITE_URL)
EMDASH_DATABASE_URLSobrescribir la URL de la base de datos
EMDASH_AUTH_SECRETSecreto para autenticación con passkey
EMDASH_PREVIEW_SECRETSecreto para generación de tokens de vista previa
EMDASH_URLURL remota de EmDash para sincronización del esquema

Genera un secreto de autenticación con:

npx emdash auth secret

Configuración en package.json

Configuración opcional en package.json:

{
	"emdash": {
		"label": "My Blog Template",
		"description": "A clean, minimal blog template",
		"seed": ".emdash/seed.json",
		"url": "https://my-site.pages.dev",
		"preview": "https://emdash-blog.pages.dev"
	}
}
OpciónDescripción
labelNombre de la plantilla para mostrar
descriptionDescripción de la plantilla
seedRuta al archivo JSON seed
urlURL remota para sincronización del esquema
previewURL del sitio de demo para vista previa

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