Zum Hauptinhalt springen

Public Center-Website API — Vertrag (Read)

Dieses Dokument ist der verbindliche Referenz-Vertrag für öffentliche Lesezugriffe auf Center-Daten über das CockpitOS-Dashboard (Next.js API Routes). Es dient:

  • als Spezifikation für Backend (P0/P1),
  • als Kontext für v0 / andere Frontends,
  • als Grundlage für späteres Dogfooding (server-data-loader nur noch per HTTP).

Siehe auch: Lückenanalyse & Prioritäten (CORS-Status, fehlende Routen).


Basis

FeldWert
Produktions-Basis-URLhttps://dashboard.cockpit-os.de
Konfiguration im CodegetDashboardApiUrl() in packages/dashboard-api — Env: DASHBOARD_API_URL, NEXT_PUBLIC_DASHBOARD_URL oder NEXT_PUBLIC_API_URL
Auth (dieser Vertrag)Keine Session, keine API-Keys — nur öffentlich zulässige Lesedaten. Schreibzugriffe und website-config GET sind nicht Teil dieses Vertrags.
Content-Typeapplication/json
FehlerTypisch { success: false, error: string } oder { error: string } mit passendem HTTP-Status (400, 404, 500). Exakte Felder können je Route leicht variieren.

Status je Endpunkt (Stand laut Gap-Analyse)

  • Bereit + CORS * — Browser von anderer Origin (z. B. Vercel) kann direkt fetchen.
  • Bereit, CORS offen (P0) — gleiche URL nutzbar von Server (SSR); für Client-seitiges fetch muss CORS nachgezogen werden.
  • Geplant (P1) — noch nicht oder nur teilweise am Dashboard; Vertrag beschreibt Ziel.

Ablauf: centerId ermitteln

Externe Apps kennen oft den Slug oder die Custom Domain, nicht die UUID.

1) Nach Slug

GET /api/centers/by-slug/{slug}

CORSJa (*)
AntwortObjekt mit u. a. id, name, slug, city, address, baseColor, logo, urls

Beispiel (gekürzt):

{
"id": "34eca9c3-1ea7-4c5a-b83a-d2b6bfb0c9f2",
"name": "Beispiel Center",
"slug": "beispiel-center",
"city": "Berlin",
"address": "Musterstraße 1",
"baseColor": "#3b82f6",
"secondaryColor": "#64748b",
"logo": "https://…",
"logoUrl": "https://…",
"coverImageUrl": null,
"websiteFavicon": null,
"theme": "light",
"organization": null,
"urls": {
"dashboard": "https://dashboard.cockpit-os.de/centers/34eca9c3-…",
"manager": "https://beispiel-center.manager.cockpit-os.de",
"signage": "https://beispiel-center.signage.cockpit-os.de",
"companion": "https://beispiel-center.signage.cockpit-os.de/companion",
"main": "https://preview.cockpit-os.de/beispiel-center"
}
}

2) Nach Custom Domain

GET /api/centers/by-domain?domain={host}

Parameterdomain — Hostname ohne Pfad, z. B. www.palais-vest.de oder palais-vest.de
CORSJa (*)

Beispiel:

{
"success": true,
"center": {
"id": "…",
"name": "…",
"slug": "…",
"customDomain": "palais-vest.de",
"websiteEnabled": true,
"domainVerified": true,
"baseColor": "#…",
"secondaryColor": "#…",
"logoUrl": "https://…",
"organizationId": "…",
"comingSoonEnabled": false
}
}

Theme & Website-Konfiguration (öffentlich)

Theme-Konfiguration (SPA / Template)

GET /api/centers/by-slug/{slug}/theme-config

CORSJa (*)
AntwortObjekt aus SpaThemeManager.getSpaThemeConfig(centerId) — Struktur center- und template-abhängig (Theme-Plugin, templateContent, Farben, Feature-Flags). Für stabile Felder: Live-Response eines Centers loggen und im Frontend defensiv parsen.

Hinweis: Vollständiges GET /api/centers/{centerId}/website-config ist auth-pflichtig und kein Teil dieses Vertrags. Entscheidung offen: später schlanker public-Endpoint oder nur dieses Bündel (by-slug + theme-config + nachfolgende Daten-Endpoints).


Shops

GET /api/centers/{centerId}/shops

QueryDefaultBeschreibung
limit100Seitengröße
offset0Offset

