Skip to main content

Public Center-Website API — Gap Analysis & Priorities

Status: Analysis against the code in apps/center-website (mainly lib/server-data-loader.ts) and apps/dashboard/src/app/api. Goal: external frontend (e.g., v0 + Vercel) can retrieve all relevant reads via https://dashboard.cockpit-os.de (or configured DASHBOARD_API_URL) — without Prisma on the client app and without data duplication.

Legend

SymbolMeaning
CORS *Access-Control-Allow-Origin: * (and typical OPTIONS) on GET responses
No CORSJSON is delivered, browser cross-origin requests from e.g., *.vercel.app fail without a proxy
AuthEndpoint expects a session / not for anonymous public access
Prisma CWCenter website currently loads directly from the DB or via its own /api routes of the same app (not the dashboard URL)

Note: Server-side rendering on Vercel (only fetch from Node) requires no CORS. As soon as the colleague uses client components or browser fetch, CORS + OPTIONS are necessary on the dashboard.


Source of Truth: server-data-loader.ts

Data / FeatureCurrent Path in LoaderDashboard Equivalent (if available)
Shop CategoriesDashboard GET …/website-categories (Dogfooding; logic in @mall-os/database)GET /api/categories (Dashboard list); Center website /api/categories = thin BFF
Page Content (incl. opening hours)Dashboard GET …/page-content (Dogfooding)GET CORS *
ServicesDashboard GET …/services?publicWebsite=true (Dogfooding)Without flag: only isActive
OfficesDashboard GET /api/offices?centerId=&status= (Dogfooding)Local /api/offices optional for BFF
Homepage TilesDashboard …/homepage-tiles
Category ThemesDashboard GET …/category-themes-for-website (Dogfooding; @mall-os/database)…/category-themes = raw for cockpit UI
Office ThemesDashboard GET …/office-themes (Dogfooding; isActive, only offices Active)
Gastronomy ThemesDashboard …/gastronomy-themes
ShopsDashboard GET …/shops?publicWebsite=true&status=… (Dogfooding; limit up to 5000)Without publicWebsite: broader status filter (not inactive)
Current Events (News, Events, Offers, Jobs)Dashboard GET …/aktuelles-bundle (Dogfooding; max 500 per type)Individual routes …/news etc. for other clients
Construction DiaryDashboard GET …/news?constructionDiary=true (Dogfooding in loader)✅ P1; local route /api/construction-diary remains in repo, SSR does not use it
Individual OfferDashboard GET …/offers?offerId= (Dogfooding in loader)✅ P1; local route /api/offers/[id] remains in repo, SSR does not use it
Center Plan / FloorsDashboard wayfinding/centerplan + wayfinding/floors (Dogfooding in loader; media resolved as before in loader)Center website /api/centers/centerplan = BFF (slug, combined response)

Related Files (Center Website):

  • apps/center-website/lib/server-data-loader.ts — Main loader
  • apps/center-website/app/api/categories/route.ts
  • apps/center-website/app/api/shops/route.ts
  • apps/center-website/app/api/aktuelles/route.ts (Client/BFF)
  • apps/center-website/app/api/construction-diary/route.ts
  • apps/center-website/app/api/offers/[id]/route.ts
  • apps/center-website/app/api/centers/centerplan/route.ts
  • apps/center-website/middleware.ts — including GET …/api/centers/by-domain

P0 — Status (Technical Foundation)

Implemented (CORS * + OPTIONS for Public Reads)

Shared module: apps/dashboard/src/lib/public-api-cors.ts.

AreaFile
Categories GETapps/dashboard/src/app/api/categories/route.ts
Page Content GETapps/dashboard/src/app/api/centers/[centerId]/page-content/route.ts (POST remains without broad CORS — protection against cross-origin write access)
Website Shop Categories GET…/centers/[centerId]/website-categories
Category Themes (Website Payload) GET…/centers/[centerId]/category-themes-for-website
Gastronomy/Category-(Admin)-/Office-Themes GET…/gastronomy-themes, …/category-themes, …/office-themes
Center Plan GETapps/dashboard/src/app/api/wayfinding/centerplan/route.ts
Floors GETapps/dashboard/src/app/api/wayfinding/floors/route.ts
Routing POST + Touchscreen GETapps/dashboard/src/app/api/wayfinding/routing/route.ts
Entrances GETapps/dashboard/src/app/api/centers/[centerId]/entrances/route.ts
DOOH Public (Playlists / Player for v0)…/centers/[centerId]/dooh/public/playlists, …/playlist, …/active, …/local-hero — see API Contract — DOOH

