Architecture

Sur cette page

EmDash s’intègre profondément avec Astro pour fournir une expérience CMS complète. Cette page explique les décisions architecturales clés et comment les pièces s’assemblent.

Vue d’ensemble de haut niveau

┌──────────────────────────────────────────────────────────────────┐
│                         Votre site Astro                          │
│                                                                  │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │                   Intégration EmDash                       │  │
│  │                                                            │  │
│  │  ┌──────────────┐   ┌──────────────┐   ┌───────────────┐   │  │
│  │  │   Contenu    │   │    Admin     │   │    Plugins    │   │  │
│  │  │    APIs      │   │    Panneau   │   │               │   │  │
│  │  └──────────────┘   └──────────────┘   └───────────────┘   │  │
│  │                                                            │  │
│  │  ┌──────────────────────────────────────────────────────┐  │  │
│  │  │                    Couche de données                 │  │  │
│  │  │   Base de données (D1/SQLite)  +  Stockage (R2/S3)   │  │  │
│  │  └──────────────────────────────────────────────────────┘  │  │
│  └────────────────────────────────────────────────────────────┘  │
│                                                                  │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │                    Framework Astro                         │  │
│  │         Live Collections · Middleware · Sessions           │  │
│  └────────────────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────────────────┘

EmDash fonctionne comme une intégration Astro. Il injecte des routes pour le panneau d’administration et les API REST, fournit un chargeur de contenu pour les Live Collections et gère les migrations de base de données et les connexions de stockage.

Schéma database-first

Contrairement aux CMS traditionnels qui définissent le schéma dans le code, EmDash stocke les définitions de schéma dans la base de données elle-même. Deux tables système suivent la structure de votre contenu :

  • _emdash_collections — Métadonnées de collection (slug, label, fonctionnalités)
  • _emdash_fields — Définitions de champs pour chaque collection

Lorsque vous créez une collection “products” avec des champs title et price via l’interface d’administration, EmDash :

  1. Insère des enregistrements dans _emdash_collections et _emdash_fields
  2. Exécute ALTER TABLE pour créer ec_products avec les colonnes appropriées

Cette conception permet :

  • Modification du schéma à l’exécution — Créer et éditer des types de contenu sans changements de code ni reconstructions
  • Configuration conviviale pour les non-développeurs — Les éditeurs de contenu peuvent concevoir leur modèle de données via l’interface
  • Vraies colonnes SQL — Indexation appropriée, clés étrangères et optimisation des requêtes

Tables par collection

Chaque collection obtient sa propre table SQLite avec un préfixe ec_ :

-- Créé lors de l'ajout de la collection "posts"
CREATE TABLE ec_posts (
  -- Colonnes système (toujours présentes)
  id TEXT PRIMARY KEY,
  slug TEXT UNIQUE,
  status TEXT DEFAULT 'draft',  -- draft, published, scheduled
  author_id TEXT,
  created_at TEXT DEFAULT (datetime('now')),
  updated_at TEXT DEFAULT (datetime('now')),
  published_at TEXT,
  deleted_at TEXT,              -- Suppression douce
  version INTEGER DEFAULT 1,    -- Verrouillage optimiste

  -- Colonnes de contenu (de vos définitions de champs)
  title TEXT NOT NULL,
  content JSON,                 -- Portable Text
  excerpt TEXT
);

Pourquoi des tables par collection au lieu d’une seule table de contenu avec JSON ?

  • Les vraies colonnes SQL permettent une indexation et des requêtes appropriées
  • Les clés étrangères fonctionnent correctement
  • Le schéma s’autodocumente dans la base de données
  • Pas de surcharge d’analyse JSON pour l’accès aux champs
  • Les outils de base de données peuvent inspecter le schéma directement

Intégration Live Collections

EmDash utilise les Live Collections d’Astro 6 pour servir le contenu à l’exécution. Les changements de contenu sont immédiatement disponibles sans reconstructions statiques.

emdashLoader() implémente l’interface LiveLoader d’Astro :

// src/live.config.ts
import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";

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

Interrogez le contenu en utilisant les fonctions wrapper fournies :

import { getEmDashCollection, getEmDashEntry } from "emdash";

// Obtenir tous les articles publiés
const { entries: posts } = await getEmDashCollection("posts");

// Obtenir les brouillons
const { entries: drafts } = await getEmDashCollection("posts", {
	status: "draft",
});

