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 en tant qu’intégration 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: [],
}),
],
});
Options d’intégration
database
Obligatoire. Configuration de l’adaptateur de base de données.
// 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
Obligatoire. Configuration de l’adaptateur de stockage multimédia.
// Système de fichiers local (développement)
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
});
Voir Options de stockage pour plus de détails.
plugins
Optionnel. Tableau de plugins EmDash.
import seoPlugin from "@emdash-cms/plugin-seo";
plugins: [seoPlugin()];
auth
Optionnel. Configuration de l’authentification.
auth: {
// Configuration de l'inscription libre
selfSignup: {
domains: ["example.com"],
defaultRole: 20, // Contributor
},
// Fournisseurs 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,
},
},
// Configuration des sessions
session: {
maxAge: 30 * 24 * 60 * 60, // 30 jours
sliding: true, // Réinitialiser l'expiration à chaque activité
},
// OU utiliser Cloudflare Access (mode exclusif)
cloudflareAccess: {
teamDomain: "myteam.cloudflareaccess.com",
audience: "your-app-audience-tag",
autoProvision: true,
defaultRole: 30,
syncRoles: false,
roleMapping: {
"Admins": 50,
"Editors": 40,
},
},
}
auth.selfSignup
Autorisez les utilisateurs à s’inscrire si leur domaine e-mail est autorisé.
| Option | Type | Défaut | Description |
|---|---|---|---|
domains | string[] | [] | Domaines e-mail autorisés |
defaultRole | number | 20 | Rôle pour les auto-inscriptions |
selfSignup: {
domains: ["example.com", "acme.org"],
defaultRole: 20, // Contributor
}
auth.oauth
Configurez les fournisseurs de connexion 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
Configuration des sessions.
| Option | Type | Défaut | Description |
|---|---|---|---|
maxAge | number | 2592000 (30j) | Durée de vie de la session en secondes |
sliding | boolean | true | Réinitialiser l’expiration à chaque activité |
auth.cloudflareAccess
Utilisez Cloudflare Access comme fournisseur d’authentification au lieu des passkeys.
| Option | Type | Défaut | Description |
|---|---|---|---|
teamDomain | string | requis | Domaine de votre équipe Access |
audience | string | requis | Application Audience (AUD) tag |
autoProvision | boolean | true | Créer les utilisateurs à la première connexion |
defaultRole | number | 30 | Rôle par défaut pour les nouveaux utilisateurs |
syncRoles | boolean | false | Mettre à jour le rôle à chaque connexion |
roleMapping | object | — | Mapper les groupes IdP aux rôles |
siteUrl
Optionnel. L’origine publique accessible au navigateur pour le site (schéma + hôte + port optionnel, pas de chemin).
Derrière un proxy inverse terminant 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 snapshots, le sitemap, robots.txt et les données structurées JSON-LD. Définir siteUrl corrige tout cela en une seule fois.
L’intégration valide cette valeur au chargement : elle doit être une URL valide avec le protocole http: ou https: et est normalisée en origin (le chemin est supprimé).
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. Cela est utile pour les déploiements en conteneur où l’URL publique est définie à l’exécution.
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) utilisés par vos utilisateurs. Dans astro dev, ajoutez vite.server.allowedHosts correspondant pour que Vite accepte l’en-tête Host du proxy.
Préférez corriger allowedDomains (et les en-têtes transférés) en premier ; utilisez siteUrl lorsque l’URL reconstruite diverge toujours de l’origine du navigateur (typique lorsque TLS se termine devant et que la requête en amont reste en http://).
Avec TLS en frontal, lier le serveur de développement au loopback (astro dev --host 127.0.0.1) est souvent suffisant : le proxy se connecte localement tandis que siteUrl correspond à l’origine HTTPS publique.
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",
}),
],
});
Adaptateurs de base de données
Import depuis emdash/db :
import { sqlite, libsql, postgres, d1 } from "emdash/db";
sqlite(config)
Base de données SQLite utilisant better-sqlite3.
| Option | Type | Description |
|---|---|---|
url | string | Chemin de fichier avec préfixe file: |
sqlite({ url: "file:./data.db" });
libsql(config)
Base de données libSQL.
| Option | Type | Description |
|---|---|---|
url | string | URL de la base de données |
authToken | string | Jeton d’authentification (optionnel en local) |
libsql({
url: process.env.LIBSQL_DATABASE_URL,
authToken: process.env.LIBSQL_AUTH_TOKEN,
});
postgres(config)
Base de données PostgreSQL avec pool de connexions.
| Option | Type | Description |
|---|---|---|
connectionString | string | URL de connexion PostgreSQL |
host | string | Hôte de la base de données |
port | number | Port de la base de données |
database | string | Nom de la base de données |
user | string | Utilisateur de la base de données |
password | string | Mot de passe de la base de données |
ssl | boolean | Activer SSL |
pool.min | number | Taille minimale du pool (défaut : 0) |
pool.max | number | Taille maximale du pool (défaut : 10) |
postgres({ connectionString: process.env.DATABASE_URL });
d1(config)
Base de données Cloudflare D1. Import depuis @emdash-cms/cloudflare.
| Option | Type | Défaut | Description |
|---|---|---|---|
binding | string | — | Nom du binding D1 depuis wrangler.jsonc |
session | string | "disabled" | Mode de réplication en lecture : "disabled", "auto" ou "primary-first" |
bookmarkCookie | string | "__ec_d1_bookmark" | Nom du cookie pour les signets de session |
// Basique
d1({ binding: "DB" });
// Avec répliques de lecture
d1({ binding: "DB", session: "auto" });
Lorsque session est "auto" ou "primary-first", EmDash utilise l’API D1 Sessions pour acheminer les requêtes de lecture vers les répliques à proximité. Les utilisateurs authentifiés bénéficient d’une cohérence lecture-écriture basée sur les signets. Voir Options de base de données — Répliques de lecture pour plus de détails.
Adaptateurs de stockage
Import depuis emdash/astro :
import emdash, { local, r2, s3 } from "emdash/astro";
local(config)
Stockage sur le système de fichiers local.
| Option | Type | Description |
|---|---|---|
directory | string | Chemin du répertoire |
baseUrl | string | URL de base pour servir les fichiers |
local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
});
r2(config)
Binding Cloudflare R2.
| Option | Type | Description |
|---|---|---|
binding | string | Nom du binding R2 |
publicUrl | string | URL 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 dans s3({...}) est résolu depuis la variable d’environnement S3_* correspondante au démarrage du processus Node. Les valeurs explicites ont toujours la priorité.
Prérequis : installez @aws-sdk/client-s3 et @aws-sdk/s3-request-presigner dans votre projet. Le noyau d’EmDash ne fournit pas le SDK AWS. Voir Options de stockage → Stockage compatible S3 pour plus de détails.
| Option | Type | Description |
|---|---|---|
endpoint | string | URL du point de terminaison S3 (S3_ENDPOINT) |
bucket | string | Nom du bucket (S3_BUCKET) |
accessKeyId | string | Clé d’accès (S3_ACCESS_KEY_ID) |
secretAccessKey | string | Clé secrète (S3_SECRET_ACCESS_KEY) |
region | string | Région, défaut "auto" (S3_REGION) |
publicUrl | string | URL CDN optionnelle (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 résolution des 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, et non via process.env, donc les variables d’environnement S3_* ne sont pas lues. 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.
Live Collections
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() accepte une configuration optionnelle :
emdashLoader({
// Aucune option actuellement - réservé pour une utilisation future
});
Variables d’environnement
EmDash respecte ces variables d’environnement :
| Variable | Description |
|---|---|
EMDASH_SITE_URL | Origine publique côté navigateur (se rabat sur SITE_URL) |
EMDASH_DATABASE_URL | Remplacer l’URL de la base de données |
EMDASH_AUTH_SECRET | Secret pour l’authentification par passkey |
EMDASH_PREVIEW_SECRET | Secret pour la génération de jetons d’aperçu |
EMDASH_URL | URL EmDash distante pour la synchronisation du schéma |
Générez un secret d’authentification avec :
npx emdash auth secret
Configuration package.json
Configuration optionnelle dans 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"
}
}
| Option | Description |
|---|---|
label | Nom du modèle à afficher |
description | Description du modèle |
seed | Chemin vers le fichier JSON seed |
url | URL distante pour la synchronisation du schéma |
preview | URL du site de démo pour l’aperçu du modèle |
Configuration TypeScript
EmDash génère les types dans .emdash/types.ts. Ajoutez à votre tsconfig.json :
{
"compilerOptions": {
"paths": {
"@emdash-cms/types": ["./.emdash/types.ts"]
}
}
}
Générez les types avec :
npx emdash types