ignis-cms
v0.1.3
Published
Ein leichtgewichtiges, git-basiertes CMS als npm-Package für Next.js App Router Projekte.
Readme
ignis-cms
Ein leichtgewichtiges, git-basiertes CMS als npm-Package für Next.js App Router Projekte.
Das Package liest YAML-Dateien aus einem /src/content-Verzeichnis, generiert automatisch eine CMS-Oberfläche unter /admin, und schreibt Änderungen per GitHub API direkt ins Repository zurück.
Kernprinzipien
- Git ist die Datenbank. Alle Inhalte leben als YAML-Dateien im Repo. Versionierung und Rollback kommen gratis über Git.
- Zero Config. Package installieren, Admin-Route einhängen, Env-Vars setzen — fertig. Kein Setup-Wizard, keine Config-Datei.
- Schema-Ableitung. Feldtypen werden automatisch aus dem YAML-Wert ermittelt. Kein separates Schema nötig.
- GitHub API statt lokalem Filesystem. Funktioniert auf Vercel und allen serverlosen Hosts ohne Einschränkungen.
Installation
npm install ignis-cmsKein transpilePackages nötig. ignis-cms wird als gebautes Package aus dist/ konsumiert.
Setup
1. Admin-UI
CmsClient muss im Layout leben, damit der Zustand bei der Navigation erhalten bleibt. Die Styles werden einmal im Layout importiert, Fonts und optionale Feature-Flags bleiben in der Host-App.
// app/admin/layout.tsx
import { CmsClient } from "ignis-cms/ui";
import "ignis-cms/ui/styles.css";
export default function AdminLayout() {
const aiEnabled = !!process.env.OPENROUTER_API_KEY;
return <CmsClient aiEnabled={aiEnabled} />;
}// app/admin/[[...path]]/page.tsx
export default function AdminPage() {
return null; // Layout übernimmt alles
}2. API-Route
Zero-Config
// app/api/cms/[...action]/route.ts
export const runtime = "nodejs";
export { GET, POST, PUT, DELETE, PATCH } from "ignis-cms/api";Konfigurierbar
// app/api/cms/[...action]/route.ts
import { cmsApiHandler } from "ignis-cms/api";
const github = {
token: process.env.GITHUB_TOKEN ?? "",
owner: process.env.GITHUB_OWNER ?? "",
repo: process.env.GITHUB_REPO ?? "",
branch: process.env.GITHUB_BRANCH ?? "main",
appBasePath: process.env.GITHUB_APP_BASE_PATH ?? "",
committerName: process.env.GITHUB_COMMITTER_NAME ?? "ignis CMS",
committerEmail: process.env.GITHUB_COMMITTER_EMAIL ?? "[email protected]",
};
export const runtime = "nodejs";
export const { GET, POST, PUT, DELETE, PATCH } = cmsApiHandler(
{ contentDir: "./src/content", uploadsDir: "./public/uploads" },
{ github }
);3. Umgebungsvariablen
# .env.local
# CMS Auth (Pflicht)
IGNIS_CMS_AUTH_SECRET=<zufälliger-geheimer-string>
IGNIS_CMS_BASE_URL=http://localhost:3001
# GitHub API — Content Reads/Writes (Pflicht)
GITHUB_TOKEN=<fine-grained-personal-access-token>
GITHUB_OWNER=<org-oder-username>
GITHUB_REPO=<repo-name>
GITHUB_BRANCH=main
# Monorepo: Pfad zur Next.js App relativ zum Repo-Root, z.B. "apps/demo"
GITHUB_APP_BASE_PATH=
GITHUB_COMMITTER_NAME=ignis CMS
[email protected]
# Passwort-Reset per Mail (optional)
MAILERSEND_API_KEY=
MAILERSEND_FROM_EMAIL=
MAILERSEND_FROM_NAME=
# AI YAML-Assistent (optional)
OPENROUTER_API_KEY=GitHub Token Berechtigungen: Contents (Read & Write), Metadata (Read).
4. Inhalt ins Repo legen
Minimal:
src/content/
_users.yamlBeispiel:
# src/content/_users.yaml
users:
- email: [email protected]
name: Admin
password: "scrypt:SALT:HASH"
role: adminBeim ersten Aufruf initialisiert ignis-cms fehlende Basisdateien selbst.
Erster Start — Auto-Init
Beim ersten Aufruf des CMS (kein /src/content-Ordner im Repo) wird automatisch ein Grundgerüst angelegt:
src/content/
settings.yaml ← Globale Einstellungen (Firmenname, SEO, Header, Footer)
_assets.yaml ← Asset-Index (wird automatisch verwaltet)
pages/
home.yaml ← Starter-SeiteKein manuelles Anlegen nötig — einfach aufrufen und loslegen.
Benutzer verwalten
Benutzer werden in src/content/_users.yaml verwaltet. Im CMS selbst ist die Benutzerverwaltung für Admins über die Seitenleiste erreichbar.
# src/content/_users.yaml
users:
- email: [email protected]
name: Admin
password: "scrypt:SALT:HASH"
role: admin
- email: [email protected]
name: Editor
password: "scrypt:SALT:HASH"
role: editorPasswort-Hash erzeugen:
import { hashPassword } from "ignis-cms";
console.log(hashPassword("mein-sicheres-passwort"));
// → scrypt:abc123...:def456...Zwei Rollen:
- admin — Neue Dokumente anlegen, löschen, Benutzer verwalten
- editor — Nur bestehende Dokumente bearbeiten
Was ignis-cms selbst mitbringt
- Session-Auth über signiertes
httpOnlyCookie - Login, Logout, Session-Check unter
/api/cms/login,/api/cms/logout,/api/cms/session - Setup-Flow für ersten Admin unter
/api/cms/setup - Passwort-Reset unter
/api/cms/password-resetwenn MailerSend konfiguriert ist - Content-, Asset- und Benutzer-API unter
/api/cms/*
Kein NextAuth, keine separate Auth-Route, keine lokale Git-Integration nötig.
Content-Struktur
src/content/
blog/ ← Collection (Ordner = mehrere Dokumente)
post-1.yaml
post-2.yaml
pages/ ← Collection
home.yaml
about.yaml
settings.yaml ← Singleton (direkt in /src/content, kein Ordner)
_assets.yaml ← Reserviert (Asset-Index, kein Content)
_users.yaml ← Reserviert (Benutzer, kein Content)Dateien mit _-Prefix sind reserviert und erscheinen nicht als Content in der UI.
Schema-Ableitung
Feldtypen werden automatisch aus dem YAML-Wert ermittelt:
| YAML-Wert | Feld-Typ | UI-Komponente |
|---|---|---|
| "Einzeiliger Text" | text | Text Input |
| mehrzeiliger String mit \| | richtext | Textarea |
| "/uploads/bild.jpg" | image | Asset Picker (Bilder) |
| "/uploads/doc.pdf" | file | Asset Picker (alle Typen) |
| 2025-03-15 | date | Date Picker |
| true / false | boolean | Toggle |
| 42 / 3.14 | number | Number Input |
| ["a", "b"] | list | Tag-Liste |
| [{label: "X", url: "/"}] | list_objects | Verschachteltes Formular |
| {title: "...", body: "..."} | group | Fieldset / Accordion |
Schema-Override (optional)
Für Select-Felder oder Validierung kann pro Collection eine _schema.yaml angelegt werden:
# src/content/blog/_schema.yaml
fields:
category:
type: select
options:
- tutorial
- news
- reviewYAML-Editor
Jedes Dokument lässt sich über den YAML-Button in der Toolbar als Roh-YAML bearbeiten. Der Editor bietet:
- Syntax-Highlighting
- Echtzeit-Fehler- und Warnungsanzeige
- Formatieren-Button (nur bei gültigem YAML aktiv)
- Tab fügt 2 Leerzeichen ein
Wechsel zwischen Formular- und YAML-Modus ist verlustfrei — Änderungen werden sofort übernommen.
AI YAML-Assistent
Wenn OPENROUTER_API_KEY gesetzt ist, erscheint im YAML-Editor eine KI-Eingabezeile. Eingabe in natürlicher Sprache — das Modell (Claude 3.5 Haiku via OpenRouter) ergänzt oder verändert das YAML entsprechend.
Beispiele:
- „Füge ein neues Feld og_image hinzu"
- „Ändere den Titel auf Hallo Welt"
- „Ergänze drei weitere Navigationseinträge"
Asset Library
Dateien werden nach /public/uploads/ hochgeladen (max. 10 MB). Metadaten (Titel, Alt-Text) werden automatisch in src/content/_assets.yaml gespeichert. Image- und File-Felder öffnen einen Asset Picker.
Erlaubte Dateitypen: JPG, PNG, GIF, WebP, SVG, AVIF, PDF, DOC, DOCX, XLS, XLSX.
Passwort-Reset per E-Mail (optional)
Wenn MAILERSEND_API_KEY gesetzt ist, erscheint auf der Login-Seite ein „Passwort vergessen"-Link. Der Reset-Link ist 1 Stunde gültig und wird über MailerSend versendet.
Tastaturkürzel
| Kürzel | Funktion |
|---|---|
| Cmd+S / Ctrl+S | Dokument speichern |
Git-Commits
Jede schreibende Operation erzeugt automatisch einen Git Commit via GitHub API:
| Operation | Commit Message |
|---|---|
| Dokument anlegen | content: create blog/post-1 |
| Dokument bearbeiten | content: update blog/post-1 |
| Dokument duplizieren | content: create blog/post-1-kopie |
| Dokument löschen | content: delete blog/post-1 |
| Asset hochladen | assets: upload hero.jpg |
| Asset-Metadaten | assets: update metadata hero.jpg |
| Asset löschen | assets: delete hero.jpg |
| Passwort geändert | users: update account [email protected] |
| Passwort zurückgesetzt | users: reset password [email protected] |
Anforderungen
- Next.js 14+ (App Router)
- Node.js 18+
- GitHub Repository (Public oder Private)
- Fine-grained GitHub Token: Contents (Read & Write), Metadata (Read)
Lizenz
MIT
ignis CMS — Migration Prompt
Diesen Prompt an eine KI übergeben, um eine bestehende Website ins ignis CMS zu migrieren.
Du migrierst eine bestehende Website ins ignis CMS. Das ignis CMS speichert alle Inhalte als YAML-Dateien in einem /src/content-Verzeichnis und schreibt sie via GitHub API ins Repository.
## Deine Aufgabe
Analysiere die Website (URL, Screenshot, HTML oder Beschreibung) und erstelle für jede Seite eine strukturierte YAML-Datei. Alle Inhalte werden 1:1 übernommen — kein Text wird verändert, gekürzt oder umformuliert.
## Dateistruktur
src/content/
pages/
home.yaml <- Startseite
about.yaml <- Über uns
leistungen.yaml <- Leistungsseite
kontakt.yaml <- Kontaktseite
[weitere].yaml
settings.yaml <- Globale Einstellungen (Firmenname, SEO, Navigation, Footer)
Kein globals/-Ordner. Navigation, Footer und SEO-Defaults gehören alle in settings.yaml.
## settings.yaml Struktur
companyName: "Firmenname GmbH"
seo:
title: "Firmenname GmbH"
description: "Kurze Beschreibung für Google (max. 160 Zeichen)."
keywords: "keyword1, keyword2"
ogImage: "/uploads/og-image.jpg"
header:
logo: "/uploads/logo.svg"
navigation:
- label: "Startseite"
url: "/"
- label: "Leistungen"
url: "/leistungen"
- label: "Über uns"
url: "/ueber-uns"
- label: "Kontakt"
url: "/kontakt"
cta:
label: "Anfragen"
url: "/kontakt"
footer:
tagline: "Ihr Partner für..."
links:
- label: "Impressum"
url: "/impressum"
- label: "Datenschutz"
url: "/datenschutz"
copyright: "© 2025 Firmenname GmbH"
## Sektions-Typen
Erkenne Sektionen anhand der visuellen Struktur. Typische Sektionen (beispielsweise, nicht exklusiv):
- hero — Hauptbanner: Überschrift, Subtext, CTA-Button, Bild/Video
- intro — Kurze Einleitung, oft zentriert
- features — Vorteile/Leistungen als Liste von Objekten
- about — Über-uns-Abschnitt, Bild + Text
- team — Personen-Grid (Liste mit name, role, image)
- testimonials — Kundenstimmen (Liste mit quote, author, company)
- gallery — Bildergalerie (Liste von Bild-Pfaden)
- cta — Call-to-Action-Block
- contact — Kontaktinformationen
- faq — Häufige Fragen (Liste mit question + answer)
- stats — Kennzahlen (Liste mit value + label)
- logos — Partner-Logos (Liste von Bild-Pfaden)
- text — Reiner Textblock (für rechtliche Seiten)
## Feldtypen
# Einzeilige Texte -> einfache Strings
title: "Willkommen bei uns"
button_text: "Jetzt anfragen"
button_url: "/kontakt"
# Mehrzeilige Texte / Absätze -> Block-Scalar mit |
body: |
Hier steht ein längerer Text.
Er kann über mehrere Zeilen gehen.
# Bilder -> Pfad (Platzhalter wenn noch kein Upload)
image: "/uploads/hero-bild.jpg"
# Video-URLs
video_url: "https://www.youtube.com/watch?v=..."
video_url: "/uploads/hero-video.mp4"
# Booleans
visible: true
# Listen von Strings
tags:
- "Solar"
- "Elektro"
# Listen von Objekten (Features, Team, FAQ, etc.)
items:
- title: "Leistung 1"
description: "Beschreibung der Leistung."
icon: "check"
- title: "Leistung 2"
description: "Weitere Leistung."
icon: "shield"
## YAML-Template pro Seite
# Seitenweite SEO (überschreibt settings.yaml Defaults)
seo:
title: "Seitentitel | Firmenname"
description: "Kurze Beschreibung der Seite (max. 160 Zeichen)."
hero:
title: "Hauptüberschrift"
subtitle: "Kurzer Satz darunter"
body: |
Optionaler längerer Text.
image: "/uploads/hero.jpg"
button:
text: "Jetzt kontaktieren"
href: "/kontakt"
intro:
title: "Wer wir sind"
body: |
Fließtext der Einleitung.
features:
title: "Unsere Leistungen"
items:
- title: "Leistung 1"
description: "Beschreibung."
icon: "zap"
- title: "Leistung 2"
description: "Beschreibung."
icon: "shield"
ctaSection:
title: "Bereit loszulegen?"
body: "Kontaktieren Sie uns noch heute."
button:
text: "Jetzt kontaktieren"
href: "/kontakt"
## Bilder
### Mit Tool-Zugriff (empfohlen)
1. Website analysieren -> alle Bild-URLs sammeln
2. Für jedes Bild: curl -L "<url>" -o /tmp/name.jpg, dann POST an /api/cms/upload
3. Zurückgegebenen Pfad (/uploads/name.jpg) im YAML verwenden
4. YouTube/Vimeo: URL direkt als String (kein Download)
5. Selbst gehostete Videos: herunterladen -> hochladen wie Bilder
Dateinamen: Englisch, kebab-case (hero-solar-panel.jpg, team-max-mueller.jpg).
### Ohne Tool-Zugriff
- Platzhalter eintragen: /uploads/beschreibender-name.jpg
- Am Ende Liste aller Platzhalter für manuellen Upload ausgeben
## Wichtige Regeln
1. Kein Inhalt weglassen — Jeder Text, jedes Bild, jede Information übernehmen
2. Keine Texte verändern — 1:1, kein Umformulieren, kein Kürzen
3. Kein globals/-Ordner — Navigation, Footer und Default SEO gehören in settings.yaml
4. Sinnvolle Sektions-Benennung — Nicht alle Felder flach auf oberster Ebene
5. Wiederholende Inhalte -> immer als items-Liste mit Objekten
6. Konsistente Feldnamen über alle Seiten: immer title, nie abwechselnd headline/heading
## Ausgabe
### Mit Tool-Zugriff
1. Bilder herunterladen und hochladen
2. YAML-Dateien direkt in src/content/ schreiben
3. Kurze Zusammenfassung: Seiten migriert, Bilder hochgeladen
### Ohne Tool-Zugriff
Pro Seite:
1. Dateipfad als Kommentar: # src/content/pages/home.yaml
2. Vollständiger YAML-Inhalt
3. Am Ende: Liste aller Bild-Platzhalter
Reihenfolge: settings.yaml -> Startseite -> weitere Seiten