# 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: 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//…`), 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 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) 1. `cp .env.example .env` 2. `POSTGRES_PASSWORD` + `JWT_SECRET` setzen: je `openssl rand -hex 32` 3. Keys ableiten: `node scripts/generate-keys.mjs` → `ANON_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 `. | 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/` | ## Stand - [x] api + Hugo-Binary, Ein-Container-Setup - [x] Publish-Flow (DB → MD → `hugo` → live) - [x] Echte Hugo-Vorschau (`--buildDrafts` → `/_preview`) - [x] React-Admin (`admin/`) — Login, Editor, Frontmatter-Formular, iframe-Preview - [x] Supabase-Auth-Middleware auf der API - [x] 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