Files
OPENBUREAU/cms/README.md
T
karim 60e5ef6844 cms: headless CMS vor Hugo (Supabase + Node-API + React-Admin)
All-in-One docker-compose-Stack (Muster von RAPPORT-SERVER gespiegelt):
db/auth/rest/kong + cms-Service (Node-API + Hugo-Binary 0.161.1 + Admin-SPA).

- DB-backed: posts-Tabelle kanonisch, MD ist generiertes Artefakt
- echte Hugo-Vorschau via draft:true + --buildDrafts → /_preview
- Publish: DB → content/library/<section>/<slug>.md → hugo build → live
- Bild-Upload nach static/images/, Supabase-Auth schützt /api/*
- Proxmox-LXC-Script: legt Container an, generiert Secrets, startet Stack

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 00:21:04 +02:00

4.3 KiB

OPENBUREAU CMS

Headless CMS vor der Hugo-Engine. Hugo bleibt die Render-Engine; dieser Stack schreibt Content aus Supabase in content/*.md, baut die Site und serviert sie.

Architektur

Ein docker-compose-Stack / ein LXC (Muster gespiegelt von RAPPORT-SERVER):

docker compose
├── db        supabase/postgres   ← posts-Tabelle (schema.sql)
├── auth      gotrue              ← Login
├── rest      postgrest           ← supabase-js liest/schreibt posts
├── kong      :8000               ← API-Gateway (/auth/v1, /rest/v1)
└── cms       :8080  Node + Hugo-Binary + Admin-SPA
                     ├─ /          live (public/)
                     ├─ /_preview  Vorschau (preview/, --buildDrafts)
                     ├─ /admin     React-Editor
                     └─ /api       Backend (mountet Repo unter /site)
  • cms hält das Hugo-Binary (0.161.1 extended, = lokal), mountet das Repo-Root unter /site, serviert die Site selbst (kein separater nginx).
  • Server-seitig spricht cms Supabase intern über http://kong:8000 (Service-Key); die Admin-SPA nutzt browser-seitig API_EXTERNAL_URL + ANON_KEY.
  • Abweichung von RAPPORT: realtime + storage weggelassen (nutzt das CMS nicht — Bild-Uploads gehen auf Platte nach static/images/). Nachrüstbar durch Kopieren der Service-Blöcke aus RAPPORT-SERVER.

Quelle der Wahrheit (DB-backed)

Die posts-Tabelle in Supabase ist kanonisch. content/*.md ist ein generiertes Artefakt — nicht von Hand editieren, wird beim Publish überschrieben. Drafts liegen als draft: true in content/ und werden vom Live-Build automatisch ausgelassen; nur der Preview-Build (--buildDrafts) zeigt sie.

Setup

Schnellweg: Proxmox-LXC

proxmox/create-openbureau-lxc.sh auf dem Proxmox-Host ausführen — legt den LXC an, installiert Docker, zieht das Repo, generiert alle Secrets und startet den Stack. Details im Script-Kopf (Storage, Bridge, GIT_TOKEN für das private Repo).

Manuell (oder im Container)

  1. cp .env.example .env
  2. POSTGRES_PASSWORD + JWT_SECRET setzen: je openssl rand -hex 32
  3. Keys ableiten: node scripts/generate-keys.mjsANON_KEY + SERVICE_ROLE_KEY in .env
  4. SITE_URL + API_EXTERNAL_URL auf die LAN-/Domain-Adresse setzen
  5. docker compose up -d --build (Erststart: DB bootet + Schema/Migrations)
  6. Login-User anlegen (Self-Signup ist aus):
    source .env
    curl -X POST "$API_EXTERNAL_URL/auth/v1/admin/users" \
      -H "apikey: $SERVICE_ROLE_KEY" -H "Authorization: Bearer $SERVICE_ROLE_KEY" \
      -H "Content-Type: application/json" \
      -d '{"email":"du@example.ch","password":"…","email_confirm":true}'
    

Dann: Admin …:8080/admin/ · Live …:8080/ · Preview …:8080/_preview/

Lokale Entwicklung am Admin

cd admin && npm install && npm run dev (Vite-Devserver, proxyt /api + /_preview an den laufenden Container auf :8080).

API

Alle /api/* (ausser /health) verlangen Authorization: Bearer <supabase-token>.

Methode Pfad Zweck
GET /api/health Healthcheck (offen)
GET /api/posts Alle Posts listen
GET /api/posts/:id Einen Post
POST /api/posts Post anlegen (Draft)
PUT /api/posts/:id Post aktualisieren
POST /api/preview/:id Draft als draft:true schreiben + Preview-Build
POST /api/publish/:id Live schreiben + Public-Build + (opt.) git commit
POST /api/upload Bild → static/images/, liefert /images/<name>

Stand

  • api + Hugo-Binary, Ein-Container-Setup
  • Publish-Flow (DB → MD → hugo → live)
  • Echte Hugo-Vorschau (--buildDrafts/_preview)
  • React-Admin (admin/) — Login, Editor, Frontmatter-Formular, iframe-Preview
  • Supabase-Auth-Middleware auf der API
  • Bild-Upload für cover_image

Noch denkbar

  • nginx davor für Caching/TLS (oder über deinen bestehenden Reverse-Proxy)
  • Post löschen / Slug-Umbenennung (alte MD-Datei entfernen)
  • Mehrbenutzer + Rollen, wenn ein zweiter Autor dazukommt