Référence de Configuration

Sur cette page

EmDash est configuré via deux fichiers : astro.config.mjs pour l’intégration et src/live.config.ts pour les collections de contenu.

Intégration Astro

Configurez EmDash comme une intégration Astro dans 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: [],
		}),
	],
});

Options d’intégration

database

Requis. Configuration de l’adaptateur de base de données. Choisissez un adaptateur :

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

Voir Options de base de données pour plus de détails.

storage

Requis. Configuration de l’adaptateur de stockage de médias. Choisissez un adaptateur :

// Système de fichiers local (développement)
storage: local({
	directory: "./uploads",
	baseUrl: "/_emdash/api/media/file",
});

// Liaison R2 (Cloudflare Workers)
storage: r2({
	binding: "MEDIA",
	publicUrl: "https://pub-xxxx.r2.dev", // optionnel
});

// Compatible S3 (n'importe quelle plateforme) — tous les champs des variables d'environnement S3_*
storage: s3()

// Ou avec des valeurs explicites
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", // optionnel, défaut : "auto"
	publicUrl: "https://cdn.example.com", // optionnel
});

Voir Options de stockage pour plus de détails.

plugins

Optionnel. Tableau de plugins EmDash. L’exemple suivant enregistre un plugin :

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

plugins: [seoPlugin()];

fonts

Optionnel. Configuration des polices de l’interface d’administration.

Par défaut, EmDash charge Noto Sans via l’API de polices Astro. Les polices sont téléchargées depuis Google au moment de la construction et auto-hébergées, il n’y a donc pas de requêtes CDN à l’exécution. La police de base couvre les écritures latine, cyrillique, grecque, devanagari et vietnamienne.

Pour ajouter la prise en charge de systèmes d’écriture supplémentaires, passez des noms de scripts. L’exemple suivant ajoute l’arabe et le japonais :

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

Les scripts disponibles sont 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 et tibetan.

Chaque script correspond à la variante Noto Sans correspondante sur Google Fonts (par exemple, "arabic" charge Noto Sans Arabic). Toutes les polices partagent un seul nom font-family et utilisent unicode-range afin que le navigateur ne télécharge que les fichiers dont il a besoin pour les caractères de la page.

Définissez sur false pour désactiver complètement l’injection de polices et utiliser les polices système :

emdash({
	fonts: false,
})

Le CSS d’administration utilise la variable CSS --font-emdash. Ceci est défini automatiquement par la configuration de police ci-dessus.

auth

Optionnel. Un adaptateur d’authentification. La connexion intégrée d’EmDash utilise les passkeys ; définir auth les remplace par un fournisseur externe. L’adaptateur Cloudflare Access, access(), est fourni par @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,
		},
	}),
});

Options pour access() :

OptionTypeDéfautDescription
teamDomainstringrequisVotre domaine d’équipe Cloudflare Access
audiencestringTag d’audience (AUD) de l’application. Sur Workers, préférez audienceEnvVar.
audienceEnvVarstring"CF_ACCESS_AUDIENCE"Variable d’environnement à partir de laquelle lire le tag d’audience à l’exécution
autoProvisionbooleantrueCréer un utilisateur EmDash lors de la première connexion
defaultRolenumber30Niveau de rôle pour les utilisateurs non correspondants à roleMapping (voir Rôles utilisateur)
syncRolesbooleanfalseRéappliquer roleMapping à chaque connexion au lieu de seulement lors du provisionnement
roleMappingobjectMapper les noms de groupes IdP aux niveaux de rôle EmDash ; la première correspondance gagne

authProviders

Optionnel. Un tableau de fournisseurs de connexion enfichables (niveau supérieur, aux côtés de auth). Chaque entrée est le résultat de l’appel d’une fabrique de fournisseur, comme indiqué ci-dessous :

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

Fournisseurs intégrés :

  • github() — lit EMDASH_OAUTH_GITHUB_CLIENT_ID / EMDASH_OAUTH_GITHUB_CLIENT_SECRET (ou replis non préfixés).
  • google() — lit EMDASH_OAUTH_GOOGLE_CLIENT_ID / EMDASH_OAUTH_GOOGLE_CLIENT_SECRET.
  • atproto() — Connexion au compte Atmosphere (Bluesky et le réseau AT Protocol plus large). Aucune variable d’environnement nécessaire. Accepte { allowedDIDs, allowedHandles, defaultRole }. Voir le guide de connexion Atmosphere.

Les packages tiers peuvent enregistrer leurs propres fournisseurs en utilisant la même forme AuthProviderDescriptor — voir Fournisseurs de connexion.

siteUrl

Optionnel. L’origine publique orientée navigateur pour le site (schéma + hôte + port optionnel, pas de chemin).

