apps/admin/: - Nuxt 4.1.3 + @nuxt/ui 4 + @nuxtjs/supabase, port 3017 staging - 7 pages: index (59 LOC dashboard), login (72 LOC), auth/confirm, plus stubs für domains/users/stats/moderation (14-17 LOC each, content für separate Phase 2 Session) - composables/useAdminAuth.ts: Supabase login + verifyAdminRole hook - middleware/admin-auth.ts: route guard (Phase 3 backend-check ready) - layouts/default.vue, app.vue, README.md - nuxt.config.ts: SSR=true, port 3017, dark-mode preference, Supabase pkce-flow, runtimeConfig.adminSecret für Phase 3 backend-binding Deploy-Infrastructure: - .github/workflows/deploy-admin-staging.yml: build admin auf push to main mit path-filter apps/admin/**, scp tar zu Server, atomic-mv + pm2 restart - scripts/deploy-admin-from-artifact.sh: Server-side deploy (extract, atomic mv, pm2 reload). Kein prisma-migrate (admin hat kein eigenes DB-Schema). - apps/admin/start-admin-staging.sh: pm2 start-script mit Infisical-wrapper, port 3017, mappt Infisical SUPABASE_URL/KEY auf NUXT_PUBLIC_* - ecosystem.config.js: rebreak-admin-staging Eintrag (port 3017, max_memory_restart 400M) - ops/nginx/admin-staging.rebreak.org.conf: HTTP→HTTPS redirect, SSL paths, proxy auf 127.0.0.1:3017, noindex header Pending User-Actions für go-live: 1. DNS-A-Record admin.staging.rebreak.org → 49.13.55.22 2. SSL-cert via certbot (oder bestehender wildcard *.staging.rebreak.org) 3. nginx-config auf Server aktivieren (sudo cp + ln + reload) 4. pm2 initial start: pm2 start ecosystem.config.js --only rebreak-admin-staging 5. Infisical-secret ADMIN_SECRET (server-only, Phase 3 binding) GH-Actions: keine neuen Secrets (nutzt bestehende HETZNER_SSH_KEY/HOST/USER) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
259 lines
8.0 KiB
Markdown
259 lines
8.0 KiB
Markdown
# rebreak Admin
|
|
|
|
Internes Verwaltungspanel fuer rebreak.org.
|
|
|
|
**Status:** Phase 1 -- Skeleton (Auth-Wiring, Layout, Stub-Pages). Keine echten API-Calls.
|
|
|
|
---
|
|
|
|
## Stack
|
|
|
|
| Schicht | Technologie |
|
|
|---|---|
|
|
| Framework | Nuxt 4.1.3 (SSR) |
|
|
| UI | @nuxt/ui 4.x (Tailwind 4, Nuxt UI Komponenten) |
|
|
| Auth | @nuxtjs/supabase 2.x (PKCE-Flow) |
|
|
| Backend-Komm. | $fetch gegen /api/admin/* (Phase 3) |
|
|
| Laufzeit | Node 24.11.1 / pm2 auf Hetzner CX23 |
|
|
|
|
---
|
|
|
|
## Wo die Admin-App lebt
|
|
|
|
| Environment | URL | Port (intern) | pm2-Service |
|
|
|---|---|---|---|
|
|
| Staging | admin.staging.rebreak.org | 3017 | rebreak-admin-staging |
|
|
| Prod | admin.rebreak.org | 3018 | rebreak-admin |
|
|
|
|
Nginx-Routing-Config: wird in Phase 2 Deploy angelegt (analog zu staging.rebreak.org-Config).
|
|
|
|
---
|
|
|
|
## Lokale Entwicklung
|
|
|
|
```bash
|
|
# Vom Monorepo-Root:
|
|
pnpm dev:admin
|
|
|
|
# Oder direkt:
|
|
cd apps/admin && pnpm dev
|
|
```
|
|
|
|
Laeuft auf http://localhost:3017.
|
|
|
|
Infisical-Secrets werden fuer lokales Dev nicht gebraucht -- die Supabase-URL/Key
|
|
kommen als `process.env.NUXT_PUBLIC_SUPABASE_URL/KEY` oder fallen auf Staging-Defaults zurueck.
|
|
|
|
---
|
|
|
|
## Auth-Architektur
|
|
|
|
```
|
|
Admin-Browser
|
|
|
|
|
| 1. POST /api/auth/login (Supabase Email/Password)
|
|
v
|
|
Supabase Auth (db-staging.rebreak.org oder db.rebreak.org)
|
|
|
|
|
| 2. JWT zurueck (access_token)
|
|
v
|
|
Admin-Browser haelt Session (PKCE-Flow, persistSession=true)
|
|
|
|
|
| 3. GET /api/admin/* (Authorization: Bearer <token>)
|
|
v
|
|
Backend (staging.rebreak.org)
|
|
|
|
|
| 4. requireAdmin-Middleware:
|
|
| - JWT verifizieren (Supabase public key)
|
|
| - user_id in admin_users-Tabelle pruefen
|
|
| - Bei Misserfolg: 403
|
|
v
|
|
Admin-Endpoint-Response
|
|
```
|
|
|
|
**Aktueller Status (Phase 1):** Supabase-Login funktioniert. Schritt 4 (requireAdmin) ist NICHT
|
|
implementiert -- jeder eingeloggte Supabase-User koennte theoretisch rein, wenn er die URL kennt.
|
|
Das ist akzeptabel weil die Admin-URL nicht public ist und Staging-Daten keine hochsensiblen
|
|
Produktionsdaten enthalten.
|
|
|
|
**Phase 3 schaltet requireAdmin ein** -- dann ist der Zugang haerter gesperrt.
|
|
|
|
---
|
|
|
|
## DSGVO-Considerations fuer Admin-Zugriff
|
|
|
|
Admins haben Zugriff auf User-Daten. Das bedingt:
|
|
|
|
1. **Audit-Log (TODO Phase 4 -- hans-mueller):** Jede Admin-Aktion (User ansehen, Domain genehmigen,
|
|
Content moderieren) muss in einer `admin_audit_log`-Tabelle geloggt werden:
|
|
- Wer (admin_user_id)
|
|
- Was (action: "view_user" | "approve_domain" | "reject_domain" | "ban_user")
|
|
- Welcher Datensatz (target_id)
|
|
- Wann (timestamp)
|
|
|
|
2. **Daten-Minimierung im Admin-UI:** User-Liste zeigt NIEMALS echten Namen oder E-Mail.
|
|
Nur Nickname (analog zur App-Anzeige). E-Mail ist nur fuer Kontaktaufnahme via Support-Ticket,
|
|
nicht fuer Browse-UI.
|
|
|
|
3. **Admin-Zugangs-Liste:** `admin_users`-Tabelle in Supabase darf nur von User (Chahine) befuellt werden.
|
|
Kein Self-Signup, kein automatisches Promoten.
|
|
|
|
4. **Data-Processing-Agreement:** Wenn externe Personen Admin-Zugang bekommen (z.B. Moderatoren),
|
|
braucht es einen AVV. Aktuell: nur interner Zugang (Chahine).
|
|
|
|
5. **Datenschutzfolgeabschaetzung (DSFA):** Admin-Zugang auf Nutzerdaten von Suchtkranken faellt
|
|
unter Art. 35 DSGVO (besondere Kategorien). Hans-Mueller-Task.
|
|
|
|
---
|
|
|
|
## Deploy-Plan
|
|
|
|
### Variante A: SSR auf Hetzner (Empfehlung)
|
|
|
|
Analog zu `rebreak-staging` -- separater pm2-Service, separater Port, nginx-Subdomain.
|
|
|
|
**Pros:** Kein zusaetzlicher Hosting-Service, Server-Side-Auth-Checks funktionieren native,
|
|
Infisical-Secrets per Infisical-run-wrapper wie gewohnt.
|
|
|
|
**Cons:** Belastet CX23 zusaetzlich (RAM). Admin-App hat aber wenig Traffic -- kein Risiko.
|
|
|
|
**Setup:**
|
|
```bash
|
|
# /srv/rebreak/ecosystem.config.js (ergaenzen):
|
|
{
|
|
name: 'rebreak-admin-staging',
|
|
script: '/srv/rebreak/apps/admin/.output-staging/server/index.mjs',
|
|
env: { PORT: 3017, NODE_ENV: 'production' }
|
|
}
|
|
```
|
|
|
|
```nginx
|
|
# nginx: admin.staging.rebreak.org
|
|
server {
|
|
listen 443 ssl;
|
|
server_name admin.staging.rebreak.org;
|
|
location / {
|
|
proxy_pass http://127.0.0.1:3017;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Variante B: Static via Cloudflare Pages
|
|
|
|
`nuxt generate` + Deploy zu Cloudflare Pages.
|
|
|
|
**Pros:** Kostenlos, globales CDN, keine Hetzner-Last.
|
|
|
|
**Cons:** Kein echter SSR (Auth-Checks nur client-side), API-Calls gehen trotzdem zu Hetzner.
|
|
Fuer eine Admin-App die Server-Side-Checks braucht suboptimal.
|
|
|
|
**Entscheidung: Variante A (SSR auf Hetzner).**
|
|
|
|
---
|
|
|
|
## GitHub-Actions-Pipeline -- Plan fuer Phase 2 Deploy
|
|
|
|
Die bestehende `.github/workflows/deploy-staging.yml` baut nur `backend/`.
|
|
Admin-App braucht einen separaten Job ODER einen eigenen Workflow.
|
|
|
|
### Option 1: Separater Workflow `deploy-admin-staging.yml` (empfohlen)
|
|
|
|
```yaml
|
|
name: Deploy Admin Staging
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
paths:
|
|
- "apps/admin/**"
|
|
- ".github/workflows/deploy-admin-staging.yml"
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: pnpm/action-setup@v4
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 24.11.1
|
|
cache: pnpm
|
|
- run: pnpm install --frozen-lockfile
|
|
- name: Build admin
|
|
working-directory: apps/admin
|
|
run: pnpm build
|
|
- run: tar czf admin-output.tar.gz -C apps/admin/.output .
|
|
- uses: actions/upload-artifact@v4
|
|
with:
|
|
name: admin-output
|
|
path: admin-output.tar.gz
|
|
|
|
deploy:
|
|
needs: build
|
|
runs-on: ubuntu-latest
|
|
environment: staging
|
|
steps:
|
|
# ... analog zu deploy-staging.yml:
|
|
# scp admin-output.tar.gz -> /srv/rebreak/apps/admin/.output-incoming.tar.gz
|
|
# ssh -> scripts/deploy-admin-from-artifact.sh
|
|
```
|
|
|
|
**Warum separater Workflow:** `paths`-Filter verhindert dass ein Backend-Push
|
|
auch die Admin-App rebuildet (und vice versa). Weniger GH-Actions-Minutes-Verbrauch.
|
|
|
|
### Option 2: Zusaetzlicher Job in `deploy-staging.yml`
|
|
|
|
Parallel-Job neben dem bestehenden `build`-Job. Einfacher aber kein `paths`-Filter moeglich
|
|
ohne komplizierte Logik -- jeder Push rebuildet alles.
|
|
|
|
**Entscheidung: Option 1 (eigener Workflow mit paths-Filter).**
|
|
|
|
Der Workflow-File wird in Phase 2 angelegt -- NICHT jetzt (Pipeline-Scope-Creep verhindern).
|
|
|
|
---
|
|
|
|
## Server-Script fuer Admin-Deploy
|
|
|
|
Analog zu `scripts/deploy-from-artifact.sh` -- ein `scripts/deploy-admin-from-artifact.sh`
|
|
das:
|
|
1. Admin-Artifact extrahiert nach `/srv/rebreak/apps/admin/.output-staging-new/`
|
|
2. Atomisches mv nach `.output-staging`
|
|
3. `pm2 restart rebreak-admin-staging`
|
|
|
|
KEIN Migration-Step (Admin-App hat keine eigene DB -- nutzt Backend-API).
|
|
|
|
---
|
|
|
|
## TODOs nach Phase 1
|
|
|
|
### Backend (rebreak-backend-Agent / Phase 3)
|
|
|
|
- [ ] `requireAdmin`-Middleware: JWT verifizieren + admin_users-Tabelle-Check
|
|
- [ ] Supabase-Migration: `admin_users`-Tabelle (`id uuid references auth.users`, `created_at`)
|
|
- [ ] GET /api/admin/verify-admin (prueft ob eingeloggter User Admin ist)
|
|
- [ ] GET /api/admin/users (paginierte User-Liste, NUR nickname + plan + created_at + last_seen)
|
|
- [ ] GET /api/admin/domains (Blocker-Domain-Approval-Queue)
|
|
- [ ] POST /api/admin/domains/:id/approve
|
|
- [ ] POST /api/admin/domains/:id/reject
|
|
- [ ] GET /api/admin/stats (aggregierte anonyme Metriken)
|
|
|
|
### Hans-Mueller / DSGVO (Phase 4)
|
|
|
|
- [ ] DSFA fuer Admin-Zugriff auf Nutzerdaten gemaess Art. 35 DSGVO
|
|
- [ ] Audit-Log-Design: `admin_audit_log`-Tabelle + Retention-Policy
|
|
- [ ] AVV-Template fuer externe Moderatoren (falls noetig)
|
|
- [ ] TOM (Technische+Organisatorische Massnahmen) fuer Admin-Zugang dokumentieren
|
|
- [ ] Loeschkonzept fuer Audit-Log-Eintraege (wie lange aufbewahren?)
|
|
|
|
### Backyard (Phase 2 Deploy)
|
|
|
|
- [ ] nginx-Config: `admin.staging.rebreak.org` -> Port 3017
|
|
- [ ] nginx-Config: `admin.rebreak.org` -> Port 3018
|
|
- [ ] Let's Encrypt-Cert fuer admin.staging.rebreak.org + admin.rebreak.org
|
|
- [ ] ecosystem.config.js: rebreak-admin-staging + rebreak-admin Service
|
|
- [ ] GH-Actions-Workflow: `deploy-admin-staging.yml` (paths-filtered)
|
|
- [ ] Server-Script: `scripts/deploy-admin-from-artifact.sh`
|
|
- [ ] GitHub-Environment: `staging` muss HETZNER_SSH_KEY/HOST/USER schon haben (von backend-deploy geerbt)
|