EmDash si integra profondamente con Astro per fornire un’esperienza CMS completa. Questa pagina spiega le decisioni architetturali chiave e come i pezzi si incastrano.
Panoramica di alto livello
┌──────────────────────────────────────────────────────────────────┐
│ Il tuo sito Astro │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Integrazione EmDash │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌───────────────┐ │ │
│ │ │ Contenuto │ │ Admin │ │ Plugin │ │ │
│ │ │ APIs │ │ Pannello │ │ │ │ │
│ │ └──────────────┘ └──────────────┘ └───────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────┐ │ │
│ │ │ Layer dati │ │ │
│ │ │ Database (D1/SQLite) + Storage (R2/S3) │ │ │
│ │ └──────────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Framework Astro │ │
│ │ Live Collections · Middleware · Sessioni │ │
│ └────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
EmDash funziona come integrazione Astro. Inietta route per il pannello di amministrazione e le API REST, fornisce un loader di contenuti per Live Collections e gestisce migrazioni del database e connessioni di storage.
Schema database-first
A differenza dei CMS tradizionali che definiscono lo schema nel codice, EmDash memorizza le definizioni dello schema nel database stesso. Due tabelle di sistema tracciano la struttura del contenuto:
_emdash_collections— Metadati della collection (slug, label, funzionalità)_emdash_fields— Definizioni dei campi per ogni collection
Quando crei una collection “products” con campi title e price tramite l’interfaccia di amministrazione, EmDash:
- Inserisce record in
_emdash_collectionse_emdash_fields - Esegue
ALTER TABLEper creareec_productscon le colonne appropriate
Questo design abilita:
- Modifica dello schema a runtime — Creare e modificare tipi di contenuto senza modifiche al codice o rebuild
- Setup friendly per non-sviluppatori — Gli editor di contenuti possono progettare il loro modello dati tramite l’interfaccia
- Vere colonne SQL — Indicizzazione appropriata, chiavi esterne e ottimizzazione delle query
Tabelle per collection
Ogni collection ottiene la propria tabella SQLite con prefisso ec_:
-- Creata quando viene aggiunta la collection "posts"
CREATE TABLE ec_posts (
-- Colonne di sistema (sempre presenti)
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, -- Soft delete
version INTEGER DEFAULT 1, -- Optimistic locking
-- Colonne di contenuto (dalle definizioni dei campi)
title TEXT NOT NULL,
content JSON, -- Portable Text
excerpt TEXT
);
Perché tabelle per collection invece di una singola tabella di contenuto con JSON?
- Le vere colonne SQL abilitano indicizzazione e query appropriate
- Le chiavi esterne funzionano correttamente
- Lo schema è auto-documentante nel database
- Nessun overhead di parsing JSON per l’accesso ai campi
- Gli strumenti di database possono ispezionare lo schema direttamente
Integrazione Live Collections
EmDash usa le Live Collections di Astro 6 per servire contenuti a runtime. Le modifiche ai contenuti sono immediatamente disponibili senza rebuild statici.
emdashLoader() implementa l’interfaccia LiveLoader di Astro:
// src/live.config.ts
import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";
export const collections = {
_emdash: defineLiveCollection({ loader: emdashLoader() }),
};
Interroga il contenuto usando le funzioni wrapper fornite:
import { getEmDashCollection, getEmDashEntry } from "emdash";
// Ottieni tutti i post pubblicati
const { entries: posts } = await getEmDashCollection("posts");
// Ottieni le bozze
const { entries: drafts } = await getEmDashCollection("posts", {
status: "draft",
});
// Ottieni una singola voce per slug
const { entry: post } = await getEmDashEntry("posts", "my-post-slug");
Iniezione di route
L’integrazione EmDash usa l’API injectRoute di Astro per aggiungere route di amministrazione e API:
| Pattern del percorso | Scopo |
|---|---|
/_emdash/admin/[...path] | SPA del pannello di amministrazione |
/_emdash/api/manifest | Manifest admin (collection, plugin) |
/_emdash/api/content/[collection] | CRUD per voci di contenuto |
/_emdash/api/media/* | Operazioni libreria media |
/_emdash/api/schema/* | Gestione schema |
/_emdash/api/settings | Impostazioni del sito |
/_emdash/api/menus/* | Menu di navigazione |
/_emdash/api/taxonomies/* | Categorie, tag, tassonomie personalizzate |
Le route sono iniettate dal package emdash — nulla viene copiato nel tuo progetto.
Layer dati
EmDash usa Kysely per query SQL type-safe su tutti i database supportati:
SQLite
Sviluppo locale con sqlite({ url: "file:./data.db" })
D1
SQL serverless di Cloudflare con d1({ binding: "DB" })
libSQL
SQLite remoto con libsql({ url: "...", authToken: "..." })
La configurazione del database viene passata all’integrazione in 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",
}),
}),
],
});
Astrazione dello storage
I file multimediali sono memorizzati separatamente dal database. EmDash supporta:
- Filesystem locale — Sviluppo e deployment semplici
- Cloudflare R2 — Object storage compatibile S3 all’edge
- Compatibile S3 — Qualsiasi object storage compatibile S3
Gli upload usano URL firmati per upload diretti client-to-storage, bypassando i limiti di dimensione del body dei Workers.
Architettura dei plugin
I plugin estendono EmDash tramite un sistema di hook ispirato a WordPress:
- Hook di contenuto —
content:beforeSave,content:afterSave,content:beforeDelete,content:afterDelete - Hook media —
media:beforeUpload,media:afterUpload - Storage isolato — Ogni plugin ottiene accesso KV con namespace
- Estensioni UI admin — Widget dashboard, pagine impostazioni, editor di campi personalizzati
I plugin possono funzionare in due modalità:
- Native — Accesso completo all’ambiente host (per plugin proprietari)
- Sandboxed — Esecuzione in isolate V8 con permessi basati su capability (per plugin di terze parti su Cloudflare)
// astro.config.mjs
import { seoPlugin } from "@emdash-cms/plugin-seo";
emdash({
plugins: [seoPlugin({ maxTitleLength: 60 })],
});
Flusso delle richieste
Una tipica richiesta di contenuto segue questo percorso:
- Astro riceve la richiesta — Il componente della tua pagina viene eseguito
- Interroga il contenuto —
getEmDashCollection()chiamagetLiveCollection()di Astro - Il loader viene eseguito —
emdashLoaderinterroga la tabellaec_*appropriata tramite Kysely - Dati restituiti — Le voci sono mappate al formato di voce di Astro con
id,slugedata - La pagina viene renderizzata — Il tuo componente riceve il contenuto e renderizza HTML
Per le richieste admin:
- Il middleware autentica — Valida il token di sessione
- La route API gestisce la richiesta — Operazioni CRUD tramite repository
- Gli hook vengono attivati —
beforeCreate,afterUpdate, ecc. - Aggiornamenti database — Kysely esegue SQL
- Risposta restituita — Risposta JSON alla SPA admin
Moduli virtuali
EmDash genera moduli virtuali al momento del build per configurare il runtime:
| Modulo | Scopo |
|---|---|
virtual:emdash/config | Configurazione database e storage |
virtual:emdash/dialect | Factory dialect database |
virtual:emdash/plugin-admins | Import statici per UI admin plugin |
Questo approccio garantisce che i bundler possano risolvere correttamente e tree-shake il codice dei plugin.
Prossimi passi
Collection
Scopri le collection di contenuti e i tipi di campi.
Modello di contenuto
Comprendi il modello di contenuto database-first.
Pannello admin
Esplora l’architettura del pannello di amministrazione.