# DOGFATHER UNIVERSE — Interner Bereich (Cloudflare Worker + D1)

Setzt das Anforderungsdokument **„Geschützter Zugang, Rollen und Benutzerverwaltung"**
(`Geschuetzter_Zugang_Rollen_Benutzerverwaltung.pdf`) vollständig um: Session-basierte Anmeldung,
verschlüsselte (aber für Dogi jederzeit einsehbare) Zugangscodes, frei konfigurierbare Rollen mit
granularen Rechten, individuelle Ausnahmen pro Person, Aktivitätsprotokoll, Login-Lockout.

Kosten: läuft im kostenlosen Cloudflare-Tier (Workers Free, D1 Free, KV Free reichen locker).

## ✅ Bereits deployed

- **Interner Worker:** https://dogfather-universe-postfach.dogfather1608.workers.dev (Health-Check: `/health`)
- **D1-Datenbank:** `dogfather-universe-db`
- Website (`postfach.html`, `zugaenge.html`, `assets/js/*.js`) zeigt bereits auf diese URL.

Die Schritte unten sind nur nötig, falls der Worker mal neu aufgesetzt werden muss.

## Architektur

```
src/worker.js              Router — verteilt Requests an die Handler unten
src/lib/crypto.js          SHA-256-Hashing, AES-256-GCM-Verschlüsselung, ID/Token-Erzeugung
src/lib/auth.js            Login-Prüfung, Sessions, Rate-Limiting/Lockout
src/lib/permissions.js     Vollständiger Permission-Katalog aus dem Anforderungsdokument
src/lib/audit.js           Aktivitätsprotokoll schreiben (nie mit Codes!)
src/lib/http.js            CORS/JSON-Helfer
src/routes/auth.js         Login/Logout
src/routes/users.js        Zugänge und Teammitglieder (inkl. Code-Funktionen, NUR Owner)
src/routes/roles.js        Rollen erstellen/umbenennen/löschen
src/routes/applications.js Bewerbungen: Status, Zuweisung, Notizen, Antwortentwürfe
src/routes/audit.js        Aktivitätsprotokoll auslesen
migrations/0001_init.sql   D1-Schema (users, roles, sessions, audit_log, applications, ...)
```

## Das Kernprinzip (aus dem Anforderungsdokument)

> Nur der Hauptadministrator sieht jederzeit alle vollständigen Zugangscodes und nur der
> Hauptadministrator darf sie erstellen, ändern, kopieren, zurücksetzen oder sperren.

Technisch umgesetzt:

- **Owner-Code** = das `POSTFACH_CODE`-Secret. Kein Datenbankeintrag, kann nicht gelöscht/verändert
  werden außer von Dogi selbst per `wrangler secret put`.
- **Team-Codes** werden **zweifach** gespeichert:
  - `code_hash` (SHA-256, nicht umkehrbar) — für den schnellen Login-Vergleich.
  - `code_enc` (AES-256-GCM, umkehrbar) — nur damit kann der Owner sich den Klartext-Code später
    wieder anzeigen lassen. Der Schlüssel dafür liegt ausschließlich im `ENCRYPTION_KEY`-Secret.
- **Code anzeigen** verlangt zusätzlich eine Re-Authentifizierung (Owner-Code erneut eingeben),
  bevor der Klartext entschlüsselt und zurückgegeben wird.
- Folgende Aktionen sind **fest an `isOwner === true` gebunden** und erscheinen nirgends als
  wählbare Berechtigung: Code anzeigen/kopieren/erstellen/ändern/zurücksetzen, den eigenen
  Owner-Zugang bearbeiten, die Owner-Rolle löschen/einschränken, jemanden zum Owner machen.

## Voraussetzungen

- Ein (kostenloser) Cloudflare-Account
- Node.js installiert (für `npx wrangler`)

## Deploy von Grund auf (falls je nötig)

Alles im Ordner `cloudflare-worker/` ausführen:

