clusev/resources/views/livewire/system/index.blade.php

120 lines
7.4 KiB
PHP

@php
$field = 'h-9 w-full rounded-md border border-line bg-inset px-3 font-mono text-sm text-ink placeholder:text-ink-4 focus:border-accent/40 focus:outline-none';
$label = 'mb-1 block font-mono text-[11px] uppercase tracking-wider text-ink-3';
$err = 'mt-1 flex items-center gap-1.5 font-mono text-[11px] text-offline';
@endphp
<div class="space-y-5">
{{-- Header --}}
<div class="flex flex-wrap items-center gap-4 rounded-xl border border-line bg-surface p-5 shadow-panel">
<span class="grid h-14 w-14 shrink-0 place-items-center rounded-xl border border-accent/25 bg-accent/10 text-accent">
<x-icon name="settings" class="h-6 w-6" />
</span>
<div class="min-w-0 flex-1">
<p class="font-mono text-[11px] uppercase tracking-[0.2em] text-accent-text">System</p>
<h2 class="mt-0.5 truncate font-display text-xl font-semibold text-ink">Domain, TLS & Release-Kanal</h2>
<p class="truncate font-mono text-[11px] text-ink-3">Zugriffsadresse des Panels und Update-Quelle</p>
</div>
<span @class([
'inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 font-mono text-[11px]',
'border-online/30 text-online' => $hasTls,
'border-warning/30 text-warning' => ! $hasTls,
])>
<x-status-dot :status="$hasTls ? 'online' : 'warning'" />{{ $hasTls ? 'HTTPS aktiv' : 'Klartext-HTTP' }}
</span>
</div>
{{-- Domain & TLS --}}
<x-panel title="Domain & TLS" subtitle="Erreichbarkeit des Panels und Let's-Encrypt-Zertifikat">
{{-- Current TLS state --}}
@if ($hasTls)
<div class="mb-4 flex items-start gap-3 rounded-md border border-online/25 bg-online/5 p-3">
<x-icon name="shield" class="mt-0.5 h-4 w-4 shrink-0 text-online" />
<div class="min-w-0">
<p class="text-sm text-ink">HTTPS via Let's Encrypt (Caddy)</p>
<p class="mt-0.5 font-mono text-[11px] text-ink-3">Caddy stellt automatisch ein Zertifikat für <span class="text-ink-2">{{ $domain }}</span> aus und erneuert es.</p>
</div>
</div>
@else
<div class="mb-4 flex items-start gap-3 rounded-md border border-warning/25 bg-warning/5 p-3">
<x-icon name="alert" class="mt-0.5 h-4 w-4 shrink-0 text-warning" />
<div class="min-w-0">
<p class="text-sm text-ink">Bare-IP-Modus: Klartext-HTTP</p>
<p class="mt-0.5 font-mono text-[11px] text-ink-3">Ohne Domain läuft das Panel inkl. 2FA und Audit über unverschlüsseltes HTTP. Für den Produktivbetrieb eine Domain setzen.</p>
</div>
</div>
@endif
<form wire:submit="saveDomain" class="space-y-4">
<div class="grid gap-4 sm:grid-cols-2">
<div>
<label class="{{ $label }}">Domain</label>
<input wire:model="domain" type="text" placeholder="panel.example.com" autocomplete="off" class="{{ $field }}" />
@error('domain') <p class="{{ $err }}"><x-icon name="alert" class="h-3.5 w-3.5 shrink-0" />{{ $message }}</p> @enderror
<p class="mt-1 font-mono text-[11px] text-ink-4">Leer lassen für Zugriff per IP über HTTP.</p>
</div>
<div>
<label class="{{ $label }}">Admin-E-Mail (Let's Encrypt)</label>
<input wire:model="email" type="email" placeholder="admin@example.com" autocomplete="off" class="{{ $field }}" />
@error('email') <p class="{{ $err }}"><x-icon name="alert" class="h-3.5 w-3.5 shrink-0" />{{ $message }}</p> @enderror
<p class="mt-1 font-mono text-[11px] text-ink-4">Für Zertifikatshinweise von Let's Encrypt.</p>
</div>
</div>
<div class="flex justify-end">
<x-btn variant="primary" type="submit" wire:target="saveDomain" wire:loading.attr="disabled">
<svg wire:loading wire:target="saveDomain" class="h-3.5 w-3.5 animate-spin" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><path d="M21 12a9 9 0 1 1-6.219-8.56" stroke-linecap="round" /></svg>
Speichern
</x-btn>
</div>
</form>
{{-- Generated Caddy site config --}}
<div class="mt-5 border-t border-line pt-5">
<div class="mb-2 flex items-center gap-2">
<x-icon name="settings" class="h-3.5 w-3.5 text-ink-3" />
<p class="font-mono text-[11px] uppercase tracking-wider text-ink-3">Erzeugte Caddy-Konfiguration</p>
</div>
<pre class="overflow-x-auto rounded-md border border-line bg-inset p-3 font-mono text-[12px] leading-relaxed text-ink-2"><code>{{ $caddyConfig }}</code></pre>
<div class="mt-3 flex items-start gap-3 rounded-md border border-line bg-raised/40 p-3">
<x-icon name="alert" class="mt-0.5 h-4 w-4 shrink-0 text-ink-3" />
<div class="min-w-0">
<p class="text-sm text-ink-2">Caddy läuft als eigener Container und muss neu laden, um die Konfiguration zu übernehmen.</p>
<p class="mt-1 font-mono text-[11px] text-ink-4">Die Datei wurde nach <span class="text-ink-3">storage/app/caddy/clusev.caddy</span> geschrieben. Im Caddy-Container ausführen:</p>
<pre class="mt-1.5 overflow-x-auto rounded border border-line bg-inset px-2.5 py-1.5 font-mono text-[11px] text-cyan"><code>{{ $reloadHint }}</code></pre>
</div>
</div>
</div>
</x-panel>
{{-- Release-Kanal --}}
<x-panel title="Release-Kanal" subtitle="Quelle für Updates">
<div class="space-y-4">
{{-- Segmented control --}}
<div role="radiogroup" aria-label="Release-Kanal" class="inline-flex w-full max-w-md rounded-md border border-line bg-inset p-1">
@foreach ($channels as $key => $desc)
<button type="button"
role="radio"
aria-checked="{{ $channel === $key ? 'true' : 'false' }}"
wire:click="confirmChannel('{{ $key }}')"
@class([
'inline-flex h-9 flex-1 items-center justify-center gap-1.5 rounded font-mono text-xs uppercase tracking-wider transition-colors',
'bg-accent/15 text-accent-text shadow-[inset_0_0_0_1px_var(--color-accent)]' => $channel === $key,
'text-ink-3 hover:bg-raised hover:text-ink-2' => $channel !== $key,
])>
@if ($channel === $key)<x-icon name="git-branch" class="h-3.5 w-3.5" />@endif{{ $key }}
</button>
@endforeach
</div>
{{-- Per-channel description --}}
<div class="flex items-start gap-3 rounded-md border border-line bg-raised/40 p-3">
<x-icon name="git-branch" class="mt-0.5 h-4 w-4 shrink-0 text-accent-text" />
<div class="min-w-0">
<p class="text-sm text-ink">Kanal: <span class="font-mono text-accent-text">{{ $channel }}</span></p>
<p class="mt-0.5 font-mono text-[11px] text-ink-3">{{ $channels[$channel] ?? '' }}</p>
</div>
</div>
</div>
</x-panel>
</div>