docs(plan): implementation plan for the in-panel help page
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>feat/v1-foundation
parent
60295a0aea
commit
ca693219bf
|
|
@ -0,0 +1,459 @@
|
|||
# Hilfe-Seite Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Eine bilinguale In-Panel-Hilfe-Seite unter `/help` mit linker Themen-Navigation, die alle Einstellungen erklärt — inkl. generischer Reverse-Proxy-Anleitung und dem 2FA-Zugangspfad-Hinweis.
|
||||
|
||||
**Architecture:** Full-Page-Livewire-Komponente `App\Livewire\Help\Index` (App-Layout, hinter Auth + Onboarding). Kurze Strings in `lang/{de,en}/help.php`; lange Texte als Blade-Partials je Sprache unter `resources/views/livewire/help/content/{de,en}/<topic>.blade.php`, nach `app()->getLocale()` eingebunden (Fallback `de`). Kein State außer `$topic`.
|
||||
|
||||
**Tech Stack:** Laravel 13, Livewire 3 (class-based), Tailwind v4 `@theme`-Tokens, bestehende Blade-Komponenten (`x-nav-item`, `x-panel`, `x-icon`).
|
||||
|
||||
**Konventionen (hart):** Route-Pfad englisch (R13); sichtbare Texte DE+EN, kein Emoji (R9/R16); nur `@theme`-Token-Utilities, keine Inline-Styles (R4/R3); Container-Tooling (R8); Browser-Verify (R12) + Codex (R15) vor „fertig".
|
||||
|
||||
Spec: `docs/superpowers/specs/2026-06-19-help-page-design.md` (Abschnitt 4 = verbindliche Inhalte).
|
||||
|
||||
---
|
||||
|
||||
## Task 1: Gerüst + Thema „overview" + Tests (TDD)
|
||||
|
||||
**Files:**
|
||||
- Create: `app/Livewire/Help/Index.php`
|
||||
- Create: `resources/views/livewire/help/index.blade.php`
|
||||
- Create: `resources/views/livewire/help/content/de/overview.blade.php`
|
||||
- Create: `resources/views/livewire/help/content/en/overview.blade.php`
|
||||
- Create: `lang/de/help.php`, `lang/en/help.php`
|
||||
- Modify: `routes/web.php` (Route + Import)
|
||||
- Modify: `resources/views/components/sidebar.blade.php` (Nav-Eintrag nach Zeile 36)
|
||||
- Modify: `lang/de/shell.php`, `lang/en/shell.php` (`nav_help`)
|
||||
- Modify: `resources/views/components/command-palette.blade.php` (chords + `$nav`)
|
||||
- Modify: `resources/js/app.js` (`CMDK_GO` + `h`)
|
||||
- Test: `tests/Feature/HelpPageTest.php`
|
||||
|
||||
- [ ] **Step 1: Failing test**
|
||||
|
||||
`tests/Feature/HelpPageTest.php`:
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Livewire\Help\Index;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Livewire\Livewire;
|
||||
use Tests\TestCase;
|
||||
|
||||
class HelpPageTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
private function actAsAdmin(): void
|
||||
{
|
||||
$this->actingAs(User::factory()->create(['must_change_password' => false]));
|
||||
}
|
||||
|
||||
public function test_help_route_loads_for_an_authenticated_admin(): void
|
||||
{
|
||||
$this->actAsAdmin();
|
||||
$this->get('/help')->assertOk();
|
||||
}
|
||||
|
||||
public function test_default_topic_is_overview(): void
|
||||
{
|
||||
$this->actAsAdmin();
|
||||
Livewire::test(Index::class)
|
||||
->assertSet('topic', 'overview')
|
||||
->assertSee(__('help.topic_overview'));
|
||||
}
|
||||
|
||||
public function test_unknown_topic_falls_back_to_overview(): void
|
||||
{
|
||||
$this->actAsAdmin();
|
||||
Livewire::test(Index::class, ['topic' => 'bogus'])->assertSet('topic', 'overview');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run — expect FAIL**
|
||||
|
||||
Run: `docker compose exec -T -u app app php artisan test --filter=HelpPageTest`
|
||||
Expected: FAIL (class `App\Livewire\Help\Index` not found).
|
||||
|
||||
- [ ] **Step 3: Component**
|
||||
|
||||
`app/Livewire/Help/Index.php`:
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Help;
|
||||
|
||||
use Livewire\Attributes\Layout;
|
||||
use Livewire\Component;
|
||||
|
||||
/**
|
||||
* In-panel help/documentation. Pure display: a left topic nav (like Settings) and a per-locale
|
||||
* content partial. No persistence, no external calls. Topic keys map to content partials at
|
||||
* resources/views/livewire/help/content/{de,en}/<topic>.blade.php.
|
||||
*/
|
||||
#[Layout('layouts.app')]
|
||||
class Index extends Component
|
||||
{
|
||||
/** Help topics in display order. Keys are also the content-partial filenames. */
|
||||
private const TOPICS = [
|
||||
'overview', 'domain-tls', 'security', 'updates',
|
||||
'servers', 'sessions', 'email', 'audit', 'recovery',
|
||||
];
|
||||
|
||||
public string $topic = 'overview';
|
||||
|
||||
public function mount(?string $topic = null): void
|
||||
{
|
||||
if ($topic !== null && in_array($topic, self::TOPICS, true)) {
|
||||
$this->topic = $topic;
|
||||
}
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$labels = [
|
||||
'overview' => __('help.topic_overview'),
|
||||
'domain-tls' => __('help.topic_domain_tls'),
|
||||
'security' => __('help.topic_security'),
|
||||
'updates' => __('help.topic_updates'),
|
||||
'servers' => __('help.topic_servers'),
|
||||
'sessions' => __('help.topic_sessions'),
|
||||
'email' => __('help.topic_email'),
|
||||
'audit' => __('help.topic_audit'),
|
||||
'recovery' => __('help.topic_recovery'),
|
||||
];
|
||||
|
||||
$topics = array_map(fn (string $key): array => ['key' => $key, 'label' => $labels[$key]], self::TOPICS);
|
||||
|
||||
return view('livewire.help.index', ['topics' => $topics])->title(__('help.title'));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Chrome lang files**
|
||||
|
||||
`lang/de/help.php`:
|
||||
```php
|
||||
<?php
|
||||
|
||||
return [
|
||||
'title' => 'Hilfe — Clusev',
|
||||
'eyebrow' => 'Hilfe',
|
||||
'heading' => 'Hilfe & Dokumentation',
|
||||
'subtitle' => 'Einstellungen und Abläufe erklärt.',
|
||||
|
||||
'topic_overview' => 'Überblick',
|
||||
'topic_domain_tls' => 'Domain, TLS & Reverse-Proxy',
|
||||
'topic_security' => 'Sicherheit & 2FA',
|
||||
'topic_updates' => 'Updates & Versionen',
|
||||
'topic_servers' => 'Server & SSH',
|
||||
'topic_sessions' => 'Sitzungen & Benutzer',
|
||||
'topic_email' => 'E-Mail (SMTP)',
|
||||
'topic_audit' => 'Audit-Log',
|
||||
'topic_recovery' => 'Konto-Wiederherstellung',
|
||||
];
|
||||
```
|
||||
|
||||
`lang/en/help.php` (same keys, English values):
|
||||
```php
|
||||
<?php
|
||||
|
||||
return [
|
||||
'title' => 'Help — Clusev',
|
||||
'eyebrow' => 'Help',
|
||||
'heading' => 'Help & documentation',
|
||||
'subtitle' => 'Settings and procedures explained.',
|
||||
|
||||
'topic_overview' => 'Overview',
|
||||
'topic_domain_tls' => 'Domain, TLS & reverse proxy',
|
||||
'topic_security' => 'Security & 2FA',
|
||||
'topic_updates' => 'Updates & versions',
|
||||
'topic_servers' => 'Servers & SSH',
|
||||
'topic_sessions' => 'Sessions & users',
|
||||
'topic_email' => 'Email (SMTP)',
|
||||
'topic_audit' => 'Audit log',
|
||||
'topic_recovery' => 'Account recovery',
|
||||
];
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Index view**
|
||||
|
||||
`resources/views/livewire/help/index.blade.php`:
|
||||
```blade
|
||||
@php
|
||||
$locale = in_array(app()->getLocale(), ['de', 'en'], true) ? app()->getLocale() : 'de';
|
||||
$partial = 'livewire.help.content.'.$locale.'.'.$topic;
|
||||
if (! view()->exists($partial)) {
|
||||
$partial = 'livewire.help.content.de.'.$topic;
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div class="space-y-5">
|
||||
<div>
|
||||
<p class="font-mono text-[11px] uppercase tracking-[0.2em] text-accent-text">{{ __('help.eyebrow') }}</p>
|
||||
<h2 class="mt-0.5 font-display text-xl font-semibold text-ink sm:text-2xl">{{ __('help.heading') }}</h2>
|
||||
<p class="mt-1 font-mono text-xs text-ink-3">{{ __('help.subtitle') }}</p>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-5 lg:grid-cols-[15rem_1fr]">
|
||||
{{-- Topic nav --}}
|
||||
<nav class="flex flex-row flex-wrap gap-1 lg:flex-col">
|
||||
@foreach ($topics as $t)
|
||||
<button type="button" wire:click="$set('topic', '{{ $t['key'] }}')" @class([
|
||||
'rounded-md px-3 py-2 text-left font-mono text-[12px] transition-colors',
|
||||
'bg-accent/10 text-accent-text shadow-[inset_2px_0_0_var(--color-accent)]' => $topic === $t['key'],
|
||||
'text-ink-2 hover:bg-raised hover:text-ink' => $topic !== $t['key'],
|
||||
])>{{ $t['label'] }}</button>
|
||||
@endforeach
|
||||
</nav>
|
||||
|
||||
{{-- Content --}}
|
||||
<article class="min-w-0 space-y-4 rounded-xl border border-line bg-surface p-5 shadow-panel">
|
||||
@include($partial)
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
- [ ] **Step 6: overview partials (de + en)**
|
||||
|
||||
`resources/views/livewire/help/content/de/overview.blade.php` — short, real prose. Cover: was Clusev ist (Fleet-Control über SSH); Erst-Login `admin@clusev.local` / `clusev` (sofort ändern, Zwangswechsel); Bare-IP vs. Domain; wo man was findet (Server, Dienste, Dateien, Audit, Einstellungen, System, Version, Hilfe). Use `<h3 class="font-display text-base font-semibold text-ink">`, `<p class="text-sm leading-relaxed text-ink-2">`, `<code class="font-mono text-accent-text">`. NO emoji, NO inline styles.
|
||||
|
||||
`resources/views/livewire/help/content/en/overview.blade.php` — same content, English.
|
||||
|
||||
- [ ] **Step 7: Route + import**
|
||||
|
||||
`routes/web.php`: add import near the other `use App\Livewire\...;` lines:
|
||||
```php
|
||||
use App\Livewire\Help;
|
||||
```
|
||||
Inside the `EnsureSecurityOnboarded` group, after the `/versions` route:
|
||||
```php
|
||||
Route::get('/help', Help\Index::class)->name('help');
|
||||
```
|
||||
|
||||
- [ ] **Step 8: Sidebar nav**
|
||||
|
||||
`resources/views/components/sidebar.blade.php` — after the `/versions` `x-nav-item` (line 36), add:
|
||||
```blade
|
||||
<x-nav-item icon="help-circle" href="/help" :active="request()->is('help*')">{{ __('shell.nav_help') }}</x-nav-item>
|
||||
```
|
||||
If `x-icon` has no `help-circle` case, add it to `resources/views/components/icon.blade.php` with the Lucide path:
|
||||
```blade
|
||||
@case('help-circle')
|
||||
<circle cx="12" cy="12" r="10" /><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" /><path d="M12 17h.01" />
|
||||
@break
|
||||
```
|
||||
(Match the existing `@case`/`@break` structure + the component's stroke attributes.)
|
||||
|
||||
- [ ] **Step 9: shell lang + cmdk**
|
||||
|
||||
`lang/de/shell.php`: add `'nav_help' => 'Hilfe',`. `lang/en/shell.php`: add `'nav_help' => 'Help',`.
|
||||
|
||||
`resources/views/components/command-palette.blade.php`:
|
||||
- In `$chords` (after `['g v', __('shell.nav_versions')],`): add `['g h', __('shell.nav_help')],`
|
||||
- In `$nav` (after the versions entry): add `['label' => __('shell.nav_help'), 'href' => '/help', 'hint' => 'g h'],`
|
||||
|
||||
`resources/js/app.js` line ~256 `CMDK_GO`: add `h: '/help'` to the map:
|
||||
```js
|
||||
const CMDK_GO = { d: '/', s: '/servers', i: '/services', f: '/files', l: '/audit', e: '/settings', y: '/system', v: '/versions', h: '/help' };
|
||||
```
|
||||
|
||||
- [ ] **Step 10: Run — expect PASS**
|
||||
|
||||
Run: `docker compose exec -T -u app app php artisan test --filter=HelpPageTest`
|
||||
Expected: PASS (3 tests).
|
||||
|
||||
- [ ] **Step 11: Pint + commit**
|
||||
|
||||
```bash
|
||||
docker compose exec -T -u app app ./vendor/bin/pint app/Livewire/Help/Index.php tests/Feature/HelpPageTest.php
|
||||
git add app/Livewire/Help routes/web.php resources/views/livewire/help resources/views/components/sidebar.blade.php resources/views/components/command-palette.blade.php resources/views/components/icon.blade.php resources/js/app.js lang/de/help.php lang/en/help.php lang/de/shell.php lang/en/shell.php tests/Feature/HelpPageTest.php
|
||||
git commit -m "feat(help): in-panel help page scaffold + overview topic"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 2: Thema „domain-tls" (Reverse-Proxy-Anleitung)
|
||||
|
||||
**Files:**
|
||||
- Create: `resources/views/livewire/help/content/de/domain-tls.blade.php`
|
||||
- Create: `resources/views/livewire/help/content/en/domain-tls.blade.php`
|
||||
- Test: `tests/Feature/HelpPageTest.php` (add a test)
|
||||
|
||||
- [ ] **Step 1: Failing test** — add to `HelpPageTest`:
|
||||
```php
|
||||
public function test_domain_tls_topic_has_the_reverse_proxy_guide(): void
|
||||
{
|
||||
$this->actAsAdmin();
|
||||
Livewire::test(Index::class)
|
||||
->set('topic', 'domain-tls')
|
||||
->assertSee('X-Forwarded-Proto')
|
||||
->assertSee('TRUSTED_PROXY_CIDR')
|
||||
->assertDontSee('Zoraxy'); // generic — no product names
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run — expect FAIL** (`view [livewire.help.content.de.domain-tls] not found`).
|
||||
Run: `docker compose exec -T -u app app php artisan test --filter=HelpPageTest`
|
||||
|
||||
- [ ] **Step 3: Write the partials (de + en)**
|
||||
|
||||
Content per spec §4a — three modes (Bare-IP / eingebautes TLS / externer Proxy) then the **generic step-by-step** proxy guide. MUST contain the literal tokens `X-Forwarded-Proto` and `TRUSTED_PROXY_CIDR`; MUST NOT contain `Zoraxy` (use „Proxy Manager"). Steps verbatim:
|
||||
1. Im Panel: System → Domain → TLS „Externer Reverse-Proxy" → speichern → Stack neu starten.
|
||||
2. Im Proxy Manager: Domain = `panel.<domain>`; Ziel = `http://<Clusev-IP>:80` (HTTP); Host-Header + `X-Forwarded-Proto: https` weiterreichen; WebSocket aktivieren (`/app/*`, `/apps/*`).
|
||||
3. `.env`: `TRUSTED_PROXY_CIDR` = Proxy-Adresse → Secure-Cookie + echte IP im Audit; danach `sudo ./update.sh`.
|
||||
4. Firewall: HTTP-Port nur vom Proxy erreichbar.
|
||||
5. In diesem Modus stellt Clusev kein Zertifikat aus.
|
||||
Use `<h3>`, `<p>`, `<ol class="...">`/`<ul>`, and a `<pre class="overflow-x-auto rounded-md border border-line bg-inset p-3 font-mono text-[11px] text-ink-2">` for the proxy fields/CLI. @theme tokens only.
|
||||
|
||||
- [ ] **Step 4: Run — expect PASS.** `docker compose exec -T -u app app php artisan test --filter=HelpPageTest`
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
```bash
|
||||
git add resources/views/livewire/help/content tests/Feature/HelpPageTest.php
|
||||
git commit -m "feat(help): domain-tls topic with the reverse-proxy setup guide"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 3: Thema „security" (2FA-Zugangspfade + Extensions)
|
||||
|
||||
**Files:**
|
||||
- Create: `resources/views/livewire/help/content/de/security.blade.php`
|
||||
- Create: `resources/views/livewire/help/content/en/security.blade.php`
|
||||
- Test: `tests/Feature/HelpPageTest.php`
|
||||
|
||||
- [ ] **Step 1: Failing test** — add:
|
||||
```php
|
||||
public function test_security_topic_explains_the_2fa_access_paths(): void
|
||||
{
|
||||
$this->actAsAdmin();
|
||||
Livewire::test(Index::class)
|
||||
->set('topic', 'security')
|
||||
->assertSee('Backup-Code')
|
||||
->assertSee('TOTP');
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run — expect FAIL.**
|
||||
|
||||
- [ ] **Step 3: Write the partials (de + en)** — content per spec §4b + §4c:
|
||||
- TOTP / Security-Keys (YubiKey) / Backup-Codes erklären.
|
||||
- **Security-Key nur über die HTTPS-Domain** (WebAuthn braucht sicheren Kontext + Domain als rpId); über **Bare-IP/HTTP** kein Security-Key → **Backup-Code** nötig.
|
||||
- **TOTP funktioniert überall** (auch Bare-IP/HTTP), braucht keinen Backup-Code für den IP-Pfad.
|
||||
- Empfehlung: reiner Security-Key-Nutzer → Backup-Codes aufbewahren.
|
||||
- Passwort-Manager-Extensions (Bitwarden/Kaspersky) können die Registrierung abfangen → in der Extension Passkey-Übernahme deaktivieren oder ohne Extension registrieren (privates Fenster).
|
||||
Must contain literal `Backup-Code` and `TOTP`.
|
||||
|
||||
- [ ] **Step 4: Run — expect PASS.**
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
```bash
|
||||
git add resources/views/livewire/help/content tests/Feature/HelpPageTest.php
|
||||
git commit -m "feat(help): security topic — 2FA access paths + extension note"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 4: Restliche Themen (updates, servers, sessions, email, audit, recovery)
|
||||
|
||||
**Files:**
|
||||
- Create: `resources/views/livewire/help/content/{de,en}/{updates,servers,sessions,email,audit,recovery}.blade.php` (12 Partials)
|
||||
- Test: `tests/Feature/HelpPageTest.php`
|
||||
|
||||
- [ ] **Step 1: Failing test** — assert every topic renders without falling back (a marker string per topic). Add:
|
||||
```php
|
||||
/** @dataProvider topicProvider */
|
||||
public function test_each_topic_renders_its_own_partial(string $topic, string $marker): void
|
||||
{
|
||||
$this->actAsAdmin();
|
||||
Livewire::test(Index::class)->set('topic', $topic)->assertSee($marker);
|
||||
}
|
||||
|
||||
public static function topicProvider(): array
|
||||
{
|
||||
return [
|
||||
['updates', 'update.sh'],
|
||||
['servers', 'SSH'],
|
||||
['sessions', 'Sitzung'],
|
||||
['email', 'SMTP'],
|
||||
['audit', 'Audit'],
|
||||
['recovery', 'clusev:reset-admin'],
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run — expect FAIL** (missing partials).
|
||||
|
||||
- [ ] **Step 3: Write the 12 partials (de + en)** — each short + accurate:
|
||||
- `updates`: Auto-Check, „Jetzt aktualisieren"-Knopf, CLI `sudo ./update.sh`, Build+Migrate beim Update. (marker `update.sh`)
|
||||
- `servers`: Server hinzufügen, SSH-Credential-Vault, Hardening, SSH-Key-Provisioning. (marker `SSH`)
|
||||
- `sessions`: aktive Sitzungen sehen/widerrufen, weitere Admins, Audit-Attribution. (marker `Sitzung`/`Session` — DE uses `Sitzung`)
|
||||
- `email`: SMTP konfigurieren (Settings → E-Mail) + Testmail. (marker `SMTP`)
|
||||
- `audit`: was protokolliert wird + Retention. (marker `Audit`)
|
||||
- `recovery`: Forgot-Password (E-Mail-Link / 2FA-Proof), Bare-IP-Recovery, `clusev:reset-admin`. (marker `clusev:reset-admin`)
|
||||
Same markup conventions (h3/p/ul/pre), @theme tokens, no emoji. EN partials mirror DE (the marker strings are language-neutral tokens, so the EN test passes too).
|
||||
|
||||
- [ ] **Step 4: Run — expect PASS** (full HelpPageTest green).
|
||||
|
||||
- [ ] **Step 5: Run full suite + Pint**
|
||||
```bash
|
||||
docker compose exec -T -u app app ./vendor/bin/pint
|
||||
docker compose exec -T -u app app php artisan test
|
||||
```
|
||||
Expected: all green.
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
```bash
|
||||
git add resources/views/livewire/help/content tests/Feature/HelpPageTest.php
|
||||
git commit -m "feat(help): remaining topics (updates, servers, sessions, email, audit, recovery)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 5: Browser-Verify (R12), Codex (R15), Release
|
||||
|
||||
**Files:** Modify `config/clusev.php` (version), `CHANGELOG.md`.
|
||||
|
||||
- [ ] **Step 1: R12 browser-verify on the domain** — temp admin (create + delete after, do not touch the real account), headless Chrome via the puppeteer docker image against `https://panel.test.bave.dev/help`:
|
||||
- HTTP 200, no console errors, no leaked `{{ }}`/`help.`/`shell.` tokens.
|
||||
- Click two topic-nav buttons (`domain-tls`, `security`) → content swaps.
|
||||
- Toggle DE/EN → content language changes (the partial locale switches).
|
||||
- Check at 375 / 768 / 1280 widths.
|
||||
|
||||
- [ ] **Step 2: Codex review** — run `/codex:review` over the diff; fix until no errors / no security issues.
|
||||
|
||||
- [ ] **Step 3: Version + CHANGELOG** — bump `config/clusev.php` version; add a `### Hinzugefügt` entry: „In-Panel-Hilfe-Seite (`/help`) mit Themen-Navigation; erklärt alle Einstellungen, die Reverse-Proxy-Einrichtung und die 2FA-Zugangspfade (Security-Key nur über HTTPS-Domain, Backup-Code über Bare-IP, TOTP überall). Zoraxy-Produktname aus dem TLS-Hinweis entfernt."
|
||||
|
||||
- [ ] **Step 4: Commit, tag, push** (token from `/home/nexxo/.env.gitea`, output sanitized):
|
||||
```bash
|
||||
git add config/clusev.php CHANGELOG.md
|
||||
git commit -m "chore(release): vX.Y.Z — in-panel help page"
|
||||
git tag -a vX.Y.Z -m "Clusev vX.Y.Z — Hilfe-Seite"
|
||||
TOKEN="$(grep -E '^GIT_ACCESS_TOKEN=' /home/nexxo/.env.gitea | cut -d= -f2- | tr -d '\r\n')"
|
||||
git push "https://boban:${TOKEN}@git.bave.dev/boban/clusev.git" feat/v1-foundation --follow-tags 2>&1 | sed -E 's#https://[^@]*@#https://<redacted>@#g; s/[0-9a-f]{40,}/<redacted>/g'
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Deploy to VM + final verify** — `sudo ./update.sh` on the VM; confirm `/help` renders over the domain (R12 spot-check).
|
||||
|
||||
---
|
||||
|
||||
## Self-Review (Plan vs. Spec)
|
||||
|
||||
- **Spec §1 Platzierung** → Task 1 (route, sidebar, cmdk, layout). ✓
|
||||
- **Spec §2 Aufbau** → Task 1 (component `$topic`, index view, topic nav). ✓
|
||||
- **Spec §3 zweisprachig** → Task 1 (lang chrome + per-locale partials + fallback). ✓
|
||||
- **Spec §4a Proxy-Anleitung** → Task 2 (must-have tokens, no product name). ✓
|
||||
- **Spec §4b 2FA-Pfade** → Task 3 (Backup-Code/TOTP markers). ✓
|
||||
- **Spec §4c Extensions** → Task 3. ✓
|
||||
- **Spec §5 Logik klein** → Task 1 component (pure display). ✓
|
||||
- **Spec §6 Dateistruktur** → all 18 partials across Tasks 1–4. ✓
|
||||
- **Spec §7 Test/Verify** → Tasks 1–4 (Livewire tests) + Task 5 (R12/Codex/Release). ✓
|
||||
- **Spec §8 YAGNI** → no search/markdown engine; static partials only. ✓
|
||||
|
||||
Type/name consistency: topic keys identical in `TOPICS`, labels map, partial filenames, and tests (`overview`, `domain-tls`, `security`, `updates`, `servers`, `sessions`, `email`, `audit`, `recovery`). `$set('topic', …)` matches the public `$topic` prop.
|
||||
Loading…
Reference in New Issue