diff --git a/docs/superpowers/specs/2026-06-19-help-page-design.md b/docs/superpowers/specs/2026-06-19-help-page-design.md new file mode 100644 index 0000000..41f5e6c --- /dev/null +++ b/docs/superpowers/specs/2026-06-19-help-page-design.md @@ -0,0 +1,100 @@ +# Hilfe-Seite (In-Panel-Dokumentation) — Design + +**Goal:** Eine eigene, bilinguale Hilfe-Seite im Panel, die alle Einstellungen und Abläufe erklärt — inklusive einer generischen Schritt-für-Schritt-Anleitung, wie man Clusev hinter einem externen Reverse-Proxy ("Proxy Manager") betreibt. + +**Architecture:** Eine neue Full-Page-Livewire-Komponente unter `/help` mit linker Themen-Navigation (wie die Einstellungen). Kurze Chrome-Strings (Nav-Labels, Seitentitel) liegen in `lang/{de,en}/help.php`; der lange Fließtext liegt als Blade-Partials pro Sprache und wird nach aktiver Locale eingebunden. Keine neuen Tabellen, kein State außer dem gewählten Thema. + +**Tech Stack:** Laravel 13, Livewire 3 (class-based), Tailwind v4 `@theme`-Tokens, bestehende Blade-Komponenten (`x-panel`, `x-icon`, …). + +--- + +## 1. Platzierung & Navigation + +- **Komponente:** `App\Livewire\Help\Index` (Ordnerkarte R6), View `resources/views/livewire/help/index.blade.php`. +- **Route:** `Route::get('/help', Help\Index::class)->name('help')` in der onboarded-Gruppe von `routes/web.php` (gleiche Middleware-Gruppe wie Settings/System; hinter Auth + `EnsureSecurityOnboarded`). +- **Sidebar:** neuer Eintrag **„Hilfe"** unten (nach „Version"), Icon z. B. `help-circle` (Lucide, inline via `x-icon`). Label lokalisiert (`__('shell.nav_help')`). +- **Command-Palette:** Eintrag in `resources/views/components/command-palette.blade.php` (`$nav`) + Chord `g h` in `CMDK_GO` (analog zu den bestehenden `g `). +- **Layout:** `layouts.app`. +- **Route-Pfad englisch (R13)**, sichtbares Label deutsch/englisch (R9/R16). + +## 2. Aufbau der Seite + +- Öffentliche Property `public string $topic = 'overview';` — der gewählte Themenschlüssel. +- `mount()` akzeptiert optional ein `?topic`-Query/Param, validiert gegen die bekannte Themenliste, fällt sonst auf `overview` zurück (kein Lockout, kein 404 für unbekannte Keys → Default). +- Links: Themenliste (Buttons `wire:click="$set('topic', '')"`, aktives Thema hervorgehoben — gleiche Optik wie die Settings-Tabs in `settings/index.blade.php`). +- Rechts: `@include('livewire.help.content.'.app()->getLocale().'.'.$topic)` mit Existenzprüfung; fehlt das Locale-Partial, Fallback auf `de`. +- Responsive (R7): links Themen-Nav, die auf schmal über dem Inhalt als horizontale/aufklappbare Liste erscheint (gleiche Technik wie Settings-Tabs). + +### Themenliste (Reihenfolge) + +| Key | Label (de) | Inhalt-Kurzfassung | +|---|---|---| +| `overview` | Überblick / Erste Schritte | Was Clusev ist, Erst-Login (`admin@clusev.local` / `clusev`, Zwangswechsel), Navigation, Bare-IP vs. Domain. | +| `domain-tls` | Domain, TLS & Reverse-Proxy | Drei Modi (Bare-IP, eingebautes TLS/Let's Encrypt, externer Proxy); **Schritt-für-Schritt-Proxy-Anleitung**; `TRUSTED_PROXY_CIDR`; Neustart-Boundary. | +| `security` | Sicherheit & 2FA | TOTP, Security-Keys (YubiKey), Backup-Codes; **2FA-Zugangspfad-Hinweis** (s. u.); Passwort-Manager-Extension-Hinweis (Bitwarden/Kaspersky). | +| `updates` | Updates & Versionen | Auto-Check, „Jetzt aktualisieren"-Knopf, CLI `sudo ./update.sh`, was beim Update passiert (Build/Migrate). | +| `servers` | Server & SSH | Server hinzufügen, SSH-Credential-Vault, Hardening, SSH-Key-Provisioning. | +| `sessions` | Sitzungen & Mehrbenutzer | Aktive Sitzungen sehen/widerrufen, weitere Admins, Audit-Attribution. | +| `email` | E-Mail (SMTP) | SMTP für Passwort-Reset konfigurieren + Testmail. | +| `audit` | Audit-Log | Was protokolliert wird, Retention. | +| `recovery` | Konto-Wiederherstellung | Forgot-Password (E-Mail-Link / 2FA-Proof), Bare-IP-Recovery, `clusev:reset-admin`. | + +## 3. Inhalt zweisprachig + +- **Chrome (kurz):** `lang/de/help.php` + `lang/en/help.php` — Seitentitel, Eyebrow, Untertitel, die Themen-Labels (`topic_overview`, `topic_domain_tls`, …), `nav_help` in `shell.php`. +- **Lange Texte:** Blade-Partials je Sprache: + - `resources/views/livewire/help/content/de/.blade.php` + - `resources/views/livewire/help/content/en/.blade.php` + - Reines Markup mit Überschriften, Absätzen, Listen und Code-Blöcken (für Proxy-/CLI-Beispiele). **Nur `@theme`-Token-Utilities (R3), keine Inline-Styles (R4), kein Emoji (R9).** Native Tokens (`X-Forwarded-Proto`, `TRUSTED_PROXY_CIDR`, `sudo ./update.sh`) bleiben als `font-mono`. +- Locale-Auflösung im View über `app()->getLocale()`; Fallback `de`, wenn das EN-Partial (noch) fehlt — so bricht nichts, falls eine Sprache nachgezogen wird. + +## 4. Schlüssel-Inhalte (verbindlich) + +### 4a. Proxy-Anleitung (in `domain-tls`) — generisch, OHNE Produktnamen +1. **Im Panel:** System → Domain & TLS → Domain eintragen → TLS-Terminierung **„Externer Reverse-Proxy"** → speichern → **Stack neu starten** (Knopf). +2. **Im Proxy Manager:** neuen Host/Eintrag anlegen: + - Eingehende Domain = `panel.` + - Ziel/Backend = `http://:80` (**HTTP**, nicht HTTPS) + - **Host-Header durchreichen** und **`X-Forwarded-Proto: https` setzen** + - **WebSocket-Weiterleitung aktivieren** (für Realtime: Pfade `/app/*` und `/apps/*`) +3. **In der `.env`** (Clusev-Server): `TRUSTED_PROXY_CIDR` auf die Adresse/CIDR des Proxys setzen → korrekte `Secure`-Cookies + echte Besucher-IP im Audit-Log; danach `sudo ./update.sh` (oder Caddy-Neustart). +4. **Firewall:** den HTTP-Port (80) des Clusev-Servers so absichern, dass er **nur vom Proxy** erreichbar ist. +5. Hinweis: In diesem Modus stellt Clusev **kein** eigenes Zertifikat aus — TLS kommt vom Proxy. + +### 4b. 2FA-Zugangspfade (in `security`, Querverweis in `recovery`) +- **Security-Key (YubiKey)** funktioniert **nur über die HTTPS-Domain** (WebAuthn braucht einen sicheren Kontext + die Domain als rpId). Über die **Domain** wird die Security-Key-Anmeldung angeboten und funktioniert. +- Über den **Bare-IP-/HTTP-Recovery-Pfad** (`http://`) lässt sich **nicht** per Security-Key anmelden — dort muss ein **Backup-Code** verwendet werden. +- **TOTP / „Google Authenticator"** funktioniert **überall** — auch über Bare-IP/HTTP. Wer TOTP nutzt, braucht für den Bare-IP-Pfad **keinen** Backup-Code. +- Praxis-Empfehlung: Wer ausschließlich einen Security-Key nutzt, sollte die **Backup-Codes aufbewahren** (einziger Weg über den Bare-IP-Recovery-Pfad). Wer zusätzlich/alternativ TOTP hat, ist auf allen Pfaden abgedeckt. + +### 4c. Passwort-Manager-Extensions (in `security`) +- Browser-Erweiterungen wie Bitwarden/Kaspersky können die Security-Key-Registrierung abfangen („Passkey speichern"). Clusev signalisiert korrekt einen Hardware-Key (cross-platform, non-resident, `hints: ['security-key']`), aber Erweiterungen lassen sich seitenseitig nicht abschalten. Lösung: die Passkey-Übernahme der Erweiterung für die Seite deaktivieren, oder die Registrierung in einem Browser/Fenster ohne diese Erweiterung durchführen (z. B. privates Fenster). + +## 5. Komponenten-Logik (klein halten) + +`App\Livewire\Help\Index`: +- `public string $topic` +- `mount(?string $topic = null)`: validiert gegen `self::TOPICS` (Liste der Keys), Default `overview`. +- `render()`: übergibt die Themenliste (Keys + lokalisierte Labels) + den aktiven `$topic`; die View bindet das passende Locale-Partial ein. +- Keine Persistenz, keine externen Aufrufe, keine Berechnungen — reine Anzeige. + +## 6. Dateistruktur + +- Neu: `app/Livewire/Help/Index.php` +- Neu: `resources/views/livewire/help/index.blade.php` +- Neu: `resources/views/livewire/help/content/{de,en}/{overview,domain-tls,security,updates,servers,sessions,email,audit,recovery}.blade.php` (18 Partials) +- Neu: `lang/de/help.php`, `lang/en/help.php` +- Ändern: `routes/web.php` (Route), `resources/views/partials/sidebar.blade.php` (Nav-Eintrag), `lang/{de,en}/shell.php` (`nav_help`), `resources/views/components/command-palette.blade.php` + `CMDK_GO` (cmdk-Eintrag + `g h`). + +## 7. Test & Verifizierung + +- **Livewire-Test** (`tests/Feature/HelpPageTest.php`): Default-Thema = `overview`; `$set('topic', 'domain-tls')` rendert den Proxy-Abschnitt (assertSee eines bekannten Strings, z. B. `X-Forwarded-Proto`); unbekannter Topic-Key fällt auf `overview` zurück; Seite lädt für einen eingeloggten Nutzer (HTTP 200). +- **R12 Browser** (auf der Domain): `/help` lädt mit HTTP 200, keine Konsolenfehler, keine geleakten `{{ }}`/`group.key`-Tokens; Themenwechsel funktioniert; DE/EN-Umschalter zeigt den jeweiligen Inhalt; 375/768/1280. +- **R15 Codex** über den Diff. + +## 8. Bewusst NICHT enthalten (YAGNI) + +- Keine Volltextsuche in der Hilfe (Themen-Nav reicht). +- Keine kontextuellen „?"-Sprungmarken aus jeder Einstellung (kann später kommen). +- Keine aus Markdown gerenderte Doku-Engine — statische Blade-Partials genügen. +- Keine Versionierung/Changelog der Hilfe-Inhalte (das CHANGELOG deckt das ab). diff --git a/lang/de/system.php b/lang/de/system.php index 6beaca1..5d80672 100644 --- a/lang/de/system.php +++ b/lang/de/system.php @@ -70,7 +70,7 @@ return [ 'tls_mode_caddy' => 'Eingebautes TLS', 'tls_mode_caddy_desc' => 'Das Panel holt selbst ein Zertifikat für die Domain.', 'tls_mode_external' => 'Externer Reverse-Proxy', - 'tls_mode_external_desc' => 'Ein Proxy davor (z. B. Zoraxy) terminiert TLS; das Panel liefert HTTP.', + 'tls_mode_external_desc' => 'Ein vorgelagerter Proxy Manager terminiert TLS; das Panel liefert HTTP.', 'tls_external_hint' => 'Externer Proxy: setze TRUSTED_PROXY_CIDR auf die Adresse deines Proxys und schütze den HTTP-Port per Firewall, sodass er nur vom Proxy erreichbar ist. Das Panel holt dann kein Zertifikat.', 'change_tls_heading' => 'TLS-Modus ändern', 'change_tls_body_external' => 'Auf „Externer Reverse-Proxy" umstellen? Das Panel holt kein Zertifikat mehr; TLS muss der Proxy davor liefern. Gilt nach Neustart des Stacks.', diff --git a/lang/en/system.php b/lang/en/system.php index 33cc62e..a2712e4 100644 --- a/lang/en/system.php +++ b/lang/en/system.php @@ -70,7 +70,7 @@ return [ 'tls_mode_caddy' => 'Built-in TLS', 'tls_mode_caddy_desc' => 'The panel fetches its own certificate for the domain.', 'tls_mode_external' => 'External reverse proxy', - 'tls_mode_external_desc' => 'A proxy in front (e.g. Zoraxy) terminates TLS; the panel serves HTTP.', + 'tls_mode_external_desc' => 'An upstream proxy manager terminates TLS; the panel serves HTTP.', 'tls_external_hint' => 'External proxy: set TRUSTED_PROXY_CIDR to your proxy\'s address and firewall the HTTP port so it is only reachable from the upstream proxy. The panel will not fetch a certificate.', 'change_tls_heading' => 'Change TLS mode', 'change_tls_body_external' => 'Switch to "External reverse proxy"? The panel will no longer fetch a certificate; TLS must be provided by the proxy in front. Takes effect after a stack restart.',