// Obtenir une seule entrée par slug
const { entry: post } = await getEmDashEntry("posts", "my-post-slug");

Injection de routes

L’intégration EmDash utilise l’API injectRoute d’Astro pour ajouter des routes d’administration et d’API :

Modèle de cheminObjectif
/_emdash/admin/[...path]SPA du panneau d’administration
/_emdash/api/manifestManifeste d’admin (collections, plugins)
/_emdash/api/content/[collection]CRUD pour les entrées de contenu
/_emdash/api/media/*Opérations de bibliothèque média
/_emdash/api/schema/*Gestion du schéma
/_emdash/api/settingsParamètres du site
/_emdash/api/menus/*Menus de navigation
/_emdash/api/taxonomies/*Catégories, tags, taxonomies personnalisées

Les routes sont injectées depuis le package emdash — rien n’est copié dans votre projet.

Couche de données

EmDash utilise Kysely pour des requêtes SQL type-safe sur toutes les bases de données prises en charge :

SQLite

Développement local avec sqlite({ url: "file:./data.db" })

D1

SQL serverless de Cloudflare avec d1({ binding: "DB" })

libSQL

SQLite distant avec libsql({ url: "...", authToken: "..." })

La configuration de la base de données est passée à l’intégration dans astro.config.mjs :

import { defineConfig } from "astro/config";
import emdash from "emdash/astro";
import { sqlite } from "emdash/db";
import { local } from "emdash/storage";

export default defineConfig({
	integrations: [
		emdash({
			database: sqlite({ url: "file:./data.db" }),
			storage: local({
				directory: "./uploads",
				baseUrl: "/_emdash/api/media/file",
			}),
		}),
	],
});

Abstraction du stockage

Les fichiers média sont stockés séparément de la base de données. EmDash prend en charge :

  • Système de fichiers local — Développement et déploiements simples
  • Cloudflare R2 — Stockage d’objets compatible S3 en périphérie
  • Compatible S3 — Tout stockage d’objets compatible S3

Les téléchargements utilisent des URL signées pour des téléchargements directs client-vers-stockage, contournant les limites de taille de corps des Workers.

Architecture des plugins

Les plugins étendent EmDash via un système de hooks inspiré de WordPress :

  • Hooks de contenucontent:beforeSave, content:afterSave, content:beforeDelete, content:afterDelete
  • Hooks médiamedia:beforeUpload, media:afterUpload
  • Stockage isolé — Chaque plugin obtient un accès KV avec espace de noms
  • Extensions d’interface d’administration — Widgets de tableau de bord, pages de paramètres, éditeurs de champs personnalisés

Les plugins peuvent fonctionner en deux modes :

  1. Natif — Accès complet à l’environnement hôte (pour les plugins propriétaires)
  2. Sandboxé — Exécution dans des isolates V8 avec permissions basées sur les capacités (pour les plugins tiers sur Cloudflare)
// astro.config.mjs
import { seoPlugin } from "@emdash-cms/plugin-seo";

emdash({
	plugins: [seoPlugin({ maxTitleLength: 60 })],
});

Flux de requête

Une requête de contenu typique suit ce chemin :

  1. Astro reçoit la requête — Votre composant de page s’exécute
  2. Interroger le contenugetEmDashCollection() appelle getLiveCollection() d’Astro
  3. Le chargeur s’exécuteemdashLoader interroge la table ec_* appropriée via Kysely
  4. Données retournées — Les entrées sont mappées au format d’entrée d’Astro avec id, slug et data
  5. La page se rend — Votre composant reçoit le contenu et rend le HTML

Pour les requêtes d’administration :

  1. Le middleware authentifie — Valide le jeton de session
  2. La route API gère la requête — Opérations CRUD via les dépôts
  3. Les hooks se déclenchentbeforeCreate, afterUpdate, etc.
  4. Mise à jour de la base de données — Kysely exécute SQL
  5. Réponse retournée — Réponse JSON au SPA d’administration

Modules virtuels

EmDash génère des modules virtuels au moment de la construction pour configurer le runtime :

ModuleObjectif
virtual:emdash/configConfiguration de base de données et stockage
virtual:emdash/dialectFabrique de dialecte de base de données
virtual:emdash/plugin-adminsImports statiques pour les interfaces d’administration des plugins

Cette approche garantit que les bundlers peuvent correctement résoudre et tree-shake le code des plugins.

Prochaines étapes