Bundles (Export, Import, Übertragung)
Ein Bundle ist eine ZIP-Datei mit der Endung .icb (ältere Versionen: .zip, wird weiterhin angenommen) mit dem kompletten Inhalt einer Instanz: alle Websites und Seiten, CSS/JS-Assets samt Zuordnungen, Snippets, Ordner und Dateien (inklusive aller Bildvarianten), Collections mit Einträgen, REST-Quellen, Weiterleitungen und Import-Profile. Nicht enthalten sind Konten, Sitzungen, API-Tokens, das 404-Protokoll und die Versionshistorie.
Beim Einspielen wird der Inhalt der Ziel-Instanz ersetzt. Alle IDs bleiben erhalten – Templates verweisen per ID auf Inhalte (asset_url(12), image(34), children(5)), die so weiter stimmen.
- Backup: Vor jedem Einspielen sichert der Server seinen bisherigen Inhalt als Bundle unter
<data>/backups/(die letzten 3). - Domains: Hat eine Website im Bundle keine Domains (typisch für lokal gebaute Websites) und gibt es auf dem Ziel eine Website mit derselben ID, behält sie die Domains des Ziels. Ist im Bundle keine Standard-Website markiert, bleibt die Markierung des Ziels.
- Live-Daten: Einträge von Collections, die im Bundle
live: truehaben, bleiben auf dem Ziel (zugeordnet über den Slug der Collection, Anzahl inlive_kept); die Einträge dieser Collections aus dem Bundle werden verworfen. Belegt ein behaltener Eintrag eine ID, die ein Eintrag aus dem Bundle schon hat, bekommt er eine neue. Mitlive=bundle(Backup wiederherstellen, Bundle vom Live-Server lokal einspielen) gewinnt wie bei allen anderen Collections das Bundle. - Collection-Einträge behalten (
keep_items=1): Die Einträge des Ziels bleiben und werden über den Slug ihrer Collection den Collections aus dem Bundle zugeordnet; Einträge aus dem Bundle werden dann nicht übernommen. Einträge von Collections, die es im Bundle nicht gibt, entfallen (dropped_items). - Dateien: Unveränderte Dateien (gleiche Größe und CRC32) werden nicht neu geschrieben, Dateien, die nicht im Bundle sind, werden gelöscht.
- Versionen: Bundles älterer IllusionCMS-Versionen lassen sich einspielen (fehlende Felder bekommen Standardwerte). Bundles einer neueren Version lehnt der Server mit
400ab – zuerst den Server aktualisieren. - Während ein Website-Import läuft oder ein anderes Bundle eingespielt wird:
409.
Aufbau des ZIPs: manifest.json (format: "illusioncms-bundle", version, schema, created_at, counts, media_files, media_bytes, bei Bundles ohne Medien no_media: true – beim Einspielen bleibt der Medienordner dann unverändert), tables/<tabelle>.json (Zeilen als JSON-Objekte), media/… (der Medienordner, unkomprimiert abgelegt).
GET /bundle/info ✏️
Umfang und Version der Instanz – vor einer Übertragung zur Prüfung von Token und Kompatibilität.
{ "schema": 20, "build": "df06fd6a7e73", "commit": "6ce6102090ec", "version": "v0.1.9", "headless": false,
"content_fingerprint": "3f1c…",
"counts": { "pages": 377, "assets": 21, "files": 1840, "collections": 2, "collection_items": 48, … },
"media_files": 5520, "media_bytes": 4830000000 }
content_fingerprint ist ein Hash über den Inhalt, den ein Abgleich überträgt – ohne die Einträge von Collections mit Live-Daten und ohne die Trefferzähler der Weiterleitungen, die sich im Betrieb von selbst ändern. Ändert er sich, wurde am Inhalt etwas geändert.
GET /bundle ✏️
Lädt das Bundle herunter (application/zip, Content-Disposition: attachment; filename="illusioncms-<host>-<datum>.icb"). Wird gestreamt; die Größe entspricht etwa media_bytes.
POST /bundle ✏️
Spielt ein Bundle ein. Body: die ZIP-Datei roh (kein Multipart), Content-Type: application/zip. Query keep_items=1 behält die Collection-Einträge des Servers, live=bundle übernimmt auch die Einträge von Collections mit Live-Daten aus dem Bundle. Mit einem Schreib-Token lässt sich so ein Deployment skripten:
curl -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/zip" \
--data-binary @illusioncms-localhost-2026-10-05-1722.icb \
https://www.firma.de/api/v1/bundle
Antwort 200:
{
"manifest": { "format": "illusioncms-bundle", "version": 1, "schema": 9, … },
"imported": { "pages": 377, "assets": 21, "files": 1840, … },
"kept_items": 0, "dropped_items": 0, "live_kept": 14, "domains_kept": [1],
"media_written": 12, "media_skipped": 5508, "media_removed": 3,
"backup": "backup-20261005-172236-467.icb"
}
Fehler: 400 kein ZIP, kein IllusionCMS-Bundle, neuere Version, unzulässige Dateipfade oder fehlerhafte Daten (der Inhalt bleibt dann unverändert); 409 siehe oben.
Hinter einem Reverse Proxy muss die erlaubte Body-Größe zum Bundle passen (nginx: client_max_body_size; Caddy hat standardmäßig kein Limit).
GET /bundle/backups ✏️ · GET /bundle/backups/{name} ✏️
Liste der automatischen Backups (neueste zuerst: [{ "name", "type", "media", "size", "created_at" }]) bzw. Download eines Backups. type: nightly (jede Nacht ab 3 Uhr Serverzeit, nur bei Änderungen, ohne Mediendateien, die letzten 7; Name auto-…) oder import (vor jedem Einspielen, mit Medien, die letzten 3; Name backup-…). Wiederherstellen = Backup per POST /bundle einspielen.
POST /bundle/push 👤 · GET /bundle/push 👤
Überträgt den Inhalt dieser Instanz direkt an eine andere – das Gleiche wie Herunterladen und dort POST /bundle, aber in einem Schritt vom Server aus. Body mit einer gespeicherten Verbindung oder mit Adresse und Token:
{ "connection_id": 1, "keep_items": false }
{ "url": "https://www.firma.de", "token": "ic_…", "keep_items": false }
token ist ein Token des Zielservers mit Schreibrecht; ohne Verbindung wird er nicht gespeichert. Mit Verbindung merkt sich die Instanz nach dem Hochladen die Fingerabdrücke beider Seiten (für die Warnungen beim nächsten Abgleich). Läuft im Hintergrund (202); den Fortschritt liefert GET /bundle/push:
{ "status": "running", "phase": "upload", "direction": "push", "connection": 1, "target": "https://www.firma.de", "sent": 1048576000, "total": 4830000000, "started_at": 1791213700 }
status: idle, running, done (mit result = Antwort des Ziels), failed (mit error, dazu error_code/error_params, falls vorhanden). phase: check (Ziel erreichbar, Token gültig, Version kompatibel), export, upload, import. Eine Übertragung gleichzeitig (409) – Hochladen und Herunterholen teilen sich den Zustand.
POST /bundle/pull 👤
Holt den Inhalt eines Servers über eine gespeicherte Verbindung hierher: lädt dessen Bundle herunter und spielt es ein – samt Live-Daten (wie live=bundle), vorher mit Backup. Konten, API-Tokens und Verbindungen dieser Instanz bleiben. Body { "connection_id": 1 }, Antwort 202, Fortschritt über GET /bundle/push (direction: "pull", Phasen check, download, import; total ist nur geschätzt). Ist der Server neuer als diese Instanz, schlägt es fehl – zuerst hier aktualisieren.