parent
4645b168f7
commit
10b4872a04
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
namespace App\Livewire\Ui\System\Modal;
|
||||
|
||||
use Livewire\Attributes\On;
|
||||
use LivewireUI\Modal\ModalComponent;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class UpdateModal extends ModalComponent
|
||||
{
|
||||
public string $state = 'unknown'; // running|done|unknown
|
||||
public ?int $rc = null; // exit code
|
||||
public ?string $line = null; // letzte Logzeile hübsch
|
||||
public array $tail = []; // letzte N Logzeilen roh
|
||||
public int $percent = 0; // heuristisch (optional)
|
||||
public static bool $closingAllowed = false;
|
||||
|
||||
private const LOG = '/var/log/mailwolt-update.log';
|
||||
private const STATE_DIR = '/var/lib/mailwolt/update';
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->refresh();
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.ui.system.modal.update-modal');
|
||||
}
|
||||
|
||||
#[On('update:modal-refresh')]
|
||||
public function refresh(): void
|
||||
{
|
||||
$st = @trim(@file_get_contents(self::STATE_DIR.'/state') ?: '');
|
||||
$rcRaw = @trim(@file_get_contents(self::STATE_DIR.'/rc') ?: '');
|
||||
|
||||
$this->state = $st ?: 'unknown';
|
||||
$this->rc = is_numeric($rcRaw) ? (int)$rcRaw : null;
|
||||
|
||||
// Log einlesen
|
||||
$lines = @file(self::LOG, FILE_IGNORE_NEW_LINES) ?: [];
|
||||
$this->tail = array_slice($lines, -30);
|
||||
|
||||
$last = trim($this->tail ? end($this->tail) : '');
|
||||
$last = preg_replace('/^\[\w\]\s*/', '', $last);
|
||||
$last = preg_replace('/^=+ .*? =+\s*$/', 'Update beendet', $last);
|
||||
$last = preg_replace('/^\d{4}-\d{2}-\d{2}T[^ ]+\s*::\s*/', '', $last);
|
||||
$this->line = Str::limit($last, 160);
|
||||
|
||||
// ganz simple Fortschritts-Heuristik über bekannte Meilensteine
|
||||
$text = implode("\n", $this->tail);
|
||||
$pct = 5;
|
||||
foreach ([
|
||||
'Update gestartet' => 10,
|
||||
'Composer' => 25,
|
||||
'npm ci' => 40,
|
||||
'npm run build' => 60,
|
||||
'migrate' => 75,
|
||||
'optimize' => 85,
|
||||
'Version aktualisiert' => 95,
|
||||
'Update beendet' => 100,
|
||||
] as $needle => $val) {
|
||||
if (stripos($text, $needle) !== false) { $pct = max($pct, $val); }
|
||||
}
|
||||
if ($this->state === 'done') { $pct = 100; }
|
||||
$this->percent = $pct;
|
||||
|
||||
// Auto-Close vorbereiten
|
||||
if ($this->state === 'done' && $this->rc === 0) {
|
||||
static::$closingAllowed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static function modalMaxWidth(): string { return '2xl'; }
|
||||
public static function closeModalOnEscape(): bool { return static::$closingAllowed; }
|
||||
public static function closeModalOnClickAway(): bool { return static::$closingAllowed; }
|
||||
}
|
||||
|
|
@ -52,6 +52,7 @@ class UpdateCard extends Component
|
|||
|
||||
if ($this->running) {
|
||||
$this->state = 'running';
|
||||
$this->dispatch('openModal', component: 'ui.system.update-modal');
|
||||
}
|
||||
|
||||
$this->recomputeUi();
|
||||
|
|
@ -69,7 +70,7 @@ class UpdateCard extends Component
|
|||
// evtl. alte Einträge aufräumen
|
||||
Cache::forget('mailwolt.update_available');
|
||||
Cache::put($this->cacheStartedAtKey, time(), now()->addHour());
|
||||
|
||||
$this->dispatch('openModal', component: 'ui.system.update-modal');
|
||||
// Wrapper starten (setzt /var/lib/mailwolt/update/{state,rc} und schreibt Versionen)
|
||||
@shell_exec('nohup sudo -n /usr/local/sbin/mailwolt-update >/dev/null 2>&1 &');
|
||||
|
||||
|
|
@ -105,10 +106,15 @@ class UpdateCard extends Component
|
|||
if (!$this->running) {
|
||||
Cache::forget($this->cacheStartedAtKey);
|
||||
|
||||
usleep(500_000);
|
||||
// Nachlauf: Versionen & Vergleich neu aufbauen
|
||||
$this->reloadVersionsAndStatus();
|
||||
$this->recompute();
|
||||
|
||||
if ($this->current && $this->latest && version_compare($this->current, $this->latest, '>=')) {
|
||||
$this->hasUpdate = false;
|
||||
}
|
||||
|
||||
if ($this->rc === 0 && !$this->postActionsDone) {
|
||||
// Dienste neu starten (asynchron)
|
||||
// @shell_exec('nohup php /var/www/mailwolt/artisan mailwolt:restart-services >/dev/null 2>&1 &');
|
||||
|
|
@ -126,6 +132,9 @@ class UpdateCard extends Component
|
|||
badge: 'System',
|
||||
duration: 4000
|
||||
);
|
||||
|
||||
$this->dispatch('reload-page', delay: 5000);
|
||||
|
||||
} elseif ($this->rc !== null && $this->rc !== 0 && !$this->postActionsDone) {
|
||||
// Fehlerfall
|
||||
$this->postActionsDone = true;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
<div wire:poll.2s="refresh" class="p-4">
|
||||
<div class="flex items-center gap-3 mb-3">
|
||||
<div class="shrink-0 w-10 h-10 rounded-full bg-emerald-500/15 border border-emerald-400/30
|
||||
flex items-center justify-center">
|
||||
<i class="ph ph-arrows-clockwise text-xl {{ $state==='done' ? '' : 'animate-spin' }}"></i>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-white/90 font-semibold">
|
||||
{{ $state==='done'
|
||||
? ($rc===0 ? 'Update abgeschlossen' : 'Update fehlgeschlagen')
|
||||
: 'Update läuft …' }}
|
||||
</div>
|
||||
<div class="text-xs text-white/60">
|
||||
{{ $line ?? '–' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Progress --}}
|
||||
<div class="mb-3">
|
||||
<div class="h-2 bg-white/10 rounded overflow-hidden">
|
||||
<div class="h-full bg-emerald-400 transition-all"
|
||||
style="width: {{ $percent }}%"></div>
|
||||
</div>
|
||||
<div class="text-[11px] text-white/50 mt-1">{{ $percent }}%</div>
|
||||
</div>
|
||||
|
||||
{{-- Log Tail --}}
|
||||
<div class="rounded-lg bg-black/40 border border-white/10 p-2 max-h-64 overflow-auto text-[12px] font-mono text-white/80">
|
||||
@foreach($tail as $l)
|
||||
{{ $l }}
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="mt-4 flex justify-end gap-2">
|
||||
@if($state==='done' && $rc===0)
|
||||
<button wire:click="$dispatch('closeModal')" class="px-3 py-1 rounded-md
|
||||
bg-emerald-500/10 border border-emerald-400/30 text-emerald-200">
|
||||
Schließen
|
||||
</button>
|
||||
@elseif($state==='done' && $rc!==0)
|
||||
<button wire:click="$dispatch('closeModal')" class="px-3 py-1 rounded-md
|
||||
bg-rose-500/10 border border-rose-400/30 text-rose-200">
|
||||
Schließen
|
||||
</button>
|
||||
@else
|
||||
<button class="px-3 py-1 rounded-md bg-white/10 border border-white/15 text-white/50 cursor-not-allowed" disabled>
|
||||
Bitte warten …
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -50,21 +50,12 @@
|
|||
{{-- Stacked-Bar (horizontale Leiste) --}}
|
||||
@if(!empty($barSegments))
|
||||
<div class="space-y-2">
|
||||
{{-- <div class="w-full h-3 rounded-full bg-white/10 overflow-hidden ring-1 ring-white/10">--}}
|
||||
{{-- <div class="flex h-full">--}}
|
||||
{{-- @foreach($barSegments as $b)--}}
|
||||
{{-- <div class="{{ $b['color'] }} h-full" style="width: {{ $b['percent'] }}%"></div>--}}
|
||||
{{-- @endforeach--}}
|
||||
{{-- </div>--}}
|
||||
{{-- </div>--}}
|
||||
|
||||
{{-- Legende --}}
|
||||
<div class="flex flex-wrap gap-3 text-[12px] text-white/70">
|
||||
@foreach($barSegments as $b)
|
||||
<div class="inline-flex items-center gap-2">
|
||||
<span class="inline-block w-2.5 h-2.5 rounded-full {{ $b['color'] }}"></span>
|
||||
<span>{{ $b['label'] }} {{ $b['human'] }} ({{ $b['percent'] }}%)</span>
|
||||
{{-- <span>{{ $b['label'] }} {{ $b['gb'] }} GB ({{ $b['percent'] }}%)</span>--}}
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ Artisan::command('inspire', function () {
|
|||
|
||||
Schedule::job(RunHealthChecks::class)->everyMinute()->withoutOverlapping();
|
||||
Schedule::command('spamav:collect')->everyFiveMinutes()->withoutOverlapping();
|
||||
//Schedule::command('woltguard:collect-services')->everyMinute();
|
||||
//Schedule::command('mailwolt:check-updates')->dailyAt('04:10');
|
||||
Schedule::command('mailwolt:check-updates')->everytwoMinutes();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue