10 KiB
Handoff — CluPilot (Stand 2026-07-25)
Für: frische Claude-Code-Session
Art: Zustands-/Fortsetzungs-Handoff
Zuerst lesen: dieses Dokument komplett, dann git log --oneline -15.
0. TL;DR
CluPilot = Managed-Nextcloud-Cloud-Business (fertige, betreute Firmen-Cloud). Laravel-App auf einer lokalen Dev-VM. Gebaut & gepusht sind bereits: öffentliche Landingpage, helles Kundenportal (Login/2FA + Dashboard + 6 Reiter) und eine dunkle Admin-/Operator-Konsole (/admin, 6 Sektionen) — alles mit Chart.js, 48 Pest-Tests grün, jeder Commit Codex-(R15)-clean, überall 0 Konsolenfehler.
Nächster großer Schritt: die Provisionierungs-Engine v1.0 (aus Bestellung → laufende Nextcloud-Instanz). Design-Spec liegt beim Nutzer (nicht im Repo). Superpowers-Plugin (TDD) ist installiert.
1. Umgebung / Fakten
- VM: Debian, User
nexxo(uid/gid 1000), IP10.10.90.185, nur Docker (kein PHP/Composer/Node auf dem Host → alles im Container, R8). - Projekt:
/home/nexxo/clupilot(eigenes Git-Repo). - Remote:
https://git.bave.dev/boban/CluPilotCloud.git. Token liegt in.env(GIT_ACCESS_TOKEN, gitignored). Push: Token nur zur Push-Zeit inline injizieren, danachgit config branch.<b>.remote originund.git/configauf Token prüfen. Nie.env/Token committen. - Aktueller Branch:
feat/portal-design(alles hier, gepusht).mainexistiert lokal ohne Commits — Merge/PR steht aus. - Docker sudo-frei (nexxo in docker-Gruppe).
sudobräuchte Passwort → vermeiden.
2. Stack
Laravel 13.8, Livewire 3 (klassenbasiert, kein Volt — R2), Tailwind v3 (bewusst von v4 zurückgebaut, Nutzer-Entscheid), Vite, Laravel Fortify (Login + TOTP-2FA), Laravel Reverb, MariaDB 11.4, Redis, Chart.js 4, phpseclib, wire-elements/modal, Pest 4 (Tests). Fonts: IBM Plex self-hosted via @fontsource (R14, kein CDN).
3. Docker / Befehle (alles im Container, R8)
Helper clupilot (global via ~/.local/bin, + cp-* Aliases):
clupilot up | down | restart [svc] | rebuild | ps | logs [svc] | health
clupilot artisan … | composer … | npm … | tinker | migrate | shell
Roh, falls nötig (User 1000, HOME für Composer-/npm-Cache):
docker compose run --rm --no-deps -u 1000:1000 -e HOME=/tmp app <cmd>
docker compose exec -T -u 1000:1000 -e HOME=/tmp app php artisan …
Services: app (php-fpm+nginx+vite via supervisor — EIN Container), reverb, queue, scheduler, mariadb, redis. App auf :80, Vite HMR :5173 (VITE_HMR_HOST=10.10.90.185).
4. Was gebaut ist
Öffentlich
/→ Landingpage (resources/views/landing.blade.php) — Marketing, self-contained CSS/JS in@verbatim, System-Fonts, absichtlich außerhalb des App-Token-Systems. Login-Link →route('login')./legal/{impressum,datenschutz,agb,status}→ Platzhalter (legal.blade.php). ⚠️ Echter Rechtstext fehlt (Nutzer muss liefern — gesetzl. Pflicht vor Launch).
Kundenportal (hell) — layouts/portal.blade.php (Auth) + layouts/portal-app.blade.php (App-Shell)
- Auth:
/login,/two-factor-challenge(Fortify,views=false, Seiten als full-page Livewire — R1/R2). /dashboard+ Reiter/cloud /users /backups /invoices /support— je full-page Livewire (app/Livewire/*), Chart.js, Tabellen, lokalisiert DE/EN.
Admin-Konsole (dunkel) — layouts/admin.blade.php (class="theme-admin"), gate is_admin
/admin+/admin/{customers,instances,hosts,provisioning,revenue}(app/Livewire/Admin/*). Middleware['auth','admin'](EnsureAdmin).
Design-System (Kern!)
- Tokens als CSS-Variablen:
resources/css/portal-tokens.css(hell) +resources/css/admin-tokens.css(.theme-adminüberschreibt alle Tokens dunkel). Tailwind mappt Tokens → Utilities (tailwind.config.js). Nur Token-Utilities in Markup (R3), keine Hex. - Komponenten:
resources/views/components/ui/— button, input, checkbox, alert, card, badge, stat-tile, otp-input, progress-stepper, nav-item, icon (Lucide, keine Emoji im App-UI, R9), chart (Alpine-Island). - Chart-Island: PHP-Config-Array, Farben als
"token:accent"/"token:accent/0.12"(inresources/js/app.jszur Laufzeit aus CSS-Vars aufgelöst, liest vom Element → theme-aware).
5. Verifikations-Workflow (jede Änderung)
- Tests:
docker compose run --rm --no-deps -u 1000:1000 -e HOME=/tmp app ./vendor/bin/pest(aktuell 48 grün). - R12 Browser (0 Konsolenfehler): Prod-Assets bauen →
npm run build,supervisorctl stop vite+rm -f public/hot, dann Puppeteer:
Probe:docker run --rm --network host -v /pfad/probe.js:/home/pptruser/probe.js \ -w /home/pptruser ghcr.io/puppeteer/puppeteer:latest node probe.jspage.on('console'/'pageerror'/'requestfailed'), HTTP 200 + 0 Fehler pro Seite; Login-Flow via#email/#password+ submit. Danachdocker compose restart app(Vite/public/hotzurück). (Probe-Skripte liegen NICHT im Repo — bei Bedarf neu schreiben.) - R15 Codex-Review (Pflicht vor „fertig"):
Ergebnis pollen: neuestesexport CLAUDE_PLUGIN_ROOT=/home/nexxo/.claude/remote/plugins/e3a3a3c04ad124ae node "$CLAUDE_PLUGIN_ROOT/scripts/codex-companion.mjs" review "--scope working-tree --background"/tmp/codex-companion/*/jobs/review-*.log(awk '/Final output/{f=1}f')./codex:reviewistdisable-model-invocation→ Skript direkt aufrufen. Working-tree-Scope (nicht branch/base). Loop bis „no actionable regressions". Findings waren durchweg echt (a11y, i18n, Portabilität, Security) — ernst nehmen.
Dev-Logins (beide PW password): admin@clupilot.local (Admin, sieht /admin+/dashboard), kunde@clupilot.local (Kunde, /admin=403). Nach DB-Reset: clupilot artisan db:seed.
6. Gotchas (teuer gelernt — beachten!)
@js()wird NICHT kompiliert in@click/Attributen von Blade-Komponenten (<x-ui.button>) → landet literal, bricht Alpine. Lösung:@js()inx-dataeines plain Elements, Buttons referenzieren die Variable.- Test-Isolation:
env_file: .envin docker-compose wurde entfernt — es injizierte Dev-DB-Config als echte Env-Vars und überschrieb phpunit-force→ Tests liefen gegen die Dev-MariaDB und wischten sie. Laravel liest.envvon der Platte; nur Vite-Vars sind explizit gesetzt. Tests nutzen jetzt sqlite:memory:(phpunit.xmlmitforce="true"). - CSRF in Tests:
$this->post()sendet kein Token → 419. In Auth-Tests Session-Token setzen:withSession(['_token'=>'t'])->post(..., ['_token'=>'t', ...]). - Großes rohes HTML/CSS/JS in Blade →
@verbatim … @endverbatim(sonst zerlegt Blade@keyframes/${}/{{ }}). - AA-Kontrast:
#f97316(accent) fällt für weißen/kleinen Text durch AA → Buttons/Links nutzenaccent-active/accent-text(dunkleres Orange hell / helleres Orange dunkel).--accentnur Deko/Rahmen/Tints. - Locale: Fixture-Daten immer locale-aware (
Carbon::isoFormat,Number::format/currency), sonst mischt EN deutsche Formate — Codex flaggt das zuverlässig. - Vite reload:
docker compose restartliestenv_file/Config nicht neu →docker compose up -d app(recreate) wenn Env/Compose geändert.
7. Offene Punkte
| Prio | Punkt |
|---|---|
| Hoch | Echter Rechtstext (Impressum/Datenschutz/AGB) — Nutzer liefert; ich erfinde keine Rechtsangaben |
| Mittel | Merge feat/portal-design → main (Gitea-PR) |
| Mittel | Provisionierungs-Engine v1.0 (s. u.) |
| Niedrig | Multi-Domain-Split www. / app. / admin.clupilot.com (Nutzer legt Domains an) — aktuell pfad-basiert |
| Niedrig | v1.1-Auth: Passwort-Reset / „Passwort vergessen" (in Fortify bewusst deaktiviert) |
| Niedrig | Breiteres Komponenten-Kit (Modal-Nutzung, Tabs, Toast-Komponente, Skeleton, Tooltip …) falls gebraucht |
8. Nächster großer Block: Provisionierungs-Engine v1.0
Ziel: aus bezahlter Bestellung vollautomatisch eine laufende, gesicherte, überwachte Nextcloud-Instanz. Modell = DB-State-Machine + Tick-Orchestrator (kein Monolith-Job).
- Design-Spec liegt beim Nutzer (war als „Engine-v1.0-Handoff" vorhanden, ist NICHT im Repo). Vor Baubeginn anfordern.
- TDD (Superpowers
test-driven-developmentist ab dieser Session verfügbar). ProxmoxClient in Step-Tests mocken. - Grob: Migrations/Models (
customers, orders, hosts, instances, provisioning_runs, provisioning_step_events, run_resources, …) → Orchestrator-Kern (Step-Interface, StepResult advance/retry/fail, AdvanceRunJob, Per-Run-Lock, Timeout, Tick imscheduler) → ProxmoxClient (NEU, REST/UPID-Polling) → 15 idempotente Kunden-Pipeline-Steps → Stripe-Webhook (Idempotency-Key) → Livewire-Live-Fortschritt via Reverb. - Nicht anfassen/vermischen: Alt-Domäne existiert hier nicht (frischer Build) — Engine ist additiv. Panel-Reverb/Queue/Scheduler sind da und wiederverwendbar.
- Die Admin-
/admin/provisioning-Seite ist aktuell Fixture-View → später an echteprovisioning_runs/provisioning_step_eventsbinden (live via Reverb).
9. Regeln (Kurzfassung — verbindlich)
R1/R2 full-page klassenbasierte Livewire als Routen (kein Volt, kein Page-Controller) · R3 nur @theme/Token-Utilities, kein Hex · R4 keine Inline-Styles außer Progress-width · R5 destruktiv → wire-elements/modal · R6 Ordner-Map · R7 responsive 375/768/1280, Touch ≥44px · R8 alles im Container · R9 UI-Copy DE, keine Emoji (Status via Farbe/Dots/Pills) · R10 Design-System wiederverwenden · R11 URLs per UUID (nicht Integer-PK) · R12 Browser-verifiziert HTTP 200 + 0 Konsolenfehler · R13 Routen-Pfade/Namen englisch · R14 Fonts self-hosted · R15 Codex-Review clean vor „fertig" · R16 alles lokalisiert DE+EN (identische Keys) · R17 Blade-@php-Block-Regeln.
Volltext
rules.md/CLAUDE.mdhat der Nutzer (nicht im Repo). Bei Konflikt: STOP & fragen.
10. Session-Memory
Persistente Notizen unter ~/.claude/projects/-home-nexxo/memory/ (MEMORY.md + clupilot-project-state, clupilot-portal-progress, clupilot-r15-codex-review) — beim Start werden die relevanten automatisch eingespielt.