# 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)
