Auth/RLS-Fix (Schreiben gab 400): - supabase.js: eigener supabaseAuth-Client für Login/Token-Check, damit signInWithPassword den Service-Daten-Client nicht prozessweit aufs User-Token umstellt (sonst lief insert als role=authenticated → RLS-Block). Rollen (admin > editor > user): - auth.js: roleOf() aus app_metadata.role + ADMIN_EMAILS, requireModerator. - users.js: Rolle anzeigen/setzen über GoTrue app_metadata; .env-Admins fix. Datenmodell (schema.sql): - forums (Kategorien) + threads; Seed Allgemein/Projekte/Technik/Off-Topic und Sonder-Kategorie Beiträge. Library-Beiträge werden als Threads gespiegelt (dialog-store.syncLibrary). API (routes/dialog.js, dialog-store.js): - öffentlich: /api/forums, /api/forums/:slug, /api/recent, /api/thread - eingeloggt: POST /api/threads (Thread starten, nur in Foren) - Moderation: /api/mod/* (sperren/ausblenden), Admin: /api/admin/forums CRUD - comments: Lock-Prüfung beim Schreiben, Moderation darf jede löschen. Frontend: - static/dialog.js: Router (Übersicht-Split-View | Forum | Thread), neuer Thread, Mod-Leiste, subtiles Login (dezente Zeile statt Formular). - Admin-UI: Tabs Foren + Moderation, Rollen-Dropdown bei Autor:innen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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
cmsSupabase intern überhttp://kong:8000(Service-Key); die Admin-SPA nutzt browser-seitigAPI_EXTERNAL_URL+ANON_KEY. - Abweichung von RAPPORT:
realtime+storageweggelassen (nutzt das CMS nicht — Bild-Uploads gehen auf Platte nachstatic/images/). Nachrüstbar durch Kopieren der Service-Blöcke aus RAPPORT-SERVER.
Quelle der Wahrheit: die .md-Dateien
Dateibasiert. Die echten content/**/*.md sind kanonisch — das CMS liest und
schreibt sie direkt (Frontmatter via gray-matter). Damit erscheinen alle
bestehenden Inhalte im Editor: Beiträge (library/<rubrik>/…), Seiten
(manifest.md, colophon.md) und Rubriken (_index.md).
Supabase wird nur noch für den Login (GoTrue) gebraucht — keine Posts in der
DB. Drafts liegen als draft: true in der Datei; der Live-Build lässt sie aus,
der Preview-Build (--buildDrafts) zeigt sie.
Rechte & Kollaboration
- Admin (E-Mails in
ADMIN_EMAILS) sieht und bearbeitet alle Einträge. - Autor:innen sehen nur Einträge, in denen ihre Mail unter
authors:steht. Beim Anlegen wird der Ersteller automatisch eingetragen. - Kollaboration: im Editor weitere E-Mails ins Feld „Autor:innen" → beide haben Zugriff auf denselben Beitrag.
- Bestehende Beiträge/Seiten/Rubriken ohne
authors:sind nur für Admins sichtbar; ein Admin kann Autor:innen zuweisen, um sie freizugeben. - Hinweis:
authors:landet im Frontmatter (öffentliches Repo) — also E-Mails, die du dort einträgst, sind im Repo sichtbar.
Setup
Schnellweg: Proxmox-LXC
proxmox/create-openbureau-lxc.sh auf dem Proxmox-Host ausführen — legt den LXC
an, installiert Docker, zieht das (öffentliche) Repo, generiert alle Secrets und
startet den Stack. Als Einzeiler:
bash <(curl -fsSL https://git.kgva.ch/karim/OPENBUREAU/raw/branch/main/cms/proxmox/create-openbureau-lxc.sh)
Fragt interaktiv nur Storage/Bridge/IP ab (Enter = Default). Kein Token nötig.
GIT_TOKEN nur setzen, wenn das CMS per GIT_PUBLISH nach Gitea zurückschreiben soll.
Manuell (oder im Container)
cp .env.example .envPOSTGRES_PASSWORD+JWT_SECRETsetzen: jeopenssl rand -hex 32- Keys ableiten:
node scripts/generate-keys.mjs→ANON_KEY+SERVICE_ROLE_KEYin.env SITE_URL+API_EXTERNAL_URLauf die LAN-/Domain-Adresse setzendocker compose up -d --build(Erststart: DB bootet + Schema/Migrations)- 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/content |
Alle Einträge listen (Beiträge/Seiten/Rubriken) |
| GET | /api/content/entry?path=… |
Einen Eintrag lesen (Frontmatter + Body) |
| PUT | /api/content/entry |
Eintrag anlegen/speichern ({path, frontmatter, body}) |
| POST | /api/preview |
Preview-Build (--buildDrafts), liefert /_preview/… |
| POST | /api/publish |
Public-Build → live + (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