Commit Graph

8 Commits (aa7a7b1db817f49a352e5e21b2fc5b1e73c23494)

Author SHA1 Message Date
boban aa7a7b1db8 fix(ux): editor click bug (@js→index), self-hosted fonts, split-brand auth, R14
- Fix Alpine "Invalid or unexpected token" on file/service/key actions: @js()
  does NOT compile inside a Blade component (x-btn) attribute, so a literal
  @js(...) reached the DOM and Alpine choked on it. Switched all 10 wire:click
  arguments to loop-index lookups (files: open/go/download/edit/confirmDelete;
  services: confirm; keys: confirmKeyRemoval) — the component resolves the value
  server-side by index. {{ $loop->index }} compiles in component attributes; @js
  does not.
- Self-host fonts (R14): Chakra Petch / Space Grotesk / JetBrains Mono as local
  .woff2 in resources/fonts/, declared via @font-face in app.css, Vite-bundled
  with relative urls so they resolve in dev AND the prod build. No Google Fonts
  / CDN link or @import. New rule R14 added to rules.md + CLAUDE.md.
- Auth redesign (split-brand, per reference): new auth layout with a brand panel
  (faux terminal + glow mesh, hidden below lg) + redesigned login,
  two-factor-challenge, two-factor-setup, password-change forms.
- x-btn: add size="lg" (h-11, >=44px touch target) for the full-width auth CTA;
  one shared button component, no bespoke styles.
- Remove unused Laravel welcome.blade.php (was full of raw hex — R3 cleanup).

Verified (R12): /login 200 / 0 console errors, fonts load (Chakra+Grotesk+JB),
brand panel visible @1440; file-editor REAL click -> modal opens, textarea
renders, 0 Alpine errors; editor load/save roundtrip (/etc/hostname='debian');
all 7 routes 200 / 0 console errors. Full rules audit R1-R14 clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 01:16:31 +02:00
boban f418ab35ab feat(ux): real metrics+context, working file manager, settings redesign, EN routes
Full feedback round — all browser-verified (R12: 7 routes 200, zero console errors)
and functionally tested live against the target host.

- Metrics are real AND honest: poller now stores cpu/mem/DISK/LOAD history (all
  four sparklines real, not synthetic); KPIs show absolute context (Memory
  "1,9 / 7,8 GB", Disk "13 / 80 GB", Load "4 Kerne"); tiles are status-coloured
  and Load is rated against core count (so "yellow" actually means load≈0.7-1.0×
  cores), not a fixed warning tint.
- File manager is functional: Hochladen (Livewire upload -> SFTP put), Download
  (SFTP get -> streamed), and view/edit (new FileEditor modal: SFTP get, binary/
  size guards, SFTP put on save + audit). File names + Bearbeiten open the editor.
- Settings redesigned (/frontend-design): account identity header + section nav
  (Profil / Sicherheit) instead of flat stacked panels.
- Routes are English (R13): /einstellungen -> /settings; rule added to rules.md +
  CLAUDE.md. Dummy data removed: the topbar "Flotte online / Uptime 42d" + dead
  bell button are gone, replaced by a real "<online>/<total> online" pill.

New: app/Livewire/Modals/FileEditor + view; FleetService get/read/write/upload +
Sftp putFromFile/size.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 00:32:05 +02:00
boban 5479896bf3 fix(modal)+ux: panel above backdrop, one <x-btn> style, SSH credential form
The modal panel rendered *behind* its own backdrop (it "appeared then vanished"):
Tailwind v4 no longer turns `transform` into a stacking context, so the static
panel sat under the fixed/blurred backdrop. Fixed with `relative z-10` on the
panel (interactively verified by force-opening the modal).

- New <x-btn> component: one compact, consistent style (h-8 text-xs) with
  variants (primary/accent/secondary/danger/ghost/ghost-danger). Replaced every
  ad-hoc action button — modals, service start/stop/restart, file row actions +
  upload, server-details (Zugang/Dateien/key add+remove). No more oversized,
  inconsistent buttons.
- New EditCredential form modal + "Zugang" button on Server-Details: deposit or
  update a server's SSH login (e.g. root) — this is where the privileged
  credential for systemctl/journal gets stored (encrypted vault). Changing it
  re-pulls the snapshot with the new login.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 23:32:28 +02:00
boban 94972c78bd perf+ux: lazy-load pages (skeletons), fix modal, normalize buttons + spinners
Route switching was slow because each page did its SSH reads inside mount(),
blocking the response. Now mount() only sets cheap shell state and the SSH read
runs in load() via wire:init — the page shell + skeletons render instantly and
content streams in.

- Lazy-load Dashboard (services), Services, Files, Server-Details: SSH moved from
  mount() to load(); skeleton placeholders (new <x-skeleton>) while !$ready.
- Modal container: z-10 -> z-50 (was rendering *under* the z-40 sidebar) and a
  real max-w-lg (the dynamic modalWidth classes were never generated by Tailwind,
  so modals spanned full width). This also fixes the "flash + disappear".
- Modal/action buttons: normalize size (min-h-11 uppercase font-display -> h-9
  text-sm font-medium) and add a wire:loading spinner + disabled-while-running on
  the confirm/save actions (no double-submit, clear feedback).
