Fix: Mailbox Stats über Dovecot mit config/mailpool.php

main v1.0.83
boban 2025-10-28 21:28:43 +01:00
parent 4645b168f7
commit 10b4872a04
5 changed files with 139 additions and 11 deletions

View File

@ -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; }
}

View File

@ -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;

View File

@ -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>

View File

@ -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>

View File

@ -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();