- Alt-Vertrag komplett aus dem Symfony-Code extrahiert (Routen, Params verschachtelt, Response-Shapes, Sofort-Publish-Logik, Fehlerformat) - Prod-Logs (dev/api, User) ausgewertet: ein Client (connektar), 5 Endpoints; Company-/Kontakt-Create heute 410 -> muessen nachgebaut werden; Sofort-Publish beim POST belegt - Entscheidungen 10.07.: Sofort-Publish + Admin-Schalter KI-Pruefung, getrennte Legacy-/v1-API, Ablehnungs-Rueckkanal spaeter - dev/api/ enttrackt (.gitignore, Produktivdaten analog SQL-Dumps) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
13 KiB
API-Legacy-Parität — Arbeitsdokument (Stand 10.07.2026)
Ziel (User-Vorgabe 10.07.): Beim Cutover muss die API-Schnittstelle für Bestandskunden erst einmal genauso funktionieren wie vorher. Dieses Dokument hält den belegten Alt-Vertrag fest, listet die Paritäts-Lücken und führt die einzelnen Schritte als Checkliste. Testdaten-Wünsche des Users folgen (Abschnitt „Testdaten", unten).
Quellenlage: Der komplette Alt-Code liegt im Repo unter
_businessportal24.com/ (Symfony 1.4). Analysiert am 10.07.:
apps/frontend/config/routing.yml (service_-Routen),
plugins/PressePortalPlugin/modules/pressrelease/lib/pluginPressReleaseActions.class.php,
modules/pressreleaseimage/actions/actions.class.php,
modules/company/actions/actions.class.php, lib/ApiActions.class.php,
lib/filters/ApiKeyGuardAuthFilter|SecureApiAccessFilter|ApiAccessFilter,
modules/tnHttpError/templates/*.json.php, JSON-Templates der Module.
1. Belegter Alt-Vertrag (aus dem Symfony-Code)
Authentifizierung & Zugriff
X-ApiKey-Header (FilterApiKeyGuardAuthFilterliestsfGuardUserProfile.api_key); Web-Session konnte API-Routen ebenfalls nutzen.GET/HEADverlangt CredentialapiReadAccess,POST/PUT/DELETEverlangtapiWriteAccess, andere Methoden → 405 (SecureApiAccessFilter).X-ApiKeyauf Nicht-API-Routen → 501 (ApiAccessFilter).- CSRF für API-Requests deaktiviert.
Endpoints (12 API-Routen laut routing.yml, alle mit .json-Suffix!)
| Alt-URL (Methode) | Verhalten |
|---|---|
POST /service/pressreleases.json |
PM anlegen. Params verschachtelt: press_release[company_id] (Pflicht), press_release[category_id], press_release[language], press_release[title], press_release[text], press_release[contacts_list][], ggf. press_release[backlink_url]. Status-Logik: Standard new (= Redaktions-Queue); hatte die Firma hasActivePerPresskitPayment() oder der User die Permission immediatePublication → sofort published. Response: 201 + Location-Header auf die Show-URL, Body {}. Es gab keinen separaten Submit-Call — anlegen = einreichen. |
GET /service/pressreleases.json |
Öffentlicher Index: nur status=published, portalweit (NICHT auf den User beschränkt!), Filter query[languages][], query[categories][], query[offset], query[limit], sortiert created_at DESC. Response: JSON-Array von Show-URLs (Strings), keine Objekte. |
GET /service/pressreleases/{id}.json |
Nur published (sonst 404; inaktive Firma → 404). Zählt Hits (session-dedupliziert). Response: flaches Objekt {id, title, text, teaser_end, language, category_id, view_url, company: {viewUrl, name}}. |
GET /service/pressreleases/{id}/images.json |
Nur bei published PM. Response: Array von Bild-Show-URLs. |
POST /service/pressreleases/{id}/images.json |
Nur Eigentümer (sonst 403 „You are not the creator …"). Multipart, Felder verschachtelt press_release_image[image] (+ title/description/copyright). Response: 201 + Location, Body {}. |
GET /service/pressreleases/{id}/images/{imgId}.json |
Response: {id, title, description, copyright, image_file_url}. |
GET /service/users/me/companies.json |
Eigene Firmen; limit/offset direkt ODER query[limit]/query[offset]. Response: Array von Show-URLs. |
POST /service/users/me/companies.json |
Firma anlegen (201 + Location, Body {}). |
GET /service/users/me/companies/{id}.json |
Nur eigene (sonst 403). |
GET/POST /service/users/me/companies/{id}/contacts.json + GET …/contacts/{id}.json |
Pressekontakte lesen/anlegen. |
Fehlerformat
- 422:
{ "global_errors": [...], "field_errors": {...} } - andere Fehler:
{ "global_errors": [...] }
2. Ist-Stand der neuen Aliasse (routes/legacy-api.php)
Schon da: Key-Import (legacy:import-api-keys, 406 Tokens), Auth-Übersetzung
X-ApiKey/api_key → Bearer, unbekannte Keys 410 + Hinweis, Rate-Limit,
Eligibility-Checks, Alias-Routen für pressreleases (GET/POST), pressreleases/{id}
(GET), images (GET/POST), users/me/companies (GET, Show), Rest → 410-Fallback.
Aber: Die Aliasse zeigen direkt auf die v1-Controller — Request- und Response-Verträge sind v1, nicht Alt. Für „funktioniert genauso wie vorher" fehlt eine Kompatibilitätsschicht.
3. Paritäts-Lücken (Befund 10.07.)
| # | Lücke | Wirkung auf Alt-Clients |
|---|---|---|
| L1 | .json-Suffix: Alt-URLs enden auf .json, unsere Aliasse matchen nur ohne Suffix |
Calls wie /service/pressreleases.json landen im 410-Fallback → Totalausfall |
| L2 | Verschachtelte Params: press_release[...] / press_release_image[...] / query[...] vs. flache v1-Felder |
POST/Filter validieren nicht → 422 → Anlegen unmöglich |
| L3 | Kein Auto-Submit: alt = anlegen ist einreichen (new → Redaktion bzw. sofort published); neu = Draft, Submit-Endpoint existiert im /service-Raum gar nicht |
PMs bleiben unsichtbar als Entwürfe liegen → Kernfunktion kaputt |
| L4 | Index-Semantik: alt = öffentliche published-Liste (portalweit) als URL-Array; neu = eigene PMs als Objekt-Collection | Clients, die den Index parsen/crawlen, brechen |
| L5 | Show-Shape: alt = flaches Objekt mit view_url, company.viewUrl; neu = v1-Resource (andere Felder/Nesting), owner-scoped statt published-only |
Parser brechen; nicht-eigene published PMs sind nicht mehr abrufbar |
| L6 | Create-Response: alt = 201 + Location + {}; neu = 201 + volle Resource |
vermutlich unkritisch (Location fehlt aber) |
| L7 | Fehlerformat: alt = global_errors/field_errors; neu = Laravel message/errors |
Fehlerbehandlung der Clients bricht (weich) |
| L8 | Nicht abgebildete Endpoints: Company-CREATE, Presscontact-Index/Create/Show, Einzelbild-GET → heute 410 (bewusste Entscheidung 06.07.) | Clients, die Firmen/Kontakte per API anlegen, brechen |
| L9 | KI-Deklarations-Default für /service ist umgesetzt (none/original, 10.07.) — bei Auto-Submit (L3) mitdenken | erledigt, nur verdrahten |
Einordnung: Der bisherige „end-to-end verifiziert (200)"-Test vom 06.07. prüfte Auth + Statuscode der suffix-losen Pfade, nicht den Alt-Vertrag. Verhaltensparität ist mit dem Alt-Code jetzt exakt herstellbar.
3a. Log-Auswertung Prod (User hat Logs geliefert → dev/api/, 10.07.)
Strukturierte Aktivitäts-Logs beider Portale (Mai–Juli 2026, b24_activity +
pe_activity; 8.622 erfolgreiche API-Auths, 619 Publikationen). Ergebnis:
- Genau EIN echter Client: connektar (
connekt.connektar.de, Userinfo@connektar.de, Legacy-ID 11768 auf bp24). Daneben nur eine tote Integration mit ungültigem Key (login_failed, kein User-Agent). - Tatsächlich genutzte Endpoints (alle OHNE
.json-Suffix!):Calls Endpoint Status heute 5.935 POST /service/users/me/companies410 — fehlt! 976 POST /service/users/me/companies/{id}/contacts410 — fehlt! 594 POST /service/pressreleasesAlias vorhanden, Params/Verhalten abweichend (L2/L3) 594 GET /service/pressreleases/{id}Alias vorhanden, Response/Sichtbarkeit abweichend (L5) 523 POST /service/pressreleases/{id}/imagesAlias vorhanden, Params abweichend (L2) pressrelease_publishedfeuert direkt beim POST — connektars PMs gehen sofort live (Sofort-Publish-Privileg bestätigt; Flow: Firma anlegen → Kontakte → PM (sofort published) → Bilder → GET zur Kontrolle).- Damit entschärft: L1 (
.json-Suffix wird real nicht genutzt), L4 (kein Index-Call). Verschärft: L8 — Company-/Kontakt-Create sind die meistgenutzten Calls überhaupt und heute tot.
3b. Entscheidungen des Users (10.07.)
- Zwei APIs: Die Legacy-API (
/service/…) bildet für Bestandskunden das Alt-Verhalten ab; die v1-API bleibt die neue, saubere Schnittstelle mit den neuen Funktionen (KI-Deklarationsfelder etc.). - Sofort-Publish nachbilden + Admin-Schalter: Standard = eingehende Legacy-PMs werden wie im Altsystem sofort veröffentlicht. Über einen Schalter im Admin kann stattdessen die KI-Prüfung aktiviert werden: dann Status review → KI routet (Gelb/Grün sofort veröffentlichen, Rot nicht).
- Rückmeldung bei Ablehnung: War im Altsystem nicht vorhanden (connektar pollt GET — 404 bis published). Ablehnungs-Mail an den Einlieferer existiert im neuen System bereits (
notifyAuthor); ein maschinenlesbarer Rückkanal (Status-Feld/Webhook) kommt später als Feature der neuen API — Integration hier nur vormerken, nicht Teil der Parität.
4. Schritte (Checkliste — hier abhaken + dokumentieren)
Scope nach Log-Auswertung (§3a): die 5 connektar-Endpoints, verhaltensgleich.
- S1 — Legacy-Compat-Controller
/service/…(ersetzt die direkten v1-Aliasse):POST /service/users/me/companies— Feldercompany[name|address|country_id|phone|fax|email|website](+logo-Datei); legacycountry_id→ neuercountry_code; Response201+Location+{}; Alt-Fehlerformat.POST /service/users/me/companies/{id}/contacts— Feldercontact[salutation_id|title|first_name|last_name|responsibility|phone|fax|email]; Zugriff nur eigene Firma (403-Wortlaut s. §1); Response wie oben.POST /service/pressreleases— Felderpress_release[company_id|contacts_list[]|category_id|language|title|text|backlink_url|…]; Statusverhalten s. S2; KI-Deklarations-Default none (erledigt 10.07.); Response201+Location+{}.POST /service/pressreleases/{id}/images— Multipartpress_release_image[image|title|description|copyright]; Eigentümer-Check (403 „You are not the creator of this press release"); ai_declaration-Default original (erledigt 10.07.); Response201+Location+{}.GET /service/pressreleases/{id}— nurpublished(sonst 404 — connektars Polling-Kontrolle!), Hit-Zählung, flaches Alt-Objekt{id,title,text,teaser_end,language,category_id,view_url,company:{viewUrl,name}}.- Alt-Fehlerformat überall:
{"global_errors":[…],"field_errors":{…}}(422) bzw.{"global_errors":[…]}. - Pest-Paritäts-Tests je Endpoint gegen den §1-Vertrag (inkl. connektar-Flow end-to-end: Firma → Kontakt → PM → Bild → GET).
- Nicht nachgebaut (real ungenutzt, bleiben 410): GET-Index-Routen, Einzelbild-GET, Company-GET/Show, Kontakt-GET,
.json-Suffix-Formen. GET users/me/companies + Show bleiben als vorhandene Aliasse bestehen (real nur von der toten Integration gecallt, schadet nicht).
- S2 — Statusverhalten create (Entscheidung 10.07., §3b): Default Sofort-Publish (Alt-Verhalten, per Log belegt); Admin-Schalter „KI-Prüfung für Legacy-API" → Status review + KI-Routing (Gelb/Grün auto-publish, Rot Ablehnung + Mail). Umsetzung: Setting (config + Admin-UI-Toggle), Legacy-Create wertet es aus. Blacklist/Slot-Verbrauch laufen in beiden Wegen über
PressReleaseService::publish(). - S3 — Endpoint-Scope entschieden (10.07.): per Log-Auswertung (§3a) — nur die 5 echten connektar-Endpoints nachbauen, Rest bleibt 410. (User hat Logs in
dev/api/geliefert; ersetzt das frühere [EXTERN] Log-Beschaffen.) - S4 — Testdaten (User liefert Vorgaben, s. Abschnitt unten): Vorschlag: Artisan-Command
legacy:seed-api-testdata— legt API-Testkunden nach connektar-Muster an (aktiv, grandfathered, Legacy-Key) inkl. Firma, Kategorie, Beispiel-PMs und gibt Key + curl-Sammlung aus; nurlocal/staging. - S5 — Paritäts-Smoke auf Staging: curl-Sammlung des connektar-Flows (X-ApiKey, verschachtelte Params) gegen Staging; Vergleich gegen Alt-System-Responses.
- S6 — Rückkanal Ablehnung (nachgelagert, neue API): Status-Feld/Webhook in v1 für „abgelehnt" — Design offen; bis dahin gilt Alt-Verhalten (GET pollt, 404 = nicht veröffentlicht) + Ablehnungs-Mail.
- S7 — R6.4-Mailing ergänzen: Legacy-Keys laufen weiter, Verhalten unverändert (Sofort-Publish bzw. KI-Schalter), KI-Deklarations-Default (Erklärungswirkung), Hinweis auf neue v1-API.
5. Testdaten (Platzhalter — Vorgaben vom User folgen)
User (10.07.): „hier hätte ich gerne auch selber ein paar Testdaten — dazu gleich mehr". Sobald die Wünsche da sind, hier dokumentieren (welche Kunden, welche PMs/Firmen, welcher Kanal: /service-Alt-Calls und/oder v1) und in S4 umsetzen.
6. Protokoll
- 10.07.2026: Dokument angelegt; Alt-Vertrag vollständig aus dem Symfony-Code extrahiert (§1); Lücken L1–L9 identifiziert (§3). Vorarbeiten heute bereits erledigt: KI-Deklarations-Defaults für /service (L9), Dev-Queue-Fix (KI-Pipeline lokal lauffähig).
- 10.07.2026 (2): User liefert Prod-Aktivitäts-Logs (
dev/api/) → Auswertung §3a: ein Client (connektar), 5 Endpoints, Sofort-Publish belegt; zwei der fünf laufen heute auf 410. User-Entscheidungen §3b festgehalten (Sofort-Publish + Admin-Schalter KI-Prüfung, zwei getrennte APIs, Rückkanal später). Schrittplan S1–S7 auf den echten Scope zugeschnitten; S3 damit entschieden. Nächster Schritt: S1 + S2 implementieren.