```bash
cd cloudflare-worker

# 1. Bei Cloudflare einloggen
npx wrangler login

# 2. D1-Datenbank anlegen
npx wrangler d1 create dogfather-universe-db
# -> "database_id" aus der Ausgabe in wrangler.toml eintragen

# 3. Schema anlegen
npx wrangler d1 execute dogfather-universe-db --remote --file=migrations/0001_init.sql

# 4. KV-Namespace anlegen (nur noch für den Live-Status-Schalter genutzt)
npx wrangler kv namespace create BEWERBUNGEN
# -> "id" aus der Ausgabe in wrangler.toml eintragen

# 5. Owner-Code festlegen (dein persönlicher Hauptadministrator-Zugang)
npx wrangler secret put POSTFACH_CODE

# 6. Verschlüsselungs-Schlüssel für Team-Codes festlegen (32 zufällige Bytes, base64)
#    z.B. erzeugen mit: node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
npx wrangler secret put ENCRYPTION_KEY

# 7. (Optional) Bewerbungen zusätzlich automatisch als Ticket bei
#    Ticketanizer anlegen (dort in Discord sichtbar) — siehe Abschnitt
#    "Bewerbungen als Ticketanizer-Tickets" unten. Ohne diesen Schritt
#    läuft alles wie gehabt, es fehlt nur das automatische Ticket.

# 8. Deployen
npx wrangler deploy
```

## Bewerbungen als Ticketanizer-Tickets

Jede eingehende Bewerbung wird zusätzlich zur Speicherung in der eigenen
Datenbank als **echtes Ticket** bei Ticketanizer angelegt — im passenden
Bereich (Modi/Scout/Creator/Kooperation), exakt wie auf der Website getrennt.
Das läuft über die offizielle Ticketanizer-Inbound-Ticket-API
(`POST https://api.ticketanizer.com/v1/tickets`), kein Discord-Bot-Code nötig.

### Einrichtung (einmalig, ca. 2 Minuten)

1. Im Ticketanizer-Dashboard: **API & Webhooks → API-Keys → Key erstellen**
   (Name z.B. "DOGFATHER UNIVERSE Homepage"). Den Key **sofort kopieren** —
   wird meist nur einmal im Klartext angezeigt.
2. Als Secret im Worker hinterlegen:

```bash
npx wrangler secret put TICKETANIZER_API_KEY
```

3. Danach einmal `npx wrangler deploy`.

Die Zuordnung Bewerbungsbereich → Ticketanizer-Panel ist fest in
`src/lib/ticketanizer.js` (`PANEL_IDS`) hinterlegt, abgelesen aus dem
Ticketanizer-Dashboard unter **Panels**:

| Bewerbung auf der Website | Ticketanizer-Panel | panel_id |
|---|---|---|
| Modi | Modi-Bewerbung | 4 |
| Kooperation | Kooperationsbewerbung | 5 |
| Scout | Scout-Bewerbung | 6 |
| Creator | Manager-Bewerbung | 7 |

Ändert sich ein Panel (neu angelegt, andere ID), einfach die Zahlen in
`PANEL_IDS` in `src/lib/ticketanizer.js` anpassen und neu deployen.

### Wichtig

- **Ticketanizer ist rein informativ, kein Ausfallpunkt.** Fehlt der API-Key
  oder ist Ticketanizer kurz nicht erreichbar, wird die Bewerbung trotzdem
  ganz normal in der eigenen Datenbank gespeichert — nur das Zusatz-Ticket
  entfällt dann.
- Der Key steht **nie** im Code oder in `wrangler.toml` (die ist eingecheckt),
  sondern ausschließlich verschlüsselt bei Cloudflare — genau wie
  `POSTFACH_CODE` und `ENCRYPTION_KEY`.
- Ändern/Ersetzen des Keys: `wrangler secret put TICKETANIZER_API_KEY` erneut
  ausführen, überschreibt den alten Wert.
- Es wird bewusst nur Schritt 1 der Ticketanizer-API genutzt (Ticket
  eröffnen). Das Nachladen von Staff-Antworten (Schritt 3, "pollen") ist
  hier nicht eingebaut, da eine Bewerbung ein einmaliger Vorgang ist und
  kein dauerhaft laufender Chat.
- **Wichtige Einschränkung (2026-08-01 festgestellt):** Über die Inbound-
  Ticket-API angelegte Tickets landen bei Ticketanizer nur in der
  **Web-Inbox** (eigene Weboberfläche, siehe Dashboard → "Inbox") und lösen
  lediglich einen Rollen-Ping im Log-Kanal aus — es entsteht **kein**
  echter, sichtbarer Ticket-Kanal in Discord (auch nicht nach manuellem
  "Übernehmen" in der Inbox). Deshalb zusätzlich der Discord-Webhook-Weg
  weiter unten, der die Bewerbung garantiert sichtbar direkt in den
  passenden Kanal postet.

## Bewerbungen direkt in Discord-Kanälen (Webhooks)