- Server-Details gauges keep wire:poll (live) but no longer block initial render.

Verified: every page renders ready=false + skeleton with no SSH, then load()
populates real data; modal buttons carry the spinner.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 23:17:02 +02:00
boban 5a9b6d317f fix(ssh): address adversarial review — host-key pinning, escaping, parsers
Resolve all 12 confirmed findings from the SSH-integration review.

Security:
- TOFU host-key pinning (new VerifiesHostKey trait + servers.ssh_host_key column):
  SshClient/Sftp record the server host key on first connect and refuse the
  connection on any later mismatch — phpseclib does not pin host keys itself.
- PollMetrics keeps one long-lived SSH connection per server and reuses it across
  ticks (a single login, not one per interval) — avoids fail2ban/auth-log churn;
  dead connections are dropped and re-established next tick.
- @js() escaping for every server-controlled value interpolated into wire:click
  (file names, paths, service names, SSH-key comments/fingerprints) — prevents
  JS-string breakage / injection from untrusted remote data.

Correctness:
- parseKeys regex makes the key comment optional (keys without a comment were
  silently dropped).
- parseVolumes pops the three trailing single-token df fields and rejoins the
  rest, so mount points containing spaces parse correctly.
- Sftp gains a disconnect() method to match SshClient (explicit cleanup).

Rules:
- Services "Start" -> "Starten" (R9, German).
- Files breadcrumb buttons get min-w-11 + padding (R7, >=44px touch target).

Verified live: host key stored + mismatch refused + correct key reconnects;
poller reuse polls ok; all pages still render real data.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 21:55:27 +02:00
boban b9b0f62d78 feat(ssh): wire real fleet data over SSH (FleetService + live poller)
Replace the page mock data with real reads from the target host via the
phpseclib SSH layer. New FleetService parses raw command output into the exact
shapes the Livewire pages already consume; all parsing forces LC_ALL=C and reads
/proc to stay locale-independent.

- app/Services/FleetService.php: metrics (cpu via two /proc/stat samples, mem via
  /proc/meminfo, disk via df), full snapshot (identity/specs incl. virt+disk_gb,
  volumes, interfaces from ip+/proc/net/dev, sshd/fail2ban/ufw hardening, authorized
  keys), systemd units + journal, and an ls-based directory listing. One compound
  command per read; connect/parse failures bubble up.
- clusev:poll-metrics command replaces the mock emitter in the dev supervisor:
  polls every credentialed server, persists cpu/mem/disk/status, broadcasts
  MetricsTicked(server) — unreachable boxes flagged offline, loop never dies.
- Pages wired with graceful failure (offline state, never a 500):
  Dashboard (live cached metrics + notable units), Services (real units+journal),
  Files (real listing + dir navigation via open/go/up), Server-Details (live
  snapshot persisted onto the row + offline banner).
- WithFleetContext prefers a credentialed, non-offline server as the default.
- dualChart filters ticks by server name so the chart tracks the active host.

Verified live against a real Debian 13 box: metrics/services(91)/journal(25)/
files(navigable)/snapshot(5 ifaces, real hardening, real key) all parse correctly.
Credentials are stored encrypted in the vault — never in source.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 21:34:13 +02:00
boban 218806727c feat(r5): wire-elements/modal confirmations for destructive actions
Add a generic ConfirmAction modal (LivewireUI\Modal\ModalComponent). On confirm
it persists exactly one AuditEvent and re-dispatches a page event so the origin
applies its own state change — domain-agnostic and reused everywhere (R5).

Wired the destructive actions, each writing an audit row:
- Services: start/stop/restart -> confirm + audit; service state reflects result
- Files: delete -> confirm + audit; entry removed
- Server-Details: revoke SSH key (new per-row trash button) -> confirm + audit
  (carries server_id)

Supporting changes:
- Publish + restyle the modal container for the dark theme (void backdrop +
  surface panel + shadow-pop instead of the package's gray/white defaults)
- Toaster island in the app layout that catches the `notify` browser event
- Add alert/power/rotate/trash icons to x-icon

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 15:05:50 +02:00
boban 5fce2dd757 feat: v1 pages — servers (index + details), services, files, audit + SSH layer
Five full-page Livewire routes (R1/R2) on the component kit + seeded models,
German UI, responsive, tokens-only:
- /servers: fleet overview (status KPIs, live search, rows linking by uuid).
- /servers/{uuid}: details — resource rings, specs, volumes, network interfaces,
  security hardening checklist, SSH keys (mock until the SSH layer).
- /services: systemd units (enabled/status, start/stop/restart placeholders per R5)
  + journalctl tail.
- /files: SFTP file browser (breadcrumb, perms/size/modified, actions).
- /audit: real AuditEvent log with live filter + empty state.

SSH layer (app/Support/Ssh): SshClient (exec), Sftp, CredentialVault (decrypts the
encrypted vault) — phpseclib3, validated, ready for a target server.

Verified: all pages HTTP 200, no errors; 5/5 screenshot-checked on-brand.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 06:33:59 +02:00