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>
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
# 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:
-
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)
-
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.
-
Admin-Zugangs-Liste:
admin_users-Tabelle in Supabase darf nur von User (Chahine) befuellt werden. Kein Self-Signup, kein automatisches Promoten. -
Data-Processing-Agreement: Wenn externe Personen Admin-Zugang bekommen (z.B. Moderatoren), braucht es einen AVV. Aktuell: nur interner Zugang (Chahine).
-
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:
# /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: 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)
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:
- Admin-Artifact extrahiert nach
/srv/rebreak/apps/admin/.output-staging-new/ - Atomisches mv nach
.output-staging 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:
stagingmuss HETZNER_SSH_KEY/HOST/USER schon haben (von backend-deploy geerbt)