Zusätzlich zu Ticketanizer wird jede Bewerbung als formatierte Nachricht
(Embed) direkt per normalem Discord-Webhook in den passenden Kanal gepostet —
das ist der Weg, der garantiert sichtbar in Discord ankommt (siehe
Einschränkung oben).

### Kanal-Zuordnung (von Dogi aus Discord kopiert, 2026-08-01)

| Bewerbung auf der Website | Discord-Kanal | Secret |
|---|---|---|
| Modi | `#modi-bewerbung` | `DISCORD_WEBHOOK_MODI` |
| Kooperation | `#kooperations-bewerbung` | `DISCORD_WEBHOOK_KOOPERATION` |
| Scout **und** Creator | `#scout-bewerbung` (gemeinsam) | `DISCORD_WEBHOOK_SCOUT_MANAGER` |

### Einrichtung (pro Kanal ca. 1 Minute)

1. Im jeweiligen Discord-Kanal: Kanal bearbeiten → **Integrationen** →
   **Webhooks** → **Neuer Webhook** → **Webhook-URL kopieren**.
2. Als Secret hinterlegen:

```bash
npx wrangler secret put DISCORD_WEBHOOK_MODI
npx wrangler secret put DISCORD_WEBHOOK_KOOPERATION
npx wrangler secret put DISCORD_WEBHOOK_SCOUT_MANAGER
```

3. Danach einmal `npx wrangler deploy`.

Genau wie bei Ticketanizer: **rein informativ, kein Ausfallpunkt.** Fehlt
eine Webhook-URL oder ist Discord kurz nicht erreichbar, wird die Bewerbung
trotzdem ganz normal gespeichert. Die Zuordnung Typ → Webhook steht in
`src/lib/discord-webhooks.js` (`WEBHOOK_ENV_KEYS`).

## Wer den Owner-Code kennen darf

Nur du. Er ist dein Hauptadministrator-Zugang — sieht ausnahmslos alles, kann Zugänge/Rollen/Codes
verwalten. Ändern: `wrangler secret put POSTFACH_CODE` erneut ausführen.

## Zugänge und Teammitglieder verwalten

Auf `zugaenge.html` (verlinkt von `postfach.html`, wenn du als Owner eingeloggt bist):

- **Neue Zugänge erstellen** — Name, Benutzername, E-Mail, Rolle, Gültigkeit, Notiz. Code manuell
  festlegen oder automatisch sicher generieren lassen. Wird **einmalig** im Klartext angezeigt —
  danach nur noch über „Code anzeigen" (mit Re-Auth) abrufbar.
- **Rollen frei erstellen** — jedes einzelne Recht per Checkbox an-/abschaltbar (Bewerbungsbereiche,
  Bearbeitung, Antworten, Notizen, Teamverwaltung, Einstellungen). Die Code-Funktionen tauchen dort
  bewusst nirgends auf.
- **Individuelle Ausnahmen pro Person** — zusätzlich zur Rolle einzelne Rechte gezielt erlauben
  oder entziehen.
- **Delegierbare Teilfunktion:** Personen mit dem Recht „Neue Benutzer vorbereiten" können einen
  Entwurf anlegen (Name, Benutzername, Rolle) — der Zugang bleibt inaktiv, bis du ihn im Bereich
  „Zugänge" mit „Freigeben & Code erstellen" final aktivierst.
- **Pro Person:** Code anzeigen/kopieren/neu erstellen (nur Owner), Sperren/Entsperren, alle
  Sitzungen beenden (nur Owner), Rolle ändern, Rechte verwalten, vollständig löschen (nur Owner).

## Sicherheitsmaßnahmen

- Nach 5 falschen Codes pro Client 15 Minuten Sperre (`login_attempts`-Tabelle).
- Sessions: 30 Minuten Inaktivitäts-Timeout, 12 Stunden absolute Höchstdauer.
- Zugangscodes erscheinen **niemals** im Aktivitätsprotokoll, im Quelltext oder in Fehlermeldungen.
- CORS ist aktuell offen (`*`) — sobald die finale Domain feststeht, in `src/lib/http.js` →
  `corsHeaders()` auf die echte Domain einschränken.

## Wartung

- Bewerbungen/Zugänge/Rollen: alles über `zugaenge.html` bzw. `postfach.html` als Owner.
- Direktzugriff auf die Datenbank bei Bedarf:
  `npx wrangler d1 execute dogfather-universe-db --remote --command="SELECT ..."`