| CORS | Ja (*) |

Beispiel (Struktur):

{
"success": true,
"data": [
{
"id": "…",
"name": "Shop oder Filiale",
"category": "Fashion",
"isShopLocation": false,
"tags": [],
"logo": "https://…",
"slug": "…"
},
{
"id": "…",
"name": "Filiale XY",
"category": "Food",
"isShopLocation": true,
"chain": { "id": "…", "name": "Kette", "logo": "https://…" },
"floor": "EG"
}
],
"total": 42,
"pagination": {
"limit": 100,
"offset": 0,
"hasMore": false,
"nextOffset": null
},
"meta": {
"total": 42,
"limit": 100,
"offset": 0,
"returned": 42,
"centerId": "…",
"centerName": "…",
"breakdown": {
"standaloneShops": 10,
"shopLocations": 32,
"shopLocationsFiltered": 0
}
}
}

Shop-Kategorien

GET /api/categories

QueryBeschreibung
centerIdUUID des Centers (empfohlen)
statusz. B. Aktiv
includeGlobaltrue / false
minShopCountMindestanzahl Shops pro Kategorie
forServicestrue für Service-Kategorien

| CORS | P0 — aktuell ohne * (SSR ok, Browser-Cross-Origin erst nach Nachziehen) |

Antwort: JSON-Array von Kategorie-Objekten (inkl. u. a. id, name, icon, color, shopCount — exakte Felder siehe Prisma-Modell / Live-Response).

[
{
"id": "…",
"name": "Fashion",
"description": "…",
"icon": "shirt",
"color": "#e11d48",
"sortOrder": 1,
"shopCount": 12
}
]

News, Events, Angebote, Jobs

Alle unter GET /api/centers/{centerId}/… mit CORS *.

News

GET /api/centers/{centerId}/news

QueryBeschreibung
limitDefault 20
offsetDefault 0
publishedtrue filtert veröffentlichte + sichtbare Daten
categoryString-Filter auf category
{
"success": true,
"data": [ { "id": "…", "title": "…", "slug": "…", "excerpt": "…", "image": "…", "publishDate": "2026-03-01T10:00:00.000Z", "status": "…", "center": { "id": "…", "name": "…", "slug": "…" } } ],
"meta": { "total": 0, "limit": 20, "offset": 0, "returned": 0, "centerId": "…" }
}

P1 — Baustellen-Tagebuch: Filter isConstructionDiary fehlt auf dieser Route; geplant: Query constructionDiary=true oder GET /api/centers/{centerId}/construction-diary.

Events

GET /api/centers/{centerId}/events — Query u. a. limit, offset (siehe Route).

Angebote (Liste)

GET /api/centers/{centerId}/offers — Query u. a. limit, offset.

P1 — Einzel-Angebot: GET /api/centers/{centerId}/offers/{offerId} oder ?id= — noch zu spezifizieren/implementieren.

Jobs

GET /api/centers/{centerId}/jobs — Query u. a. limit, offset.


Services

GET /api/centers/{centerId}/services

| CORS | Ja (*) |

Antwortstruktur siehe Live-Call; typisch Liste/Success-Wrapper analog zu anderen Center-Routen.


Büros & Praxen

GET /api/offices

QueryBeschreibung
centerIdempfohlen
typeTyp-Filter oder all
statusz. B. Aktiv oder all

| CORS | Ja (*) |

{
"success": true,
"data": [
{
"id": "…",
"name": "…",
"type": "Praxis",
"floor": "1. OG",
"logo": "https://…",
"center": { "id": "…", "name": "…", "slug": "…" },
"officeType": { "id": "…", "name": "…" }
}
],
"count": 1
}

Page Content (CMS-Seiten, z. B. Öffnungszeiten)

GET /api/centers/{centerId}/page-content

| CORS | P0 — ergänzen für Browser-Cross-Origin |

{
"pageContents": [
{
"id": "…",
"centerId": "…",
"pageType": "oeffnungszeiten",
"pageTitle": "Öffnungszeiten",
"pageSubtitle": null,
"pageDescription": "…",
"metaTitle": "…",
"metaDescription": "…",
"metaKeywords": null,
"customContent": {},
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z"
}
]
}

pageType-Werte sind center-abhängig (u. a. shops, gastronomy, oeffnungszeiten, kontakt, …).


