Website-Import
Übernimmt eine bestehende Website (Webflow, WordPress, Squarespace, statische Seiten …) mit Seitenbaum, HTML, CSS, JS und Medien. Der Import läuft in zwei Schritten als Hintergrund-Jobs: erst analysieren, dann mit der gewünschten Auswahl importieren.
Alle Import-Endpunkte erfordern Schreibrechte ✏️.
Ablauf und Verhalten
- Analyse (
POST /import/scan): liestrobots.txtund die Sitemap(s) (inkl. Sitemap-Index und.xml.gz), folgt optional allen internen Links und wertet aus, von welchen Hosts die Website Dateien lädt – auch Hosts, die erst in CSS-Dateien auftauchen (Fonts). - Import (
POST /import/run) mit den ausgewählten Seiten und Hosts:- Seitenbaum aus den URL-Pfaden. Die Startseite der Quelle wird auf
target_page_idabgebildet. Fehlende Zwischenebenen (z. B./leistungenbei/leistungen/webdesign) werden als Entwurf angelegt. - Pfade werden normalisiert: Kleinschreibung, Umlaute (
ä→ae), Sonderzeichen →-, Kollisionen →-2. Reservierte Namen auf oberster Ebene (api,media,cms-admin) bekommen ein-seite. Interne Links werden entsprechend umgeschrieben, Query und Fragment bleiben erhalten. <head>:<title>→title,meta description→description;charset,viewport,generatorund<base>entfallen (setzt IllusionCMS selbst), alles andere landet inhead.<html>/<body>-Attribute →html_attrs/body_attrs(wichtig für Webflow-Interaktionen).- CSS/JS werden zu Assets. Stylesheets am Anfang des
<head>und Skripte am Ende von<body>, die auf allen importierten Seiten identisch sind, werden an die Zielseite gehängt und vererbt. Alle übrigen bleiben exakt an ihrer Position und verweisen per{{ asset_url(id) }}auf die Bibliothek – die Ladereihenfolge bleibt so erhalten. Wird die Startseite nicht mit importiert, bekommt jede Seite ihre Dateien selbst (nicht vererbt). integrity/crossoriginwerden bei übernommenen Dateien entfernt (sonst würde eine bearbeitete Datei blockiert).preload-Hinweise auf übernommene CSS/JS-Dateien entfallen.- Medien (Bilder, Fonts, Videos, PDFs, Favicons, Social-Media-Bilder) kommen in die Dateiverwaltung, Ordner
Import <domain>. Erfasst werden u. a.src,srcset,poster,style="…url()…",<style>,url()/@importin CSS,data-src/data-srcset/data-bg(Lazy-Loading),data-video-urls,og:image. - Bilder mit
srcset: Nur die größte Fassung wird geladen; dassrcsetwird durch die eigenen Varianten ersetzt.og:image& Co. bekommen absolute URLs auf der ursprünglichen Domain. - Template-Syntax im Original (
{{,{%,{#) wird per{% verbatim %}geschützt. - Dateien der eigenen Domain werden immer übernommen, externe nur von den Hosts in
download_hosts; alles andere bleibt extern verlinkt.
- Seitenbaum aus den URL-Pfaden. Die Startseite der Quelle wird auf
Grenzen: Seiten, die erst im Browser per JavaScript aufgebaut werden (React/Vue-SPAs), liefern kaum HTML – die Analyse meldet das als js_rendered. Formulare, Suche, Logins und andere serverseitige Funktionen der Quelle werden nur optisch übernommen. Skripte, die zur Laufzeit weitere Dateien relativ zu ihrer eigenen URL nachladen, müssen ggf. angepasst werden.
Job-Objekt
{
"id": "9f2c41d0a7b3e815",
"kind": "import",
"status": "running",
"phase": "Loading pages and files",
"phase_code": "import.phaseLoad",
"done": 12,
"total": 48,
"files": 87,
"assets": 5,
"log": [
{ "time": 1791197160, "level": "warn", "message": "file not imported: HTTP 404", "url": "https://cdn.example.com/alt.png",
"code": "import.fileNotImported", "params": { "error": "HTTP 404" } }
],
"result": null,
"started_at": 1791197150,
"finished_at": 0
}
| Feld | Beschreibung |
|---|---|
kind |
scan oder import |
status |
running, done, failed, cancelled |
done / total |
Fortschritt in Seiten |
files / assets |
Bisher übernommene Dateien bzw. CSS/JS-Dateien (nur Import) |
phase / phase_code |
Aktueller Schritt (englischer Text) und sein Code zum Übersetzen |
log |
Protokoll (info, warn, error), max. 3000 Einträge; Einträge mit code/params (siehe Fehler) |
error / error_code / error_params |
Bei failed: Meldung, Code und Parameter |
result |
Ergebnis, sobald status = done (siehe unten) |
Jobs liegen im Arbeitsspeicher; die letzten 20 bleiben abrufbar, ein Neustart löscht sie.
POST /import/scan ✏️
{ "url": "https://www.firma.de", "crawl": false, "respect_robots": true, "max_pages": 500 }
| Feld | Beschreibung |
|---|---|
url |
Startadresse; ohne Schema wird https:// angenommen. Redirects werden verfolgt, die finale Domain gilt als „eigene Domain“ (www. wird ignoriert) |
crawl |
Zusätzlich allen internen Links folgen. Wird automatisch aktiviert, wenn keine Sitemap gefunden wird |
respect_robots |
Per robots.txt gesperrte Seiten werden trotzdem aufgeführt, aber mit "excluded": "robots" markiert (die Oberfläche wählt sie vorab ab, der Crawler folgt ihren Links nicht). robots.txt richtet sich an Suchmaschinen – beim Umzug der eigenen Website gehören gesperrte Seiten wie Impressum oder Datenschutz meist dazu |
max_pages |
Obergrenze (Standard 500, max. 5000) |
Antwort 202 mit dem Job-Objekt. Ergebnis (result) nach Abschluss:
{
"base": "https://www.firma.de",
"sitemaps": ["https://www.firma.de/sitemap.xml"],
"pages": [
{ "url": "https://www.firma.de/", "path": "/", "title": "Firma GmbH", "source": "start" },
{ "url": "https://www.firma.de/ueber-uns", "path": "/ueber-uns", "title": "Über uns", "source": "sitemap" },
{ "url": "https://www.firma.de/rechtliches/impressum", "path": "/rechtliches/impressum", "source": "sitemap", "excluded": "robots" }
],
"hosts": [
{ "host": "www.firma.de", "category": "site", "label": "Own domain", "count": 14, "kinds": { "img": 10, "file": 4 }, "suggested": true },
{ "host": "cdn.prod.website-files.com", "category": "builder", "label": "Site builder CDN", "count": 96, "kinds": { "css": 3, "js": 6, "img": 80, "font": 7 }, "suggested": true },
{ "host": "fonts.gstatic.com", "category": "fonts", "label": "Web fonts", "count": 4, "kinds": { "font": 4 }, "suggested": true },
{ "host": "www.googletagmanager.com", "category": "service", "label": "Service / embed", "count": 1, "kinds": { "js": 1 }, "suggested": false }
],
"js_rendered": false
}
Host-Kategorien und Vorauswahl (suggested). label ist eine englische Bezeichnung der Kategorie; Clients übersetzen über category:
category |
Beispiele | Vorauswahl |
|---|---|---|
site |
eigene Domain | immer übernommen |
builder |
Webflow-, Squarespace-, Wix-, Shopify-, WordPress.com-, Contentful-, Cloudinary-CDNs | ja |
fonts |
Google Fonts, Bunny Fonts | ja (selbst gehostete Fonts sind DSGVO-freundlicher) |
cdn |
cdnjs, jsDelivr, unpkg, code.jquery.com | ja |
service |
Analytics, Tag Manager, YouTube, Maps, Cookie-Banner, Chat, Adobe Fonts | nein – müssen live bleiben bzw. dürfen nicht kopiert werden |
other |
alle übrigen | ja, außer der Host liefert nur Skripte |
Die Analyse lädt neben der Startseite bis zu 5 weitere Seiten und bis zu 10 CSS-Dateien als Stichprobe (beim Crawlen alle gefundenen Seiten).
POST /import/run ✏️
{
"base": "https://www.firma.de",
"urls": ["https://www.firma.de/", "https://www.firma.de/ueber-uns"],
"target_page_id": 1,
"overwrite": true,
"publish": true,
"download_hosts": ["cdn.prod.website-files.com", "fonts.googleapis.com", "fonts.gstatic.com"]
}
| Feld | Beschreibung |
|---|---|
base |
base aus dem Analyse-Ergebnis |
urls |
Zu importierende Seiten (typischerweise eine Auswahl aus pages[].url) |
target_page_id |
Seite, auf die die Startseite der Quelle abgebildet wird; alle anderen landen darunter |
new_site |
Statt target_page_id: { "title": "…", "domains": "a.de\nwww.a.de" } legt eine neue Website an und importiert dorthin. 409, wenn eine Domain schon vergeben ist (es wird dann nichts angelegt). result.target_page_id enthält die ID der neuen Website |
overwrite |
Vorhandene, unbearbeitete Seiten aktualisieren (Inhalt, Head, Attribute, CSS/JS-Zuordnungen). Sonst werden sie übersprungen |
overwrite_modified |
Auch im CMS bearbeitete Seiten und CSS/JS-Dateien überschreiben (Standard: false – sie bleiben, wie sie sind) |
force |
Auch Seiten neu schreiben, deren Quelle sich seit dem letzten Import nicht geändert hat |
publish |
Neu angelegte Seiten veröffentlichen (Zwischenebenen bleiben Entwürfe). Der Status vorhandener Seiten bleibt erhalten |
download_hosts |
Externe Hosts, deren Dateien übernommen werden |
Antwort 202 mit dem Job-Objekt. Ergebnis nach Abschluss:
{
"target_page_id": 1,
"created": 46, "updated": 1, "unchanged": 0, "modified": 0, "foreign": 0, "skipped": 0, "failed": 1, "placeholders": 2,
"files": 214, "files_updated": 0, "files_reused": 0,
"assets": 7, "assets_updated": 0, "assets_reused": 0, "assets_kept": 0,
"shared_css": 1, "shared_js": 3,
"pages": [
{ "id": 1, "path": "/", "source": "https://www.firma.de/", "action": "updated" },
{ "id": 12, "path": "/ueber-uns", "source": "https://www.firma.de/ueber-uns", "action": "created" },
{ "id": 0, "path": "/kaputt", "source": "https://www.firma.de/kaputt", "action": "failed" }
]
}
action je Seite: created, updated, unchanged (Quelle unverändert), modified (im CMS bearbeitet, nicht überschrieben), foreign (Pfad gehört einer Seite aus dem Import einer anderen Website – wird nie überschrieben), skipped, failed. files/assets zählen neu angelegte Dateien, *_updated an Ort und Stelle aktualisierte, *_reused wiederverwendete, assets_kept in der Quelle geänderte, aber im CMS bearbeitete (behaltene) CSS/JS-Dateien. shared_css/shared_js: Anzahl der Dateien, die vererbt an der Zielseite hängen.
Wiederholte Importe
Ein Import kann beliebig oft wiederholt werden, ohne Duplikate zu erzeugen:
- Seiten werden zuerst über ihre Herkunft (
source_url+ Zielseite) gefunden – auch wenn sie inzwischen verschoben oder umbenannt wurden –, sonst über den Pfad. Ist das Quell-HTML seit dem letzten Import unverändert, wird die Seite übersprungen (unchanged), ohne ihre Dateien erneut zu laden. - Im CMS bearbeitete Seiten (
local_modified) werden nur mitoverwrite_modifiedüberschrieben. - Dateien werden über ihre Quell-URL wiedererkannt und per
If-None-Match(ETag) angefragt – unveränderte werden nicht erneut heruntergeladen. Geänderte Inhalte werden an Ort und Stelle ersetzt (gleiche ID, gleiche URL, Varianten neu berechnet). Mediendateien werden deshalb nur einen Tag im Browser gecacht. - CSS/JS werden über ihre Quell-URL wiedererkannt; unveränderte wiederverwendet, geänderte aktualisiert – außer, sie wurden im CMS bearbeitet (dann bleibt die CMS-Fassung, Warnung im Log).
- Die Download-Hosts werden pro Website und Quelle gemerkt und von
POST /pages/{id}/reimportverwendet – ein Import einer anderen Quelle in dieselbe Website überschreibt sie nicht. - Gehört keine der übergebenen URLs zur Quelle (
base), schlägt der Job mit einer Fehlermeldung fehl, statt still nichts zu tun.
Verwaiste Dateien früherer Importe werden nicht automatisch gelöscht. Dateien und Assets aus Importen vor dieser Version haben keine gespeicherte Herkunft und werden beim ersten Re-Import einmalig neu angelegt.
GET /import/jobs ✏️
Die letzten Jobs (neueste zuerst), ohne Log und Ergebnis.
GET /import/jobs/{id} ✏️
Zustand eines Jobs. Mit ?log_since=n werden nur Logeinträge ab Index n geliefert – praktisch für Polling:
curl -H "Authorization: Bearer $TOKEN" "$API/import/jobs/9f2c41d0a7b3e815?log_since=120"
DELETE /import/jobs/{id} ✏️
Bricht einen laufenden Job ab (204). Bereits angelegte Dateien und Assets bleiben erhalten; Seiten werden erst am Ende eines Imports geschrieben.
Beispiel: Import per Skript
JOB=$(curl -s -X POST "$API/import/scan" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"url": "https://www.firma.de", "respect_robots": true}' | jq -r .id)
until [ "$(curl -s -H "Authorization: Bearer $TOKEN" "$API/import/jobs/$JOB" | jq -r .status)" != running ]; do sleep 1; done
SCAN=$(curl -s -H "Authorization: Bearer $TOKEN" "$API/import/jobs/$JOB" | jq .result)
curl -s -X POST "$API/import/run" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d "$(jq '{base, urls: [.pages[].url], target_page_id: 1, overwrite: true, publish: true,
download_hosts: [.hosts[] | select(.suggested and .category != "site") | .host]}' <<< "$SCAN")"
# Hinweis: urls enthält hier auch per robots.txt gesperrte Seiten – zum Ausschließen
# [.pages[] | select(.excluded == null) | .url] verwenden