Files
OPENBUREAU/cms/README.md
T
karim bd4b470877 cms: Rollen + Kollaboration (Admin sieht alles, Autoren nur eigene/geteilte)
- ADMIN_EMAILS (.env) = Admins, sehen/bearbeiten alles
- Autor:innen sehen nur Einträge mit ihrer Mail unter `authors:`; Ersteller wird
  beim Anlegen automatisch Autor
- Kollaboration: Feld „Autor:innen" im Editor → mehrere Mails = gemeinsamer Zugriff
- API erzwingt Zugriff bei list/read/save (403 ohne Recht)
- ADMIN_EMAILS in compose + LXC-Script (fragt Admin-Mail ab)

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

120 lines
5.4 KiB
Markdown

# 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/<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
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 <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
- [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