'Nur getestete, vom Maintainer freigegebene Releases (getaggte Versionen). Empfohlen für den Produktivbetrieb.', 'beta' => 'Vorabversionen kommender Releases zum Testen neuer Funktionen. Kann instabil sein. Entwicklungs-Builds werden Nutzern nie angeboten.', ]; public string $domain = ''; public string $email = ''; public string $channel = 'stable'; public function mount(DeploymentService $deployment): void { $this->domain = (string) ($deployment->domain() ?? ''); $this->email = (string) ($deployment->email() ?? ''); $channel = Setting::get('release_channel', config('clusev.channel')) ?? 'stable'; // Clamp to a valid user channel — a stale/legacy value (e.g. 'dev') falls back to stable. $this->channel = array_key_exists($channel, self::CHANNELS) ? $channel : 'stable'; } /** Persist domain + email, then (re)render the Caddy site config for review. */ public function saveDomain(DeploymentService $deployment): void { $data = $this->validate([ 'domain' => ['nullable', 'string', 'max:253', 'regex:/^(?=.{1,253}$)([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/i'], 'email' => ['nullable', 'email', 'max:255', 'required_with:domain'], ], [ 'domain.regex' => 'Bitte eine gültige Domain angeben (z. B. panel.example.com).', 'email.required_with' => 'Bei gesetzter Domain ist eine Admin-E-Mail für Let\'s Encrypt Pflicht.', ]); $deployment->setDomain($data['domain'] ?? null); $deployment->setEmail($data['email'] ?? null); $this->domain = (string) ($deployment->domain() ?? ''); $this->email = (string) ($deployment->email() ?? ''); $deployment->writeCaddyConfig(); $this->audit('system.settings_updated', $this->domain !== '' ? $this->domain : 'bare-ip'); $this->dispatch('notify', message: $this->domain !== '' ? 'Domain gespeichert. Caddy-Konfiguration neu erzeugt.' : 'Bare-IP-Modus gespeichert. Caddy-Konfiguration neu erzeugt.'); } /** Release channel changes are audited (confirmation via the shared modal). */ public function confirmChannel(string $channel): void { if (! array_key_exists($channel, self::CHANNELS) || $channel === $this->channel) { return; } $this->dispatch('openModal', component: 'modals.confirm-action', arguments: [ 'heading' => 'Release-Kanal wechseln', 'body' => 'Der Release-Kanal wird auf "'.$channel.'" gesetzt. Updates werden dann aus dieser Quelle bezogen.', 'confirmLabel' => 'Wechseln', 'danger' => false, 'icon' => 'git-branch', 'auditAction' => 'system.settings_updated', 'auditTarget' => 'release_channel='.$channel, 'event' => 'channelChanged', 'params' => ['channel' => $channel], 'notify' => 'Release-Kanal auf "'.$channel.'" gesetzt.', ], ); } #[On('channelChanged')] public function applyChannel(string $channel): void { if (! array_key_exists($channel, self::CHANNELS)) { return; } Setting::put('release_channel', $channel); $this->channel = $channel; } private function audit(string $action, string $target): void { AuditEvent::create([ 'user_id' => Auth::id(), 'actor' => Auth::user()?->name ?? 'system', 'action' => $action, 'target' => $target, 'ip' => request()->ip(), ]); } public function render(DeploymentService $deployment) { return view('livewire.system.index', [ 'hasTls' => $this->domain !== '', 'caddyConfig' => $deployment->renderCaddyConfig(), 'reloadHint' => $deployment->reloadHint(), 'whyNotEnv' => $deployment->whyNotEnv(), 'channels' => self::CHANNELS, ]); } }