Derrière un proxy inverse de terminaison TLS, Astro.url renvoie l’adresse interne (http://localhost:4321) au lieu de l’adresse publique (https://cms.example.com). Cela casse les passkeys, la correspondance d’origine CSRF, les redirections OAuth, les redirections de connexion, la découverte MCP, les exports de snapshot, le sitemap, robots.txt et les données structurées JSON-LD. Définissez siteUrl pour tout corriger d’un coup.

L’intégration valide cette valeur au moment du chargement : elle doit être une URL valide avec protocole http: ou https: et est normalisée en origine (le chemin est supprimé).

L’exemple suivant définit l’origine publique :

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

Lorsque siteUrl n’est pas défini dans la configuration, EmDash vérifie les variables d’environnement dans l’ordre : EMDASH_SITE_URL, puis SITE_URL. Ceci est utile pour les déploiements de conteneurs où l’URL publique est définie à l’exécution.

Vérification de passkey multi-origine

siteUrl définit une origine canonique unique. Lorsque le même déploiement EmDash est accessible sous plusieurs noms d’hôtes qui partagent un domaine parent enregistrable (par exemple, https://example.com et https://preview.example.com), la vérification de passkey rejette les assertions dont l’origine ne correspond pas exactement à siteUrl — même si WebAuthn permet aux passkeys d’être valides sur les sous-domaines sous le même rpId.

Déclarez des origines acceptées supplémentaires via allowedOrigins dans astro.config.mjs ou la variable d’environnement EMDASH_ALLOWED_ORIGINS. Le siteUrl canonique reste la source du rpId ; les entrées listées ici sont acceptées au moment de la vérification. Les deux sources sont fusionnées à l’exécution, de sorte que la configuration peut déclarer les origines stables (versionnées, revues de code) tandis que l’environnement ajoute des extras spécifiques à l’environnement (par exemple, aperçus PR éphémères).

L’exemple suivant déclare une origine supplémentaire dans la configuration :

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

Les valeurs équivalentes peuvent également provenir de variables d’environnement :

EMDASH_SITE_URL=https://example.com
EMDASH_ALLOWED_ORIGINS=https://preview.example.com,https://staging.example.com
Validation

EmDash valide ceux-ci pour éviter une configuration morte que le navigateur n’honorerait jamais :

  • Chaque entrée doit être une URL analysable http: ou https: sans point de fin et sans étiquettes vides dans le nom d’hôte.
  • Lorsque allowedOrigins n’est pas vide, siteUrl doit être défini (n’importe quelle source) et ne doit pas être un littéral IP ni avoir un nom d’hôte avec point de fin.
  • Chaque origine doit être le même nom d’hôte que siteUrl ou un sous-domaine de celui-ci. (WebAuthn exige que rpId soit un suffixe enregistrable de chaque origine.)

Lorsque la validation échoue, vous verrez une erreur attribuée à la source comme 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.

L’endroit où l’erreur apparaît dépend de l’endroit où les valeurs sont déclarées :

  • Au démarrage d’Astro, lorsque config.allowedOrigins et config.siteUrl proviennent tous deux de astro.config.mjs — les fautes de frappe dans le code font échouer la construction.
  • À la première vérification de passkey, lorsque l’une ou l’autre valeur provient de EMDASH_ALLOWED_ORIGINS ou EMDASH_SITE_URL — les désaccords d’environnement apparaissent comme des 500 à la première tentative de vérification.

Configuration du proxy inverse

Astro ne reflète X-Forwarded-* que lorsque l’hôte public est autorisé. Configurez security.allowedDomains pour le nom d’hôte (et les schémas) que vos utilisateurs atteignent. Dans astro dev, ajoutez des vite.server.allowedHosts correspondants pour que Vite accepte l’en-tête Host du proxy.

Préférez d’abord corriger allowedDomains (et les en-têtes transférés) ; utilisez siteUrl lorsque l’URL reconstruite diverge toujours de l’origine du navigateur (typique lorsque TLS est terminé à l’avant et la requête en amont reste http://).

Avec TLS à l’avant, lier le serveur de développement à loopback (astro dev --host 127.0.0.1) suffit souvent : le proxy se connecte localement tandis que siteUrl correspond à l’origine HTTPS publique.

Si votre proxy écrit un en-tête IP client, définissez trustedProxyHeaders afin que les limites de débit d’EmDash puissent utiliser l’IP client réelle au lieu de regrouper chaque requête sous une clé “unknown” partagée.

La configuration suivante définit allowedDomains, vite.server.allowedHosts et siteUrl ensemble pour un déploiement de proxy inverse :

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

Optionnel. En-têtes auxquels faire confiance pour la résolution d’IP client lors de l’exécution derrière un proxy inverse que vous contrôlez. Utilisé par les limites de débit d’authentification (magic-link, inscription, passkey, flux de dispositif OAuth) et le point de terminaison de commentaire public.

Sur Cloudflare, l’objet cf attaché à la requête est utilisé automatiquement — vous n’avez normalement pas besoin de définir ceci. Sur les déploiements auto-hébergés derrière nginx, Caddy, Traefik, Fly, Railway ou similaires, définissez ceci sur l’en-tête que votre proxy écrit afin que les limites de débit puissent regrouper par IP client réelle au lieu de traiter chaque requête comme “unknown”.

L’exemple suivant fait confiance à l’en-tête x-real-ip défini par nginx, Caddy ou Traefik :

emdash({
	database: sqlite({ url: "file:./data.db" }),
	trustedProxyHeaders: ["x-real-ip"],
});

Les en-têtes sont essayés dans l’ordre. Les valeurs correspondant à *-forwarded-for sont analysées comme des listes séparées par des virgules et la première entrée est utilisée. L’exemple suivant préfère l’en-tête de Fly.io et se rabat sur x-forwarded-for :

emdash({
	trustedProxyHeaders: ["fly-client-ip", "x-forwarded-for"],
});

Lorsqu’il n’est pas défini dans la configuration, EmDash lit la variable d’environnement EMDASH_TRUSTED_PROXY_HEADERS (séparée par des virgules). Un tableau vide explicite dans la configuration remplace la variable d’environnement.

maxUploadSize

Optionnel. Taille maximale autorisée de téléchargement de fichier média en octets. S’applique aux téléchargements multipart directs et aux téléchargements d’URL signée. Par défaut 52_428_800 (50 Mo). L’exemple suivant augmente la limite à 100 Mo :

emdash({
	database: sqlite({ url: "file:./data.db" }),
	storage: local({
		directory: "./uploads",
		baseUrl: "/_emdash/api/media/file",
	}),
	maxUploadSize: 100 * 1024 * 1024, // 100 MB
});
ValeurDescription
number (octets)Doit être un entier fini positif
omisPar défaut 50 Mo

Les téléchargements qui dépassent la limite configurée sont rejetés avec une réponse 413 Payload Too Large sur le chemin de téléchargement direct, ou une 400 Validation Error sur le chemin d’URL signée.

Adaptateurs de base de données

Importez les adaptateurs depuis emdash/db :

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

sqlite(config)

Base de données SQLite utilisant better-sqlite3. L’exemple suivant se connecte à un fichier local :

OptionTypeDescription
urlstringChemin de fichier avec préfixe file:
sqlite({ url: "file:./data.db" });

libsql(config)

Base de données libSQL. L’exemple suivant se connecte à une base de données libSQL distante :

OptionTypeDescription
urlstringURL de la base de données
authTokenstringJeton d’authentification (optionnel pour les fichiers locaux)
libsql({
	url: process.env.LIBSQL_DATABASE_URL,
	authToken: process.env.LIBSQL_AUTH_TOKEN,
});

postgres(config)

Base de données PostgreSQL avec mise en commun de connexions.

OptionTypeDescription
connectionStringstringURL de connexion PostgreSQL
hoststringHôte de la base de données
portnumberPort de la base de données
databasestringNom de la base de données
userstringUtilisateur de la base de données
passwordstringMot de passe de la base de données
sslbooleanActiver SSL
pool.minnumberTaille minimale du pool (défaut : 0)
pool.maxnumberTaille maximale du pool (défaut : 10)

L’exemple suivant se connecte avec une chaîne de connexion :

postgres({ connectionString: process.env.DATABASE_URL });

d1(config)

Base de données Cloudflare D1. Importer depuis @emdash-cms/cloudflare.

OptionTypeDéfautDescription
bindingstringNom de liaison D1 depuis wrangler.jsonc
sessionstring"disabled"Mode de réplication en lecture : "disabled", "auto" ou "primary-first"
bookmarkCookiestring"__em_d1_bookmark"Nom de cookie pour les signets de session

L’exemple suivant montre une liaison de base et une avec des réplicas de lecture activés :

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

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

Lorsque session est "auto" ou "primary-first", EmDash utilise l’API Sessions D1 pour acheminer les requêtes de lecture vers des réplicas proches. Les utilisateurs authentifiés obtiennent une cohérence lecture-après-écriture basée sur des signets. Voir Options de base de données — Réplicas de lecture pour plus de détails.

Adaptateurs de stockage

Importez local et s3 depuis emdash/astro. L’adaptateur r2 est importé depuis @emdash-cms/cloudflare :

import emdash, { local, s3 } from "emdash/astro";
import { r2 } from "@emdash-cms/cloudflare";

local(config)

Stockage sur système de fichiers local. L’exemple suivant sert les téléchargements depuis un répertoire local :

OptionTypeDescription
directorystringChemin du répertoire
baseUrlstringURL de base pour servir les fichiers
local({
	directory: "./uploads",
	baseUrl: "/_emdash/api/media/file",
});

r2(config)

Liaison Cloudflare R2. L’exemple suivant utilise une liaison R2 avec une URL publique :

OptionTypeDescription
bindingstringNom de liaison R2
publicUrlstringURL publique optionnelle
r2({
	binding: "MEDIA",
	publicUrl: "https://pub-xxxx.r2.dev",
});

s3(config?)

Stockage compatible S3. Tous les champs de configuration sont optionnels : tout champ omis de s3({...}) est résolu depuis la variable d’environnement S3_* correspondante lorsque le processus Node démarre. Les valeurs explicites ont toujours la priorité.

Prérequis : installez @aws-sdk/client-s3 et @aws-sdk/s3-request-presigner dans votre projet. EmDash core n’inclut pas le SDK AWS. Voir Options de stockage : Stockage compatible S3 pour plus de détails.

OptionTypeDescription
endpointstringURL de point de terminaison S3 (S3_ENDPOINT)
bucketstringNom du bucket (S3_BUCKET)
accessKeyIdstringClé d’accès (S3_ACCESS_KEY_ID)
secretAccessKeystringClé secrète (S3_SECRET_ACCESS_KEY)
regionstringRégion, défaut "auto" (S3_REGION)
publicUrlstringURL CDN optionnelle (S3_PUBLIC_URL)

Les exemples suivants résolvent tous les champs depuis l’environnement, mélangent configuration et environnement, ou passent chaque champ explicitement :

// Tous les champs depuis les variables d'environnement S3_* (déploiements de conteneurs Node)
s3()

// Mixte : CDN depuis la configuration, le reste depuis l'environnement
s3({ publicUrl: "https://cdn.example.com" })

// Tout explicite
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 résolution de variables d’environnement à l’exécution est une fonctionnalité Node uniquement. Sur Cloudflare Workers, les secrets et variables sont exposés via le paramètre env du gestionnaire fetch, pas via process.env, donc les variables d’environnement S3_* ne sont pas récupérées. Les déploiements Workers doivent utiliser l’adaptateur r2(config) ou passer des valeurs explicites à s3({...}). Voir Options de stockage pour plus de détails.

Collections en direct

Configurez le chargeur EmDash dans src/live.config.ts :

import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";

export const collections = {
	_emdash: defineLiveCollection({
		loader: emdashLoader(),
	}),
};

Options du chargeur

La fonction emdashLoader() ne prend aucun argument :

emdashLoader();

Variables d’environnement

EmDash respecte ces variables d’environnement :

VariableDescription
EMDASH_SITE_URLOrigine publique orientée navigateur (se rabat sur SITE_URL)
EMDASH_ALLOWED_ORIGINSListe séparée par des virgules d’origines supplémentaires acceptées par la vérification de passkey (déploiements multi-sous-domaines).
EMDASH_DATABASE_URLRemplacer l’URL de la base de données
EMDASH_ENCRYPTION_KEYClé pour chiffrer les secrets de plugins au repos. Fournie par l’opérateur — jamais stockée dans la base de données.
EMDASH_PREVIEW_SECRETRemplacement optionnel pour le secret HMAC d’aperçu. Lorsqu’il n’est pas défini, une valeur stable par site est générée et stockée dans la base de données.
EMDASH_IP_SALTRemplacement optionnel pour le sel de hachage IP du commentateur. Lorsqu’il n’est pas défini, une valeur stable par site est générée et stockée dans la base de données.
EMDASH_AUTH_SECRETHérité. Utilisé comme source de sel IP s’il est défini ; les installations existantes doivent conserver ceci pour préserver les hachages IP de commentateur stables lors des mises à niveau.
EMDASH_URLURL EmDash distante pour la synchronisation de schéma

Générez une clé de chiffrement avec la commande suivante :

npx emdash secrets generate

Configuration de package.json

Les modèles et sites peuvent déclarer des métadonnées optionnelles sous une clé emdash dans package.json :

{
	"emdash": {
		"label": "My Blog Template",
		"seed": ".emdash/seed.json",
		"url": "https://my-site.pages.dev"
	}
}
OptionDescription
labelNom du modèle pour l’affichage
seedChemin vers le fichier JSON de semence
urlURL distante pour la synchronisation de schéma

Configuration TypeScript

EmDash génère des types dans .emdash/types.ts. Ajoutez un alias de chemin à votre tsconfig.json :

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

Générez des types avec la commande suivante :

npx emdash types