Download the PHP package deon-ai/craft-connect without Composer
On this page you can find all versions of the php package deon-ai/craft-connect. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Informations about the package craft-connect
Deon AI Connect für Craft CMS
Verbindet deine Craft-Site mit dem Deon AI Marketing-OS: SEO-Audit mit 1-Klick-Fixes, KI-Sichtbarkeits-Tracking (ChatGPT, Google AI Overview & Co.), Besucher-Tracking und automatisches Blog-Publishing.
Was das Plugin macht
- SEO-Fixes nativ am Origin — Title, Meta-Description, Canonical und Schema.org-Markup werden serverseitig ins ausgelieferte HTML geschrieben. Kein Client-Overlay: Googlebot und KI-Crawler (GPTBot, ClaudeBot, PerplexityBot) sehen die Optimierungen im rohen HTML.
- Automatische Einbindung — SDK-Script (Tracking, Conversions, A/B-Varianten) und Domain-Verifizierungs-Tag werden injiziert, ohne dass Templates angepasst werden müssen.
- robots.txt / llms.txt — Deon AI kann KI-Crawler-Freigaben und llms.txt optional direkt am Origin ausliefern (Setting
manageRobotsLlms). - Blog-Publishing — Deon AI legt generierte Artikel direkt als Entries an (Entwurf oder live), inkl. Featured-Image-Upload und Duplikat-Check über bestehende Entries. Section und Body-Feld können pro Request überschrieben werden (Multi-Section-Publishing).
- Rollback-fähiges Änderungsprotokoll — jede Deon-AI-Änderung speichert automatisch ihren Vorher-Zustand, bevor sie geschrieben wird. Kein separater Backup-Job, der ausfallen könnte: die Sicherung ist untrennbarer Teil derselben Datenbank-Transaktion wie die Änderung selbst und funktioniert auf jedem Hosting (reines SQL, kein
shell_exec/mysqldumpnötig). - Native Content-Bausteine — FAQ-Blöcke idempotent in bestehende Entries einbauen (
/deon-ai/faq), Standort-/Faktenseiten als eigene Section anlegen (/deon-ai/page, SettingpagesSectionHandle), robots.txt/llms.txt direkt im Webroot lesen/schreiben (/deon-ai/files) — jeweils mit Backup vor dem Überschreiben. - Berechtigungen — der Kunde entscheidet im Control Panel selbst, was Deon AI ändern darf. Nicht freigegebene 1-Klick-Fixes werden im Deon-AI-Dashboard ausgegraut statt einen Fehler zu werfen.
- Remote-Self-Update — Deon AI kann das Plugin bei neuen Versionen selbstständig per Composer aktualisieren, ohne dass jemand ins Control Panel muss. Craft spielt Plugin-Updates sonst nie automatisch ein.
- Blog-/Seiten-Bootstrap — legt bei Bedarf Body-/Bildfeld und Blog-/Seiten-Section selbst an (
/deon-ai/setup-blog), damit das Publishing auch auf einer frischen Craft-Installation ohne bestehendes Content-Schema funktioniert. - Navigation — verlinkt generierte Seiten automatisch in Hauptnavigation oder Footer (
/deon-ai/nav), wenn das kostenlose Plugin verbb/navigation installiert ist. Zusätzlich ein Plugin-eigener Footer-Block („Servicegebiete",/deon-ai/footer-links), der ohne Zusatz-Plugin funktioniert. - Seiten-Anbindung — Deon AI kann Craft-Seiten vollständig lesen (
/deon-ai/page-structure), per URL finden (/deon-ai/match-url), im Original-Design klonen und texturieren (/deon-ai/duplicate-page,/deon-ai/set-widget-texts), als Full-Page-Landingpage publizieren (/deon-ai/publish-lp) und die Design-Tokens der Site extrahieren (/deon-ai/theme-tokens) — Contract-Parität zum WordPress-Plugin, gleiche Response-Shapes. - Section-Tests & A/B-Varianten — komplette Seiten-Varianten mit Server-Cookie-Split (
/deon-ai/section-test/*, Winner-Merge mit Rollback) sowie Selector-basierte A/B-Änderungen per Frontend-Snippet (/deon-ai/ab-variant/*), inkl. Remote-Konfiguration (/deon-ai/configure-ab,/deon-ai/configure-tracker).
Voraussetzungen — vor der Installation prüfen
Das Plugin bringt eine eigene Datenbank-Migration mit (neue Tabellen für SEO-Overrides und robots.txt/llms.txt). Damit die Installation nicht die Live-Seite lahmlegt, vorher sicherstellen:
- Craft CMS 4.0+ oder 5.0+, PHP 8.0.2+
- DB-User mit vollen DDL-Rechten (
CREATE,ALTER,DROP,INDEX) — nicht nur Lese-/Schreibrechte. Viele Shared-Hoster (z. B. Hetzner, All-Inkl) vergeben standardmäßig einen eingeschränkten DB-User ohneALTER. Ohne dieses Recht schlägt jede Craft-Migration fehl — nicht nur unsere, auch reine Craft-Core-Updates — und reißt beim nächsten Control-Panel-Aufruf die komplette Seite mit runter (kein Plugin-spezifischer Bug, sondern Craft-Grundvoraussetzung). Bei getrennten DB-Usern nach Rechte-Stufe (z. B.xyz_1= Admin/Full,xyz_1_w= R/W ohne ALTER) vorübergehend auf den Admin-User umstellen, Migration laufen lassen, danach zurückstellen. - Admin-Account für die Einrichtung: Craft verlangt für jede Plugin-Einstellungsseite pauschal einen Admin-Account (
PluginsController::requireAdmin()) — es gibt bei Craft keine granulare, an Nicht-Admins vergebbare Berechtigung dafür. Wer das Plugin unter Einstellungen → Plugins → Deon AI Connect konfiguriert, muss also Admin sein; das lässt sich nicht per Rolle einschränken oder an z. B. einen SEO-Manager-Account delegieren. - Für die automatisierten Deon-AI-Aktionen selbst (SEO-Fixes, Blog-Publishing, robots.txt/llms.txt) ist dagegen keine Craft-Berechtigung nötig: Die REST-Endpoints laufen anonym (
allowAnonymous) und werden ausschließlich über denX-Deon-Key-Header authentifiziert, nicht über einen eingeloggten Craft-Nutzer — sie rufen die Craft-Services direkt auf und umgehen damit das CP-Berechtigungssystem komplett.
Migrationen sicher ausführen
Nach der Installation (und bei jedem künftigen Update, egal ob Plugin oder Craft-Core) Migrationen immer per Konsole laufen lassen, nicht über den Browser-Updater im Control Panel:
So bleibt die Seite live erreichbar, falls eine Migration fehlschlägt — der Fehler passiert in der SSH-Session, nicht mitten in einem Besucher-Request. Für Produktivumgebungen empfiehlt sich zusätzlich CRAFT_ALLOW_ADMIN_CHANGES=false in der Env, damit der Browser-Updater generell deaktiviert ist und Updates nur noch kontrolliert über die Konsole laufen.
Einrichtung
- In Deon AI → Website verknüpfen → Plattform Craft CMS wählen.
- Den angezeigten Connection-Key in Craft unter Einstellungen → Plugins → Deon AI Connect eintragen (einziges Pflichtfeld — Tipp: als Env-Variable hinterlegen, das Feld unterstützt Env-Autosuggest) und speichern.
- Beim Speichern holt sich das Plugin automatisch Site-ID, SDK-Key und Verifizierungs-UUID von Deon AI ab (Bootstrap-Call, authentifiziert über denselben Key) — die Einstellungsseite zeigt danach "✓ Verbunden — Site-ID: …". Schlägt das fehl (z. B. falscher Key), bleibt eine Meldung im Control Panel stehen; die restlichen Settings gehen dabei nicht verloren.
- Zurück im Deon-AI-Wizard auf Verifizieren klicken — fertig.
Für das Blog-Publishing: Section-Handle (Standard blog) und Body-Feld-Handle (Standard body) in den Plugin-Einstellungen an dein Schema anpassen. Für Featured Images zusätzlich Asset-Volume- und Bildfeld-Handle eintragen. Für robots.txt/llms.txt-Verwaltung den entsprechenden Schalter aktivieren (nur wirksam, wenn im Webroot noch keine physische robots.txt-Datei liegt).
Berechtigungen
Unter Einstellungen → Plugins → Deon AI Connect → Berechtigungen legt der Kunde fest, was Deon AI eigenständig ändern darf — pro Kategorie ein Schalter:
| Schalter | Standard | Gatet |
|---|---|---|
Title/Meta-Description/Canonical/Schema (allowSeoMeta) |
an | /deon-ai/seo |
Inhalte bestehender Seiten bearbeiten (allowContentEdit) |
aus | /deon-ai/faq |
Neue Seiten anlegen (allowPageCreate) |
aus | /deon-ai/entry, /deon-ai/page |
robots.txt / llms.txt (allowFiles) |
aus | /deon-ai/files, /deon-ai/hygiene |
Bild-Uploads (allowAssets) |
aus | /deon-ai/asset |
Plugin automatisch aktualisieren (allowSelfUpdate) |
an | /deon-ai/self-update |
Navigation bearbeiten (allowNavEdit) |
aus | /deon-ai/nav |
A/B-Tests & Tracking umkonfigurieren (allowAbTest) |
aus | /deon-ai/configure-ab, /deon-ai/configure-tracker |
/deon-ai/setup-blog läuft unter derselben Berechtigung wie neue Seiten (allowPageCreate), da es im Kern ebenfalls Content-Struktur anlegt. /deon-ai/publish-winner prüft allowContentEdit als Basis-Berechtigung, für enthaltene seo_meta-Changes zusätzlich allowSeoMeta (sonst wird nur dieser Change-Type übersprungen, der Rest angewendet).
Nur SEO-Overrides und Self-Update sind standardmäßig aktiv — SEO-Overrides, da sie rein serverseitig wirken und keinen Inhalt verändern; Self-Update, da Updates auch Sicherheitsfixes enthalten können. Ein Aufruf gegen einen nicht freigegebenen Endpoint liefert 403 { "ok": false, "error": "consent_required", "permission": "<key>" }. /deon-ai/ping gibt den aktuellen Freigabe-Stand aller Kategorien im Feld permissions zurück, damit Deon AI nicht freigegebene 1-Klick-Fixes im Dashboard ausgrauen kann. Lese-Endpoints (ping, seo-list, entries, hygiene-list, rollback/*) sind bewusst nicht gegated — Rückgängig machen (Rollback) funktioniert unabhängig von diesen Schaltern immer.
Remote-Self-Update
Craft spielt Plugin-Updates nie automatisch ein — jemand muss im Control Panel klicken. Deon AI kann das stattdessen selbst anstoßen:
POST /deon-ai/self-update({ "version": "0.6.1" }) — hebt ausschließlichdeon-ai/craft-connectper Composer auf die angegebene Zielversion an (niecraft update alloder andere Pakete). Ziel bereits installiert →{ ok: true, already: true }. Ein Preflight-Check prüft vorher, ob der Server das technisch kann (proc_openverfügbar,memory_limit≥ 256M,composer.pharvorhanden) — schlägt er fehl, bleibt die Installation unangetastet und die Antwort ist422 { ok: false, error: "self_update_unavailable", reason: "…" }. Vor dem Swap wird fail-soft ein DB-Backup versucht (Craft::$app->getDb()->backup(), brauchtmysqldump/pg_dump). Antwort bei Erfolg:{ ok: true, from: "0.4.0", to: "0.6.1", needs_migration: true }.POST /deon-ai/up— führt danach, in einem neuen Request, die Plugin-Migrationen der frisch installierten Version aus. Zwei getrennte Requests sind nötig, weil direkt nach dem Composer-Swap im selben PHP-Prozess noch der alte Klassen-Code geladen ist. Antwort:{ ok: true, migrated: true, version: "0.6.1" }.
/deon-ai/ping meldet zusätzlich ein Fähigkeits-Flag self_update (kann dieser Server technisch selbst updaten, unabhängig vom allowSelfUpdate-Schalter) sowie plugin_version/craft_version/php_version und sections_ok (ob die konfigurierten Section-Handles für Blog und Seiten tatsächlich existieren).
Handle-Resilienz gegen Project-Config-Reverts: Craft speichert Plugin-Settings in der Project Config. Läuft eine Installation mit allowAdminChanges = false (Produktions-Standard), schreibt savePluginSettings() zwar erfolgreich, aber der auf jedes Self-Update folgende php craft up → project-config/apply kann die per /setup-blog geschriebenen Handles (Section-/Feld-/Volume-Handles) wieder auf ihre Defaults zurücksetzen — ein Setting ist damit eher eine Präferenz als ein verlässlicher Fakt. Alle Endpoints, die Handle-Settings brauchen, lösen sie deshalb selbst auf: Request-Parameter → Plugin-Setting (nur wenn es auf ein existierendes Objekt zeigt) → Deon-Bootstrap-Handle (deonBlog/deonPages/deonBody/deonFeaturedImage) → null (sauberer 422 statt einer halbfertigen Seite). sections_ok/fields_ok in /ping prüfen entsprechend die aufgelösten, nicht die rohen Werte; /ping liefert additiv handles (tatsächlich benutzte Werte) und handles_repaired (welche Settings in diesem Request abgedriftet waren und automatisch zurückgeschrieben wurden — ein nicht-leeres Array direkt nach einem Update ist der Beleg für einen Project-Config-Revert, kein Fehler). Capability-Flag: handle_fallback.
Funktioniert Composer im Web-Request nicht (manches Shared-Hosting sperrt proc_open oder begrenzt memory_limit/max_execution_time zu knapp), bleibt als Fallback die Konsole — z. B. per Cron:
Gleicher Code-Pfad wie die REST-Endpoints (Composer-Swap + Migrationen in einem Lauf), nur ohne die Web-Request-Limits.
Blog-/Seiten-Bootstrap
Auf einer frischen Craft-Installation ohne bestehendes Blog-Schema scheitert das Publishing an section_not_found oder fehlenden Feld-Handles. POST /deon-ai/setup-blog behebt das: legt bei Bedarf ein Body-Feld (deonBody — CKEditor, falls craftcms/ckeditor installiert ist, sonst Redactor, sonst ein mehrzeiliges Klartext-Feld), ein Featured-Image-Feld (deonFeaturedImage, auf das erste vorhandene Volume beschränkt) sowie die Blog- und Seiten-Section an. Ziel-Handles sind dabei immer settings.blogSectionHandle/pagesSectionHandle (Standard blog/pages), falls die schon auf eine existierende Section zeigen — nur wenn sie leer oder ungültig sind, legt das Plugin eigene Sections (deonBlog/deonPages) an. Idempotent: bereits vorhandene, gültige Handles werden nie überschrieben, nur leere oder kaputte Plugin-Settings automatisch mit den neuen Handles verdrahtet. Fehlt ein Volume, wird das Bildfeld übersprungen (featured_image: "no_volume") — das Plugin legt nie selbst ein Volume/Filesystem an, das ist hosting-abhängig.
settings.assetVolumeHandle wird dabei automatisch mitgesetzt — ohne dieses Setting hängen /deon-ai/entry und /deon-ai/page trotz vorhandenem featuredImageFieldHandle nie ein Bild an. Für neu angelegte Bildfelder kommt der Handle vom verwendeten Volume; für ein bereits bestehendes deonFeaturedImage-Feld wird er aus dessen restrictedLocationSource abgeleitet (heilt auch Alt-Setups, ohne das Feld selbst anzufassen). /deon-ai/ping prüft in fields_ok.featured_image beide Settings inkl. echter Volume-Existenz — nur so erkennt der Worker-Self-Heal betroffene Sites zuverlässig und ruft setup-blog erneut auf.
Fallback-Templates: Das Plugin bringt zwei eigene, self-contained Templates mit — deon-ai/entry fürs Blog, deon-ai/page für Standort-/Leistungsseiten (beide leben im Plugin, nichts wird in das templates/-Verzeichnis der Site geschrieben). Neu angelegte Sections bekommen ihr passendes Template direkt gesetzt. Bestehende Sections mit leerem Template werden automatisch repariert; bestehende Sections mit einem gesetzten, aber erkennbar leeren/kaputten Custom-Template nur mit explizitem { "fix_template": true } im Request — eine funktionierende Konfiguration wird nie stillschweigend überschrieben. Response meldet template/previous_template fürs Blog sowie additiv template_pages/previous_template_pages für die Seiten-Section ("kept"/"set"/"fixed"), jede Änderung ist über /deon-ai/rollback rückgängig machbar.
Titel-Feld-Check: Craft überschreibt automatisch jeden gesetzten Titel mit leer, wenn der Entry-Type einer Section weder ein Titel-Feld noch ein titleFormat hat (craft\elements\Entry::updateTitle(), läuft unumgehbar bei jedem Speichern) — Ergebnis wäre „Eintrag ohne Titel" im CP. /deon-ai/entry und /deon-ai/page brechen deshalb mit 422 title_field_missing ab, statt eine unsichtbar untitled Seite zu veröffentlichen. setup-blog prüft das für Blog- und Seiten-Section (Response title_field/title_field_pages: "ok"|"missing"|"fixed") und repariert es mit { "fix_template": true }.
Navigation
POST /deon-ai/nav ({ target: "main"|"footer", url, title, entry_id? }) verlinkt eine generierte Seite in Hauptnavigation oder Footer. Craft hat keine Kern-Navigation, daher eine Strategie-Kaskade:
- Ist verbb/navigation installiert (der De-facto-Standard für Craft-Navigationen), wählt das Plugin die passende Nav per Handle-/Namens-Heuristik (
main/haupt/primarybzw.footer/fuss, sonst die erste vorhandene Nav) und legt einen Node an — dedupliziert über die Ziel-URL bzw. den verlinkten Entry. Antwort:{ ok, via: "verbb", nav: { handle } }. - Sonst, falls eine Structure-Section mit Handle
nav/menuexistiert und deren Entry-Type ein FeldlinkUrloderurlhat, wird dort ein Entry angelegt. Ohne ein eindeutiges Link-Feld wird nicht geraten. - Sonst
422 { ok: false, error: "nav_not_automatable", hint: "…" }mit dem Hinweis, den Link manuell im CP/Template zu setzen — und dem Tipp, dass Deon AI die Navigation mit dem kostenlosen verbb-Plugin automatisch pflegen kann.
Seiten-Anbindung
Damit Deon AI Craft-Seiten analysieren, im Original-Design klonen und texturieren kann — Response-Shapes bewusst identisch zum WordPress-Plugin (aideon-connect), damit der Deon-AI-Worker beide Plattformen einheitlich anspricht:
GET /deon-ai/match-url?url=…— findet den Entry zu einer URL ({ matched, id, title, slug, … })GET /deon-ai/pages?per_page=— Entries aller Sections, nach Änderungsdatum absteigendGET /deon-ai/page-structure/<id>— kompletter Seiteninhalt inkl. Body-HTML und walkbaren Text-Blöcken (content_blocks, IDspc-N: h1–h3 =title, p =editor) — funktioniert nur für das einedeonBody-Feld (Deons eigene Blog-/Standortseiten)GET /deon-ai/site-inventory— alle Sections der Site (handle/type/name/entry_count/sample_entry_id), damit der Worker Section-Handles nicht raten muss, um z. B. eine bestehende Leistungsseite als Vorbild für eine neue zu finden. Zusätzlich pro Sectionhas_urls/uri_format/template/template_existssowie das daraus abgeleiteterenderable(Konjunktion aus beidem — eine Section mit URL, aber ohne existierendes Template, speichert klaglos und liefert erst beim Aufruf einen 404) undentry_types[{handle, name, has_title_field, block_field_handles[]}], damit ein Klonziel für/duplicate-pagedeterministisch statt geraten gewählt werden kann.GET /deon-ai/section-catalogundGET /deon-ai/section-catalog/<id>— der Skelett-Katalog: welche Block-Typen darf ein Matrix-/Neo-Feld überhaupt aufnehmen, und welche Sub-Felder sind darin befüllbar? Voraussetzung fürop: "add"unten, dablock_typedort ein existierender Handle sein muss. Ohne<id>(Site-Scope): jedes Matrix-/Neo-Feld einmal, dedupliziert nach Feld-Handle, mitused_in: ["section:entryType", …]. Mit<id>(Entry-Scope): nur die Block-Felder dieses Entries, dafür mit je einem Beispielblock pro Typ aus dem Entry selbst. Pro Block-Typ:handle,name,has_title_field,can_add(bei Neo-Feldern bewusstfalsestatt eines Fehlers beim Schreiben), Sub-Felder mitclass,required,writable_text(tri-statetrue/false/null) undrole_hint(headline/subline/body/cta_label/cta_url/media/null, reine Heuristik). Link-/Button-Felder (craft\fields\Link) liefern zusätzlichlink_field: true,label_enabledundsub_targets(die beiden adressierbaren Sub-Felderhandle:url/handle:labelmit je eigenemrole_hint/writable_text). Matrix-Felder, die selbst wieder auf dem Field-Layout eines Block-Typs liegen (Matrix-in-Matrix, z. B. FAQ-Items in einerfaqSection), liefern bis zu zwei Ebenen tiefnested_block_types(vollständig beschriebene Sub-Felder je verschachteltem Blocktyp) plusnested_writableals schnellen Hinweis, ob sich darin übernested(siehe unten) überhaupt etwas beschreiben lässt.GET /deon-ai/entry-sections/<id>— strukturierte Block-Inhalte eines beliebigen Entries (nicht nur Deons eigene Seiten): native Matrix-Felder (Craft 5: verschachtelte Entries, Craft 4: MatrixBlocks) sowie Neo-Felder (spicyweb/craft-neo, sofern installiert), rekursiv aufgelöst — jeder Block mitblock_id(stabile Element-ID),block_type/block_label(Handle/Name des Block-Typs),enabled(bool) und seinen Feldwerten (Text, Bilder inkl. Alt-Text, Entry-/Category-Relationen). Feature-detected über die Feld-Klasse, nicht über Feld-/Block-Namen. Listet auch deaktivierte Blöcke (enabled: false) — Aufrufer, die nur sichtbare Sektionen wollen, filtern selbst. Ohne Matrix-/Neo-Feld am Entry-Type greiftlegacy_body_fallback(identisch zupage-structure). Tiefen-/Blockzahl-Limit: 4 Ebenen / 200 Blöcke.POST /deon-ai/entry-sections/<id>—{ patches?: [{ field_handle, block_id?, block_index?, op?, field?, value?, block_type?, position?, enabled?, title?, fields?, nested? }], reorder?: { field_handle, block_ids } }(max. 50 Patches pro Call, mindestens eins vonpatches/reordernötig) — das Gegenstück zum GET oben, dieselbe Route, per HTTP-Methode verzweigt. Jeder Patch-Eintrag hat einop(Default"set"):op: "set"(Default) — schreibt Text in ein Sub-Feld (field/valuePflicht). Nur "textartige" Sub-Felder (PlainText/CKEditor/Redactor oder ein Feld mit aktuell einfachem String-Wert) — Assets/Relationen/verschachtelte Matrix-Neo-Felder werden nie angefasst. Ausnahme: Link-/Button-Felder (craft\fields\Link) sind überfield: "handle:url"/"handle:label"sub-adressierbar (natives Feld speichert ein strukturiertesvalue/label/type/target-Array, kein einfacher String — der jeweils andere Teil bleibt beim Schreiben erhalten).labelliefertlink_label_disabled, wenn das Feld kein Label-Eingabefeld hat;link_url_missing, wenn eine URL fehlt, während ein Label gesetzt werden soll — ein Button ohne Ziel-URL wird nie gespeichert.op: "disable"/"enable"— blendet einen ganzen Block aus/ein (kein Hard-Delete, nurenabled-Flag — reversibel). Ein deaktivierter Block bleibt im CP sichtbar (ausgegraut).op: "add"— legt einen neuen Block eines existierendenblock_typean (field_handle/block_typePflicht,position/enabled/title/fieldsoptional) und befüllt optional direkt Textfelder (inkl."handle:url"/"handle:label"für Link-Felder). Nicht setzbare Sub-Felder werden nicht still verschluckt, sondern inskipped_fieldsgemeldet (sub_field_not_found/value_not_string/field_not_text_writable). Unbekannterblock_type→block_type_not_foundplus Liste der verfügbaren Handles. Nur Matrix, Neo liefertadd_unsupported_field_type.op: "remove"— nimmt einen Block strukturell aus der Seite (verschwindet auch im CP, nicht nur ausgegraut wie beidisable), aber als Soft-Delete — vollständig über Rollback wiederherstellbar. Adressierung überblock_idoderblock_index, ohne Default (destruktiv wirkende Operation).block_idbevorzugt (stabil),block_indexnur als Fallback direkt nach einemduplicate-page-Klon ohne zwischenzeitlichen Read — bei genesteten Neo-Blöcken istblock_indexbeim GET pro Verschachtelungsebene neu bei 0 gezählt, beim POST-Fallback dagegen ein Index in die flache Blockliste; dort immerblock_idverwenden.nested: { field_handle, block_id?, block_index?, block_type?, fields? }(optional, auf jedemop) — adressiert Matrix-in-Matrix: einen Block, der auf dem Field-Layout des überfield_handle/block_idaufgelösten äußeren Blocks liegt (z. B. ein einzelnes FAQ-Item innerhalb einerfaqSection), statt auf dem Field-Layout des Entries selbst. Beiop: "add"istnested.block_typePflicht (analog zum Top-Level-add). Beispiel — ein FAQ-Item ändern:{"field_handle":"websiteContent","block_id":39337,"op":"set","nested":{"field_handle":"faq","block_id":39338},"field":"answer","value":"…"}. Neues FAQ-Item anlegen:{"field_handle":"websiteContent","block_id":39337,"op":"add","nested":{"field_handle":"faq","block_type":"faqItem","fields":{"question":"…","answer":"…"}}}. Vollständig rollback-fähig, siehe unten. Voraussetzung:GET /deon-ai/section-catalogzeigt für das äußere Feldnested_writable: true.reorder: { field_handle, block_ids: [id, id, …] }bringt die Blöcke eines Matrix-Feldes in eine neue Reihenfolge (nicht erwähnte Blöcke werden ans Ende angehängt, nie entfernt); nur für Matrix, Neo liefertreorder_neo_not_supported. Kann zusammen mitpatchesoder allein aufgerufen werden.- Ein
add/removeim selben Request macht den auf dem Entry (bzw. beinestedauf dem äußeren Block) memoisierten Matrix-Feldwert für nachfolgende Patches auf demselben Feld veraltet — der Endpoint lädt den Entry bzw. den äußeren Block dann intern frisch (nur als Lesequelle) nach, damit ein direkt folgendesset/reordertrotzdem den richtigen Block trifft. Schlägt das Nachladen ausgerechnet für einen anschließendenreorderfehl, bricht der Reorder-Teil mitreorder_skipped_stale_entryab, statt mit einer veralteten Blockliste zu speichern — bereits angewendete Patches bleiben gültig. - Jede Operation einzeln im Change-Log erfasst, granulares Rollback pro Block-Feld/-Sichtbarkeit/-Reihenfolge/-Existenz über
/deon-ai/rollback— auch fürnested-Patches. Berechtigung:allowContentEdit.
POST /deon-ai/build-template-page—{ source_entry_id, field_handle?, title?, force? }. Klont die echten nativen Design-Blöcke einer bestehenden Seite (i. d. R. die Startseite) in eine dedizierte, automatisch angelegte Section (deonTemplates, immerdisabled) — eine sichere Kopiervorlage für künftige Standort-/Leistungsseiten, für Sites, auf denen außer der (als Single-Section ungeeigneten) Startseite noch kein Entry mit echten Blöcken existiert. Die Vorlagen-Section erbthasUrls/uriFormat/templatevon der Quell-Section, aber nur wenn deren Template tatsächlich existiert (View::doesTemplateExist()) — sonst wird sie bewusst ganz ohne URLs angelegt, statt eine Seite zu erzeugen, die beim Aufruf mit 404 abbricht. Bereits bestehende, betroffenedeonTemplates-Sections (aus v0.19.0/v0.20.x) werden beim nächsten Aufruf automatisch repariert (Response-Feldsection_repaired). Nutzt dieselbe nativeduplicateElement()-API wie/duplicate-page, liest die Quelle nur, verändert sie nie. Idempotent ohneforce. Berechtigung:allowPageCreate.POST /deon-ai/set-widget-texts—{ post_id, texts: [{ id: "pc-N", title?|editor? }] }setzt Texte punktgenau in den Body (Reihenfolge identisch zupage-structure), mit Rollback-Protokoll. Berechtigung:allowContentEdit.POST /deon-ai/duplicate-page—{ source_post_id|source_page_url, title, replacements: [{find, replace}], h1_override?, page_id?, target_section_handle?, target_entry_type_handle?, … }klont eine Seite 1:1 (Craft-nativesduplicateElement, alle Felder inkl. Bilder), tauscht Texte und legt sie als Entwurf an — der Standortseiten-Pfad. Idempotent perpage_id. Ohnetarget_section_handleklont Craft immer in die Section der Quelle zurück — das macht z. B. die Startseite (meist einesingle-Section mit genau einem erlaubten Entry) strukturell unbrauchbar als Vorlage. Mittarget_section_handle(Ziel-Entry-Type pertarget_entry_type_handleoder automatisch der erste mit einem gemeinsamen Matrix-/Neo-Feld) landet der Klon stattdessen dort — da Feld-Handles in Craft global eindeutig sind, ist dieselbe Block-Palette in jedem Entry-Type legal, der dasselbe Feld einbindet. Validierung vor dem Klon mit eigenen Fehlercodes statt einer 500er-Exception:target_section_not_found(404),target_section_is_single(409),target_section_has_no_entry_type(409),target_entry_type_not_in_section(409, inkl.available_entry_types),target_block_field_missing(409, inkl.source_block_fields/target_block_fields— ein Klon ohne gemeinsames Block-Feld wäre eine leere Seite). Response liefert zusätzlichsection_handle,entry_type_handle,cloned_into_section(bool). Berechtigung:allowPageCreate.GET /deon-ai/render-preview?post_id|url&token=— liefert das gerenderte Frontend-HTML (für die Dashboard-Preview). Zweifach gesichert:X-Deon-Keyplus kurzlebiges HMAC-Token (60 s), nur Same-Origin-URLs.POST /deon-ai/publish-lp— Full-Page-Landingpage aus Roh-HTML inkl.<style>/<script>(eigene Tabelle + Route pro Slug, kein Entry). Hinweis:chrome: "bare"wird gespeichert, gerendert wird in Craft immer das Roh-HTML als eigenständiges Dokument — es gibt kein Theme, in das sich „bare" einbetten ließe.slugist strikt auf[a-z0-9-/]beschränkt und darf weder mitdeon-ai/kollidieren noch einer bestehenden Entry-URI entsprechen (422 slug_invalid/slug_reserved/slug_collides_with_entry) — die LP-Routen werden intern als Yii-URL-Rules registriert, ein ungefilterter Slug könnte sonst Kern-Routen des Plugins überschreiben. Berechtigung:allowPageCreate.GET /deon-ai/theme-tokens— Farben/Fonts/Radius/Palette der Site. Craft hat kein theme.json wie WordPress-Block-Themes, deshalb extrahiert das Plugin die Tokens aus dem CSS der gerenderten Startseite (<style>-Blöcke + Same-Origin-Stylesheets,var(--x)wird eine Ebene aufgelöst) —source: "css_extract", best-effort; der Worker-Normalizer wählt aus der Palette notfalls selbst.POST /deon-ai/site-schema— Site-weites JSON-LD, ausgespielt im<head>aller Seiten. Berechtigung:allowSeoMeta.GET|POST /deon-ai/footer-links— Plugin-eigener Footer-Block („Servicegebiete"-Links), gerendert vor</body>, ohne Zusatz-Plugin. Berechtigung (POST):allowNavEdit.GET /deon-ai/media?per_page=— Bild-Bibliothek der Site über alle Asset-Volumes (url,alt,title,filename,w,h), Pendant zu WPswp-json/wp/v2/media?media_type=image. Damit kann Deon AI passende Bilder aus dem vorhandenen Bestand in generierte Sektionen matchen, statt sie neu hochzuladen.POST /deon-ai/audit-fix— Content-Write-Fixes:{ action: "replace_content"|"append_html_box", page_url, new_content?|custom_value?, payload?: { box_marker? } }.replace_contentersetzt den kompletten Body (Freshness-Refresh),append_html_boxhängt eine HTML-Box idempotent an (existiert ein<aside>mit der Marker-Klasse bereits, wird er ersetzt statt dupliziert — für interne Verlinkung/Pillar-Backrefs). Beide mit Rollback-Snapshot (rollback_id). Berechtigung:allowContentEdit.
Section-Tests & A/B-Varianten
Craft-natives Pendant zur Test-Engine des WordPress-Plugins. WP manipuliert dort Gutenberg-Blocks/Elementor-JSON — in Craft sind die „Sections" die Top-Level-Elemente des Body-HTML (builder html, Selector = Index oder tag[n], z. B. section[1]):
POST /deon-ai/section-test/create—{ original_post_id, name?, sections_changes: [{action, selector?, position?, html?, target_selector?}] }. Legt die Variante als geklonten, deaktivierten Entry an (fürs Frontend unsichtbar) und startet den Test. Berechtigung:allowContentEdit.- Ausspielung: server-seitiger 50/50-Split per Cookie
aideon_st_<id>(30 Tage, für das SDK lesbar — Conversion-Attribution wie bei WordPress). Bots (Googlebot & Co.) sehen immer das Original. Antworten werden mitCache-Control: no-store+Vary: Cookiemarkiert, damit CDNs nicht eine Variante für alle einfrieren. Variante B ersetzt den Original-Body im gerenderten HTML — transformiert das Twig-Template den Feld-Inhalt so stark, dass er im HTML nicht wiedergefunden wird, wird fail-soft das Original ausgespielt (Warnung im Log). POST /deon-ai/section-test/preview— wendet die Änderungen an, ohne zu speichern.GET /deon-ai/section-test/list/<id>— Tests inkl. Besucher-Zählern.POST /deon-ai/section-test/stop—{ original_post_id, test_id, winner: "a"|"b"|"none" }. Winner B wird mit Rollback-Snapshot ins Original gemerged; die Variante wandert in den Craft-Papierkorb.POST /deon-ai/publish-winner— Änderungs-Liste direkt anwenden (seo_meta,content_replace,html_section), immer mit Rollback-Snapshot. Berechtigung:allowContentEdit.POST /deon-ai/ab-variant/create— Selector-basierte A/B-Variante (Moditext/html/attr/link/style/form,percentage1–99). Ausspielung über ein Frontend-Snippet (1:1 vom WP-Plugin portiert): Cookieaideon_ab_assign, Preview-Forcing per?aideon_force=a|b|<id>:bmit Banner, Impression-Tracking persendBeaconan Deon AI.POST /deon-ai/configure-ab/GET /deon-ai/ab-statusundPOST /deon-ai/configure-tracker/GET /deon-ai/tracker-status— Remote-Konfiguration.tracker_enabled=falseschaltet die SDK-Injection zusätzlich zur CP-Einstellung ab.
Änderungsprotokoll & Rollback
Jeder schreibende Endpoint (/deon-ai/seo, /deon-ai/entry, /deon-ai/hygiene) speichert vor jeder Änderung automatisch den bisherigen Zustand und gibt eine rollback_id (Format rb_123) zurück. Das ist keine separate Backup-Aktion, die vergessen oder übersprungen werden könnte — das Protokollieren passiert atomar mit der Änderung selbst, von der allerersten Aktion an.
Die Endpoints folgen derselben /rollback/*-Konvention wie das WordPress-/TYPO3-Plugin, damit sie im bestehenden "Änderungs-Journal"-Tab des Deon-AI-Dashboards erscheinen (der Worker leitet dorthin 1:1 durch):
GET /deon-ai/rollback/list(?limit=) — Journal auflisten. Jeder Eintrag liefert zusätzlichnote(Freitext, siehe unten — u. a. genutzt, um FAQ-Injections als solche erkennbar zu machen) sowie, fürtargetType:"entry"-Zeilen (u. a./entry,/page,/entry-sections,/faq),title/urldes betroffenen Entries (per Batch-Query aufgelöst, kein N+1;null/null, falls der Entry seither gelöscht wurde)GET /deon-ai/rollback/<rb_id>— einzelnen Eintrag abrufenPOST /deon-ai/rollback/<rb_id>/preview— zeigt, was ein Rollback wiederherstellen würde (bricht bei erkanntem Konflikt ab, wenn der Live-Zustand seit der Änderung manuell verändert wurde)POST /deon-ai/rollback/<rb_id>/restore(optional Body{ "force": true }, um einen Konflikt zu überschreiben) — macht die Änderung rückgängig:- SEO-Override/robots.txt/llms.txt: alter Inhalt wird wiederhergestellt (oder die Zeile gelöscht, falls sie vorher nicht existierte)
- Entry: Titel/Slug/Status/Body/Featured Image werden zurückgesetzt — war der Entry neu von Deon AI angelegt, wandert er stattdessen in den Craft-Papierkorb (weiches Löschen, jederzeit wiederherstellbar)
- Block-Feld (
/entry-sections-Patch): einzelner Feldwert im betroffenen Matrix-/Neo-Block wird zurückgesetzt, unabhängig vom Rest des Entries — inkl. Link-Sub-Feldern (handle:url/handle:label) undnested-Patches (Matrix-in-Matrix, der innere Block wird direkt über seine Element-ID gefunden, unabhängig vom äußeren Block)
POST /deon-ai/rollback/restore-point(Body{ "label"? }) — kompletter Sicherungspunkt: Snapshot aller aktuell verwalteten SEO-Overrides, robots.txt/llms.txt-Inhalte und Blog-Entries als ein wiederherstellbarer Punkt. Das ist die "einmal alles gesichert, bevor sich was ändert"-Aktion — als reines SQL-Snapshot, keinshell_exec/mysqldumpnötig.
Optional bei jedem Schreibaufruf ein note-Feld mitgeben (Freitext, z. B. "Grund der Änderung") — erscheint im Protokoll. Für einen kompletten Datenbank-Snapshot außerhalb dessen, was das Plugin selbst anfasst, bleibt zusätzlich Craft's eigenes php craft db/backup empfehlenswert — das braucht allerdings mysqldump/pg_dump per shell_exec, was auf manchen Shared-Hosting-Umgebungen gesperrt ist.
Native Content-Endpoints
POST /deon-ai/files—{ op: "read"|"write", filename: "llms.txt"|"robots.txt", content? }. Strikte Dateinamen-Whitelist, liest/schreibt direkt im Webroot.writesichert den bisherigen Inhalt vor dem Überschreiben.POST /deon-ai/faq—{ uri, faq_html, body_field? }. Hängt einen FAQ-Block an den Entry-Body an (leereuri/"/"= Startseite). Idempotent: ein bereits vorhandener Block mitdata-deon-faq-Marker wird ersetzt statt dupliziert.POST /deon-ai/page—{ title, slug?, body_html, status?, section?, entry_id? }. Legt native Seiten an (Standortseiten, KI-Faktenseite). Section-Auflösung:section-Param → SettingpagesSectionHandle(Standardpages) →blogSectionHandle. Geht standardmäßig als Entwurf raus (statusexplizit"live"setzen für sofortige Veröffentlichung).
/deon-ai/entry und /deon-ai/page legen ohne entry_id immer einen neuen Entry an; mit entry_id aktualisieren sie den bestehenden. Zeigt eine explizit übergebene entry_id auf keinen (mehr) existierenden Entry, liefern beide 404 { "ok": false, "error": "entry_not_found" } statt still ein Duplikat anzulegen.
Alle drei sichern den bisherigen Inhalt fail-soft in einer eigenen Tabelle, bevor sie etwas überschreiben — ein Backup-Fehler blockiert dabei nie den eigentlichen Fix.
Sicherheit
Eingehende Deon-AI-Calls werden über den X-Deon-Key-Header authentifiziert (Konstantzeit-Vergleich). Das Plugin bricht die Seitenauslieferung nie: Jeder Patch-Schritt ist fail-soft.