Public Config Bundle (Whitelist, No website-config)

RouteFile
GET /api/centers/[centerId]/public-visitor-surfaceapps/dashboard/src/app/api/centers/[centerId]/public-visitor-surface/route.ts

Delivers only explicitly allowed fields (branding, SEO, chatbot UI, center plan / wayfinding hints, apiHints). No API keys, no companionConfig, no analyticsPagePassword, no complete templateContent / themeOverrides.

Still Auth Required: GET /api/centers/[centerId]/website-config — for dashboard editors; external sites use public-visitor-surface + by-slug / theme-config as needed.

Chatbot (Visitor)

POST /api/ai/visitor-chatbot already had CORS * — conversation runs server-side in the dashboard (OpenAI key in the dashboard environment). Body: centerId and/or WordPress apiKey (see route documentation).

Already with CORS * (Reference): among others, …/[centerId]/shops, news, events, offers, jobs, services, homepage-tiles, hotpicks, by-slug/[slug], by-slug/[slug]/theme-config, by-domain, offices, visitor-chatbot.


P1 —Parity (Status)

TopicImplementation
Construction DiaryGET …/centers/[centerId]/news?constructionDiary=true (+ optional status) — including linkedShops, date range like website
Individual OfferGET …/centers/[centerId]/offers?offerId={uuid}websiteOfferPublicFilter, not To-Go-exclusive
Current BundleGET …/centers/[centerId]/aktuelles-bundle — CORS *, limits parameterizable (max 500 per type)
Hot PicksGET …/centers/[centerId]/hotpicks — public, max. 50 active entries; path is also data.apiHints.hotPicksGet on public-visitor-surface
OpenAPI Snapshotscockpit-docs/static/openapi/agencyos-integration.yaml + public-wayfinding-read.yaml (public wayfinding GETs); Markdown contract remains authoritative in case of discrepancies

P2 — Quality, Dogfooding, Operations

TopicFile / LocationRecommendation
Loader only via HTTPapps/center-website/lib/server-data-loader.tsDone: all previously mentioned reads including shop categories + category themes (website payload). Optional: OpenAPI
Rate Limiting / Abusepublic-visitor-rate-limit.ts + Redis (rate-limit.ts)Done for: POST /api/ai/visitor-chatbot, POST /api/wayfinding/routing (per IP, 429 + CORS). Edge middleware remains generally; new expensive public POSTs checked accordingly. Env: COCKPIT_DISABLE_PUBLIC_VISITOR_RATE_LIMIT=1
Media URLsLoader resolveMediaUrlExternal frontend: document the same URL resolution or dashboard delivers consistent absolute URLs
AgencyOS /context vs. Floorplan…/agencyos/v1/centers/{id}/contextinclude=floors_summary: compact floor without mapSvg body (length/hybrid heuristic, mapping counter). Full SVG: GET …/wayfinding/floors or MCP cockpit_public_wayfinding_floors. OpenAPI: /openapi/agencyos-integration.yaml (FloorsSummaryItem), /openapi/public-wayfinding-read.yaml.
AgencyOS Writing Website UIHomepage tiles, page content, media library listDone: …/homepage-tiles, …/page-content, GET …/media; MCP cockpit_homepage_tiles, cockpit_page_content, cockpit_list_media. Search including jobs/offices: cockpit_search_content.
SEO / Revalidateapps/center-website/app/api/revalidate/route.tsAgencyOS content_push automatically triggers revalidation (CENTER_WEBSITE_URLS + REVALIDATION_SECRET). External v0/Vercel: same route + secret + URL in CENTER_WEBSITE_URLS; MCP cockpit_revalidate_website.

Short Checklist for Product/Tech Decision

  1. P0: Public read configuration (without session) + CORS on all endpoints that the browser accesses from a different origin.
  2. P1: Construction diary + offer detail + optionally aggregate + written API contract.
  3. P2: Switch center website to the same URLs and secure operations.

  • packages/dashboard-api/src/index.tsgetDashboardApiUrl() (Env: DASHBOARD_API_URL, NEXT_PUBLIC_DASHBOARD_URL, NEXT_PUBLIC_API_URL)

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