Homepage-Kacheln

GET /api/centers/{centerId}/homepage-tiles

| CORS | Ja (*) |

{
"tiles": [
{
"id": "…",
"centerId": "…",
"title": "…",
"subtitle": null,
"href": "/shops",
"icon": "shopping-bag",
"order": 0,
"mobileSize": "1x",
"desktopSize": "1x1",
"backgroundType": "gradient",
"backgroundConfig": null,
"contentType": null,
"contentConfig": null,
"enabled": true,
"gridColumnSpan": 1,
"gridRowSpan": 1
}
]
}

Themes: Gastronomie, Kategorien, Büros

RouteCORS (laut Analyse)
GET /api/centers/{centerId}/gastronomy-themesP0
GET /api/centers/{centerId}/category-themesP0
GET /api/centers/{centerId}/office-themesP0

Antwort: jeweils Array/Objekt mit Theme- und Shop-/Office-Zuordnungen — Live-Response als Referenz nutzen (umfangreiche Includes).


Wayfinding / Centerplan

RouteMethodeCORS (laut Analyse)
GET /api/wayfinding/centerplan?centerId={uuid}GETP0
GET /api/wayfinding/floors?centerId={uuid}GETP0
POST /api/wayfinding/routingPOSTP0 (Body: Start/End-Waypoints oder Location-IDs, siehe Route)
GET /api/centers/{centerId}/entrancesGETP0

Centerplan GET liefert u. a. id, centerId, name, floors (geordnet) — verschachtelte Struktur siehe Prisma-Select in der Route.


Optional geplant (P1)

EndpointZweck
GET /api/centers/{centerId}/aktuelles (oder ähnlich)News + Events + Offers + Jobs in einem Response (weniger Roundtrips)
GET …/news?constructionDiary=true oder …/construction-diaryNur Baustellen-Tagebuch-Einträge
GET …/offers/{offerId}Einzelnes Angebot inkl. Shop

Copy-Paste: Kontext für v0 / KI

Baue eine öffentliche Center-Website (Next.js, Server Components wo möglich).

API-Basis: https://dashboard.cockpit-os.de

Ablauf:
1) Center auflösen: GET /api/centers/by-slug/{slug} → id ist centerId (UUID).
Alternativ Custom Domain: GET /api/centers/by-domain?domain={hostname}
2) Theme/UI-Daten: GET /api/centers/by-slug/{slug}/theme-config
3) Inhalte (centerId einsetzen):
- Shops: GET /api/centers/{centerId}/shops?limit=100&offset=0
- Kategorien: GET /api/categories?centerId={centerId}&status=Aktiv&includeGlobal=true
- News: GET /api/centers/{centerId}/news?published=true&limit=20
- Events: GET /api/centers/{centerId}/events
- Angebote: GET /api/centers/{centerId}/offers
- Jobs: GET /api/centers/{centerId}/jobs
- Services: GET /api/centers/{centerId}/services
- Büros: GET /api/offices?centerId={centerId}&status=Aktiv
- CMS-Seiten (Öffnungszeiten etc.): GET /api/centers/{centerId}/page-content
- Homepage-Kacheln: GET /api/centers/{centerId}/homepage-tiles
- Centerplan: GET /api/wayfinding/centerplan?centerId={centerId}
- Etagen/Karte: GET /api/wayfinding/floors?centerId={centerId}

Hinweise:
- Responses nutzen teils { success, data }, teils direkte Arrays oder { tiles }, { pageContents } — jeweils prüfen.
- Einige Routen brauchen noch CORS für reines Browser-fetch von einer anderen Domain; bevorzuge fetch im Server (RSC/Route Handler).
- Keine Secrets im Client; keine Schreib-Endpunkte ohne separates Auth-Konzept.

Pflege dieses Dokuments

  • Nach CORS-P0: Status-Spalten oben und betroffene Abschnitte auf „Bereit + CORS *“ anheben.
  • Nach P1-Implementierung: neue URLs und Beispiele ergänzen.
  • Bei Schema-Änderungen in Prisma: Beispiel-JSON anpassen oder auf „Live-Response“ verweisen.

Code-Referenz: apps/dashboard/src/app/api/…

Nutzungsstatistik: Seitenaufrufe werden anonymisiert erfasst. Im Umami-Dashboard nach diesem Pfad filtern: /developer-guide/public-center-website-api-vertrag