Skip to content

feat(composer): die Admin-Fläche, mit der der Editor spricht - #186

Merged
BechsteinDigital merged 1 commit into
mainfrom
fix/composer-admin-api-nach-main
Aug 7, 2026
Merged

feat(composer): die Admin-Fläche, mit der der Editor spricht#186
BechsteinDigital merged 1 commit into
mainfrom
fix/composer-admin-api-nach-main

Conversation

@BechsteinDigital

Copy link
Copy Markdown
Owner

Baut auf #183 auf. Base ist feat/composer-plugin; nach dessen Merge auf main umstellen
(gh pr edit <nr> --base main).

1. Why is this change necessary?

#183 hat das Plugin gebaut, das Layouts besitzt — aber niemanden, der mit ihm spricht. Das ist die
Fläche, über die der Editor den Entwurf holt, speichert, veröffentlicht und verwirft.

2. What does this change do, exactly?

Vier Routen, jede mit der Berechtigung, die sie verlangt:

Route Berechtigung
GET layouts/{layoutKey}/draft composer.layout.read
PUT layouts/{layoutKey}/draft composer.layout.write
POST layouts/{layoutKey}/publish composer.layout.publish
POST layouts/{layoutKey}/discard composer.layout.publish

Daran hängt die Entwurfs-Garantie. Zwei getrennte Methoden statt einer mit Schalter nützen
nichts, wenn die eine ungeschützt im Netz steht. Der öffentliche Renderpfad ruft
GetPublishedAsync auf einem anderen Vertrag und kommt hier nie an; der einzige Weg zum Entwurf
liegt hinter einer Operator-Berechtigung.

Veröffentlichen ist nicht Schreiben. Ein Entwurf ist noch niemandes Entscheidung — er darf
falsch, halbfertig oder ein Versuch sein. Veröffentlichen stellt ihn vor Besucher, und die beiden
sind verschieden genug, dass jemand vernünftigerweise das eine dürfen kann und das andere nicht.
Verwerfen zählt zum Veröffentlichen: Beide entscheiden über den Unterschied zwischen dem, was
jemand gebaut hat, und dem, was Besucher sehen — nur in verschiedene Richtungen.

Ein veralteter Änderungsstempel beim Speichern antwortet 409, nicht 200. Das ist der
Unterschied zwischen einem Editor, der jemandem sagt, dass seine Arbeit kollidiert ist, und einem,
der still die Hälfte davon wegwirft. Was der andere geschrieben hat, steht nicht in der
Antwort: Das lüde dazu ein, im Client zu mischen, und genau dort verschwindet Arbeit.

Wer veröffentlicht hat, kommt aus der Host-Anfrage, nie aus dem Body — sonst wäre es eine
Behauptung.

3. Describe each step to reproduce the issue or behaviour.

Zehn Tests, davon sieben über die Routen selbst statt über ihr Verhalten: dass jede eine
Berechtigung nennt, dass Veröffentlichen eine andere verlangt als Schreiben, dass jede verlangte
Berechtigung auch im Katalog des Hosts steht — sonst könnte niemand sie jemandem geben.

Gegenprobe: Nimmt man der Entwurfs-Route ihre Berechtigung, wird EveryRouteDeclaresAPermission
rot.

4. Please link to the relevant issues (if any).

Setzt §7.3 (Entwurfs-Garantie an ihrer Aufrufstelle) und den zweiten Teil von §8 Baustein 7 um.

Was die Tests gefunden haben

Der Save-Handler nahm {"document": 5} entgegen. Syntaktisch gültiges JSON, kein Layout — und
ohne Prüfung landete es in der Datenbank, wo erst der Renderer darüber fiele, bei einem Besucher.
Jetzt muss das Dokument ein Objekt sein.

Zahlen

11 Dateien. .NET 1971 + 29 Analyzer, zweimal wiederholt grün.

Noch nicht drin

Der Canvas selbst — die Vue-Oberfläche, in der jemand Blöcke zieht. Dafür fehlen noch die
beiden verbliebenen Bedingungen aus §7.6: Theme-Tokens und Sektions-CSS in den Canvas scopen.

Vier Routen: Entwurf lesen, Entwurf schreiben, veröffentlichen, verwerfen. Jede nennt die
Berechtigung, die sie verlangt, und genau daran hängt die Entwurfs-Garantie aus dem
Core-Vertrag: Zwei getrennte Methoden statt einer mit Schalter nützen nichts, wenn die eine
ungeschützt im Netz steht. Der öffentliche Renderpfad ruft GetPublishedAsync auf einem
anderen Vertrag und kommt hier nie an; der einzige Weg zum Entwurf liegt hinter
composer.layout.read.

VERÖFFENTLICHEN IST NICHT SCHREIBEN
Eigene Berechtigung. Ein Entwurf ist noch niemandes Entscheidung — er darf falsch,
halbfertig oder ein Versuch sein. Veröffentlichen stellt ihn vor Besucher, und die beiden
sind verschieden genug, dass jemand vernünftigerweise das eine dürfen kann und das andere
nicht. Verwerfen zählt zum Veröffentlichen: Beide entscheiden über den Unterschied zwischen
dem, was jemand gebaut hat, und dem, was Besucher sehen — nur in verschiedene Richtungen.

Ein veralteter Änderungsstempel beim Speichern antwortet 409, nicht 200. Das ist der
Unterschied zwischen einem Editor, der jemandem sagt, dass seine Arbeit kollidiert ist, und
einem, der still die Hälfte davon wegwirft. Was der andere geschrieben hat, steht NICHT in
der Antwort: Das lüde dazu ein, im Client zu mischen, und genau dort verschwindet Arbeit.

Wer veröffentlicht hat, kommt aus der Host-Anfrage, nie aus dem Body — sonst wäre es eine
Behauptung.

Zehn Tests, davon sieben über die Routen selbst statt über ihr Verhalten: dass jede eine
Berechtigung nennt, dass Veröffentlichen eine andere verlangt als Schreiben, dass jede
verlangte Berechtigung auch im Katalog des Hosts steht — sonst könnte niemand sie jemandem
geben. Die Gegenprobe belegt, dass eine Route ohne Berechtigung auffliegt.

Ein Fund beim Testen: Der Save-Handler nahm {"document": 5} entgegen. Syntaktisch gültiges
JSON, kein Layout — und ohne Prüfung landete es in der Datenbank, wo erst der Renderer
darüber fiele, bei einem Besucher. Jetzt muss das Dokument ein Objekt sein.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@BechsteinDigital
BechsteinDigital merged commit e762922 into main Aug 7, 2026
4 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant