From 56736a648b4f68097febeaef404d0ac0629ec066 Mon Sep 17 00:00:00 2001 From: boban Date: Wed, 22 Oct 2025 16:59:07 +0200 Subject: [PATCH] =?UTF-8?q?Fix:=20Mailbox=20Stats=20=C3=BCber=20Dovecot=20?= =?UTF-8?q?mit=20config/mailpool.php?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Livewire/Ui/System/UpdateCard.php | 387 +++++++++++++----- .../livewire/ui/system/update-card.blade.php | 2 +- 2 files changed, 286 insertions(+), 103 deletions(-) diff --git a/app/Livewire/Ui/System/UpdateCard.php b/app/Livewire/Ui/System/UpdateCard.php index fd81183..6b4dc46 100644 --- a/app/Livewire/Ui/System/UpdateCard.php +++ b/app/Livewire/Ui/System/UpdateCard.php @@ -1,5 +1,6 @@ readLowLevel(); $this->current = $this->readCurrentVersion(); - $this->latest = $this->readCachedLatest(); + $this->latest = Cache::get('mailwolt.update_available'); + $this->refreshLowLevelState(); + // initiale Message if ($this->message === null) { - if ($this->hasUpdate()) { + if ($this->getHasUpdateProperty()) { $this->message = "Neue Version verfügbar: {$this->latest}"; $this->messagePositive = false; } else { - $cur = $this->current ?? '–'; + $cur = $this->current ?: '–'; $this->message = "Du bist auf dem neuesten Stand ({$cur})"; $this->messagePositive = true; } } - // if wrapper is already running when page loads, reflect that + // falls der Wrapper gerade läuft → visuell „running“ if ($this->running) { $this->state = 'running'; } @@ -49,9 +53,8 @@ class UpdateCard extends Component return view('livewire.ui.system.update-card'); } - // ---- UI actions -------------------------------------------------------- + /* =================== Aktionen =================== */ - /** Button: „Erneut prüfen“ (no toast; shows inline progress) */ public function refreshState(): void { $this->state = 'running'; @@ -59,37 +62,26 @@ class UpdateCard extends Component $this->messagePositive = null; try { - // your checker should update the cache key below Artisan::call('mailwolt:check-updates'); } catch (\Throwable $e) { - // swallow but still continue to show something meaningful + // weich fallen } - $this->current = $this->readCurrentVersion(); - $this->latest = $this->readCachedLatest(); - $this->readLowLevel(); - - if ($this->hasUpdate()) { - $this->message = "Neue Version verfügbar: {$this->latest}"; - $this->messagePositive = false; - } else { - $this->message = "Du bist auf dem neuesten Stand ($this->current ?? '–')"; - $this->messagePositive = true; - } - - $this->state = 'idle'; + $this->reloadVersionsAndStatus(); + $this->finishUiIfNoUpdate(); } - /** Button: „Jetzt aktualisieren“ (no toast; start wrapper + progress) */ public function runUpdate(): void { - // Hide the yellow badges immediately + // Badge „Update verfügbar“ sofort ausblenden Cache::forget('mailwolt.update_available'); - // spawn wrapper in the background + // Startzeit merken (Failsafe) + Cache::put($this->cacheStartedAtKey, time(), now()->addHours(1)); + + // Wrapper asynchron starten @shell_exec('nohup sudo -n /usr/local/sbin/mw-update >/dev/null 2>&1 &'); - // reflect “running” in UI $this->latest = null; $this->state = 'running'; $this->running = true; @@ -98,64 +90,67 @@ class UpdateCard extends Component } /** - * Called from the blade with `wire:poll.1200ms="tick"` – but **only** - * when $state === 'running'. This finishes the UX once the wrapper ends. + * Wird vom Blade nur während running gepollt (wire:poll="tick"). + * Bricht den Fortschritt ab, sobald der Wrapper „done“ meldet ODER + * der Failsafe greift. Lädt danach Versionen & Badge neu. */ public function tick(): void { - if ($this->state !== 'running') return; + $this->refreshLowLevelState(); - $this->readLowLevel(); - - if (!$this->running) { // wrapper finished - // give build.info a split second to land on slow disks - usleep(150000); - - $this->current = $this->readCurrentVersion(); - $this->latest = $this->readCachedLatest(); - - if ($this->rc === 0) { - // success path - if ($this->hasUpdate()) { - // very unlikely right after success, but handle anyway - $this->message = "Neue Version verfügbar: {$this->latest}"; - $this->messagePositive = false; - } else { - $this->message = "Update erfolgreich – jetzt: {$this->current}"; - $this->messagePositive = true; - } - } else { - // failure path - $rc = $this->rc ?? 1; - $this->message = "Update fehlgeschlagen (rc={$rc})"; - $this->messagePositive = false; - } - - $this->state = 'idle'; + // Failsafe: nach N Minuten Fortschritt aus + $started = (int)Cache::get($this->cacheStartedAtKey, 0); + if ($this->running && $started > 0 && (time() - $started) > $this->failsafeSeconds) { + $this->running = false; } + + if (!$this->running) { + // abgeschlossen → Startmarke löschen + Cache::forget($this->cacheStartedAtKey); + + // Versionen/Caches neu laden + $this->reloadVersionsAndStatus(); + + // wenn erfolgreich (rc=0) und keine neue Version mehr → Done + $this->finishUiIfNoUpdate(); + } + // wenn weiterhin running: nichts tun, UI zeigt Progress weiter } - // ---- helpers ----------------------------------------------------------- + /* =================== Computed =================== */ + // Blade nutzt $this->hasUpdate public function getHasUpdateProperty(): bool { - // gleiche Logik wie in deiner Klasse: $cur = $this->normalizeVersion($this->current ?? null); $lat = $this->normalizeVersion($this->latest ?? null); if ($lat === null || $cur === null) return false; return version_compare($lat, $cur, '>'); } - protected function hasUpdate(): bool - { - $cur = $this->normalizeVersion($this->current); - $lat = $this->normalizeVersion($this->latest); + /* =================== Helpers =================== */ - if ($lat === null || $cur === null) return false; - return version_compare($lat, $cur, '>'); + protected function reloadVersionsAndStatus(): void + { + $this->current = $this->readCurrentVersion(); + $this->latest = Cache::get('mailwolt.update_available'); + $this->refreshLowLevelState(); } - protected function readLowLevel(): void + protected function finishUiIfNoUpdate(): void + { + if (!$this->getHasUpdateProperty()) { + // alles aktuell → Fortschritt aus, Badge „Aktuell“, Hinweistext grün + $this->state = 'idle'; + $this->message = "Du bist auf dem neuesten Stand (" . $this->current ?? '–' . ")"; + $this->messagePositive = true; + + // zur Sicherheit Badge-Cache entfernen + Cache::forget('mailwolt.update_available'); + } + } + + protected function refreshLowLevelState(): void { $state = @trim(@file_get_contents('/var/lib/mailwolt/update/state') ?: ''); $this->running = ($state === 'running'); @@ -166,53 +161,241 @@ class UpdateCard extends Component protected function readCurrentVersion(): ?string { - // Prefer the installer/updater stamp $build = @file_get_contents('/etc/mailwolt/build.info'); if ($build) { foreach (preg_split('/\R+/', $build) as $line) { if (str_starts_with($line, 'version=')) { $v = trim(substr($line, 8)); - if ($v !== '') return $this->prettyVersion($v); + if ($v !== '') return $v; } } } - $v = trim((string)config('app.version', '')); - return $v !== '' ? $this->prettyVersion($v) : null; + $v = config('app.version'); + return $v !== '' ? $v : null; } - protected function readCachedLatest(): ?string - { - $v = Cache::get('mailwolt.update_available'); - if (!is_string($v) || $v === '') return null; - return $this->prettyVersion($v); - } - - /** - * Return a “compare-safe” semver string: e.g. `v1.0.18` → `1.0.18`. - * Accepts tags like `release-1.0.18` too. - */ protected function normalizeVersion(?string $v): ?string { - if (!$v) return null; - // keep only the first x.y.z like portion - if (preg_match('/(\d+\.\d+\.\d+)/', $v, $m)) { - return $m[1]; - } - // fallback: bare digits (x.y) - if (preg_match('/(\d+\.\d+)/', $v, $m)) { - return $m[1]; - } - return null; - } - - /** Always render with a single leading `v` (no “vv…”) */ - protected function prettyVersion(string $v): string - { - $n = $this->normalizeVersion($v); - return $n ? 'v' . $n : $v; + if ($v === null) return null; + $v = trim($v); + $v = ltrim($v, 'v'); // führt "v1.0.19" und "1.0.19" zusammen + return $v === '' ? null : $v; } } +//namespace App\Livewire\Ui\System; +// +//use Illuminate\Support\Facades\Artisan; +//use Illuminate\Support\Facades\Cache; +//use Livewire\Component; +// +//class UpdateCard extends Component +//{ +// public ?string $current = null; // installed (pretty) +// public ?string $latest = null; // available (pretty or null) +// public string $state = 'idle'; // idle|running +// +// public ?string $message = null; // inline message +// public ?bool $messagePositive = null; +// +// // low-level +// public bool $running = false; +// public ?int $rc = null; +// +// // ---- lifecycle --------------------------------------------------------- +// +// public function mount(): void +// { +// $this->readLowLevel(); +// $this->current = $this->readCurrentVersion(); +// $this->latest = $this->readCachedLatest(); +// +// if ($this->message === null) { +// if ($this->hasUpdate()) { +// $this->message = "Neue Version verfügbar: {$this->latest}"; +// $this->messagePositive = false; +// } else { +// $cur = $this->current ?? '–'; +// $this->message = "Du bist auf dem neuesten Stand ({$cur})"; +// $this->messagePositive = true; +// } +// } +// +// // if wrapper is already running when page loads, reflect that +// if ($this->running) { +// $this->state = 'running'; +// } +// } +// +// public function render() +// { +// return view('livewire.ui.system.update-card'); +// } +// +// // ---- UI actions -------------------------------------------------------- +// +// /** Button: „Erneut prüfen“ (no toast; shows inline progress) */ +// public function refreshState(): void +// { +// $this->state = 'running'; +// $this->message = 'Prüfe auf Updates …'; +// $this->messagePositive = null; +// +// try { +// // your checker should update the cache key below +// Artisan::call('mailwolt:check-updates'); +// } catch (\Throwable $e) { +// // swallow but still continue to show something meaningful +// } +// +// $this->current = $this->readCurrentVersion(); +// $this->latest = $this->readCachedLatest(); +// $this->readLowLevel(); +// +// if ($this->hasUpdate()) { +// $this->message = "Neue Version verfügbar: {$this->latest}"; +// $this->messagePositive = false; +// } else { +// $this->message = "Du bist auf dem neuesten Stand ($this->current ?? '–')"; +// $this->messagePositive = true; +// } +// +// $this->state = 'idle'; +// } +// +// /** Button: „Jetzt aktualisieren“ (no toast; start wrapper + progress) */ +// public function runUpdate(): void +// { +// // Hide the yellow badges immediately +// Cache::forget('mailwolt.update_available'); +// +// // spawn wrapper in the background +// @shell_exec('nohup sudo -n /usr/local/sbin/mw-update >/dev/null 2>&1 &'); +// +// // reflect “running” in UI +// $this->latest = null; +// $this->state = 'running'; +// $this->running = true; +// $this->message = 'Update läuft …'; +// $this->messagePositive = null; +// } +// +// /** +// * Called from the blade with `wire:poll.1200ms="tick"` – but **only** +// * when $state === 'running'. This finishes the UX once the wrapper ends. +// */ +// public function tick(): void +// { +// if ($this->state !== 'running') return; +// +// $this->readLowLevel(); +// +// if (!$this->running) { // wrapper finished +// // give build.info a split second to land on slow disks +// usleep(150000); +// +// $this->current = $this->readCurrentVersion(); +// $this->latest = $this->readCachedLatest(); +// +// if ($this->rc === 0) { +// // success path +// if ($this->hasUpdate()) { +// // very unlikely right after success, but handle anyway +// $this->message = "Neue Version verfügbar: {$this->latest}"; +// $this->messagePositive = false; +// } else { +// $this->message = "Update erfolgreich – jetzt: {$this->current}"; +// $this->messagePositive = true; +// } +// } else { +// // failure path +// $rc = $this->rc ?? 1; +// $this->message = "Update fehlgeschlagen (rc={$rc})"; +// $this->messagePositive = false; +// } +// +// $this->state = 'idle'; +// } +// } +// +// // ---- helpers ----------------------------------------------------------- +// +// public function getHasUpdateProperty(): bool +// { +// // gleiche Logik wie in deiner Klasse: +// $cur = $this->normalizeVersion($this->current ?? null); +// $lat = $this->normalizeVersion($this->latest ?? null); +// if ($lat === null || $cur === null) return false; +// return version_compare($lat, $cur, '>'); +// } +// +// protected function hasUpdate(): bool +// { +// $cur = $this->normalizeVersion($this->current); +// $lat = $this->normalizeVersion($this->latest); +// +// if ($lat === null || $cur === null) return false; +// return version_compare($lat, $cur, '>'); +// } +// +// protected function readLowLevel(): void +// { +// $state = @trim(@file_get_contents('/var/lib/mailwolt/update/state') ?: ''); +// $this->running = ($state === 'running'); +// +// $rcRaw = @trim(@file_get_contents('/var/lib/mailwolt/update/rc') ?: ''); +// $this->rc = is_numeric($rcRaw) ? (int)$rcRaw : null; +// } +// +// protected function readCurrentVersion(): ?string +// { +// // Prefer the installer/updater stamp +// $build = @file_get_contents('/etc/mailwolt/build.info'); +// if ($build) { +// foreach (preg_split('/\R+/', $build) as $line) { +// if (str_starts_with($line, 'version=')) { +// $v = trim(substr($line, 8)); +// if ($v !== '') return $this->prettyVersion($v); +// } +// } +// } +// $v = trim((string)config('app.version', '')); +// return $v !== '' ? $this->prettyVersion($v) : null; +// } +// +// protected function readCachedLatest(): ?string +// { +// $v = Cache::get('mailwolt.update_available'); +// if (!is_string($v) || $v === '') return null; +// return $this->prettyVersion($v); +// } +// +// /** +// * Return a “compare-safe” semver string: e.g. `v1.0.18` → `1.0.18`. +// * Accepts tags like `release-1.0.18` too. +// */ +// protected function normalizeVersion(?string $v): ?string +// { +// if (!$v) return null; +// // keep only the first x.y.z like portion +// if (preg_match('/(\d+\.\d+\.\d+)/', $v, $m)) { +// return $m[1]; +// } +// // fallback: bare digits (x.y) +// if (preg_match('/(\d+\.\d+)/', $v, $m)) { +// return $m[1]; +// } +// return null; +// } +// +// /** Always render with a single leading `v` (no “vv…”) */ +// protected function prettyVersion(string $v): string +// { +// $n = $this->normalizeVersion($v); +// return $n ? 'v' . $n : $v; +// } +//} + //namespace App\Livewire\Ui\System; // //use Livewire\Component; diff --git a/resources/views/livewire/ui/system/update-card.blade.php b/resources/views/livewire/ui/system/update-card.blade.php index 0f5112d..28743c8 100644 --- a/resources/views/livewire/ui/system/update-card.blade.php +++ b/resources/views/livewire/ui/system/update-card.blade.php @@ -122,7 +122,7 @@ fill="url(#shieldGradient)" filter="url(#glow)"/> - +