parent
020f55f53d
commit
56736a648b
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace App\Livewire\Ui\System;
|
||||
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
|
|
@ -8,37 +9,40 @@ 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 $current = null; // installierte Version
|
||||
public ?string $latest = null; // verfügbare Version (oder null)
|
||||
public string $state = 'idle'; // idle | running
|
||||
|
||||
public ?string $message = null; // inline message
|
||||
public ?string $message = null;
|
||||
public ?bool $messagePositive = null;
|
||||
|
||||
// low-level
|
||||
public bool $running = false;
|
||||
public ?int $rc = null;
|
||||
|
||||
// ---- lifecycle ---------------------------------------------------------
|
||||
// intern
|
||||
protected string $cacheStartedAtKey = 'mw.update.started_at';
|
||||
protected int $failsafeSeconds = 20 * 60; // 20 Min
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->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;
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@
|
|||
fill="url(#shieldGradient)" filter="url(#glow)"/>
|
||||
<path d="M32 6l20 8v12c0 13.5-8.7 22.7-20 27-11.3-4.3-20-13.5-20-27V14l20-8z"
|
||||
fill="url(#shine)"/>
|
||||
<i class="ph-bold ph-arrows-clockwise absolute top-1/2 left-1/2 -translate-1/2 text-2xl {{ $state === 'running' ? 'animate-spin' : '' }}"></i>
|
||||
<i class="ph-bold ph-arrows-clockwise absolute top-1/2 left-1/2 -translate-1/2 text-2xl {{ $this->hasUpdate && $state === 'running' ? 'animate-spin' : '' }}"></i>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue