mailwolt/app/Livewire/Ui/System/BackupStatusCard.php

497 lines
17 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace App\Livewire\Ui\System;
use Carbon\Carbon;
use Livewire\Component;
class BackupStatusCard extends Component
{
public string $lastAt = '';
public string $lastSize = '';
public string $lastDuration = '';
public string $statusText = 'unbekannt';
public string $statusColor = 'text-white/60 border-white/20 bg-white/5';
public string $progressText = '';
public string $progressPercent = '0';
public string $progressVisibleClass = 'hidden'; // <- Sichtbarkeit
protected string $statusFile = '/var/lib/mailwolt/backup.status';
public function mount(): void { $this->load(); }
public function render() { return view('livewire.ui.system.backup-status-card'); }
public function refresh(): void { $this->load(true); }
public function runNow(): void
{
@shell_exec('nohup sudo -n /usr/local/sbin/mailwolt-backup >/dev/null 2>&1 &');
// UI sofort auf "läuft" setzen
$this->progressText = 'Vorbereitung läuft...';
$this->progressPercent = '1';
$this->progressVisibleClass = 'block';
}
protected function load(bool $force = false): void
{
$kv = [];
if (is_file($this->statusFile)) {
foreach (@file($this->statusFile, FILE_IGNORE_NEW_LINES) ?: [] as $ln) {
$p = strpos($ln, '='); if ($p !== false) $kv[substr($ln,0,$p)] = substr($ln,$p+1);
}
}
$state = $kv['state'] ?? null; // running | done | failed
$step = $kv['step'] ?? null;
$percent = isset($kv['percent']) ? (int)$kv['percent'] : 0;
$ok = isset($kv['ok']) ? ((int)$kv['ok'] === 1) : null;
// Anzeigeformatierungen wie gehabt …
// (deine bestehenden formatBytes/formatDuration/Timezone-Logik)
$this->progressPercent = (string)max(0, min(100, $percent));
$this->progressText = $this->mapStep($step);
// Sichtbarkeit steuern KEINE Blade-Logik nötig
if ($state === 'running') {
$this->progressVisibleClass = 'block';
} else {
// bei done/failed: Balken verstecken und auf 100% / finalen Text setzen
$this->progressVisibleClass = 'hidden';
if ($percent >= 100 || $step === 'done') {
$this->progressPercent = '100';
$this->progressText = 'Backup abgeschlossen.';
}
}
// Status-Badge (wie gehabt)
if ($ok === true) {
$this->statusText = 'erfolgreich';
$this->statusColor = 'text-emerald-300 border-emerald-400/30 bg-emerald-500/10';
} elseif ($ok === false) {
$this->statusText = 'fehlgeschlagen';
$this->statusColor = 'text-rose-300 border-rose-400/30 bg-rose-500/10';
} else {
$this->statusText = 'unbekannt';
$this->statusColor = 'text-white/60 border-white/20 bg-white/5';
}
}
private function mapStep(?string $step): string
{
return match($step) {
'mysqldump' => 'Datenbank wird gesichert...',
'maildir' => 'Mail-Verzeichnis wird archiviert...',
'app' => 'Anwendungsdaten werden gesichert...',
'configs' => 'Konfigurationen werden gesichert...',
'compress' => 'Backup wird komprimiert...',
'retention' => 'Alte Backups werden gelöscht...',
'done' => 'Backup abgeschlossen.',
default => 'Vorbereitung läuft...',
};
}
}
//
//class BackupStatusCard extends Component
//{
// public string $lastAt = '';
// public string $lastSize = '';
// public string $lastDuration = '';
// public string $statusText = 'unbekannt';
// public string $statusColor = 'text-white/60 border-white/20 bg-white/5';
// public string $progressText = '';
// public string $progressPercent = '0';
// public bool $running = false;
// protected string $statusFile = '/var/lib/mailwolt/backup.status';
//
// public function mount(): void
// {
// $this->load();
// }
//
// public function render()
// {
// return view('livewire.ui.system.backup-status-card');
// }
//
// public function refresh(): void
// {
// $this->load(true);
// }
//
// public function runNow(): void
// {
// @shell_exec('nohup sudo -n /usr/local/sbin/mailwolt-backup >/dev/null 2>&1 &');
// $this->running = true;
// $this->progressText = 'Starte Backup...';
// $this->progressPercent = '1';
// }
//
// protected function load(bool $force = false): void
// {
// if (!is_file($this->statusFile)) {
// $this->running = false;
// return;
// }
//
// $kv = [];
// foreach (@file($this->statusFile, FILE_IGNORE_NEW_LINES) ?: [] as $ln) {
// $p = strpos($ln, '=');
// if ($p !== false) {
// $kv[substr($ln, 0, $p)] = substr($ln, $p + 1);
// }
// }
//
// $state = $kv['state'] ?? null;
// $step = $kv['step'] ?? null;
// $percent = isset($kv['percent']) ? (int)$kv['percent'] : 0;
// $ok = isset($kv['ok']) ? ((int)$kv['ok'] === 1) : null;
//
// // Formatierung
// $tz = config('app.timezone', 'Europe/Berlin');
// $finished = $kv['finished_at'] ?? $kv['start_at'] ?? null;
// $this->lastAt = $finished
// ? Carbon::parse($finished)->setTimezone($tz)->format('d.m.Y H:i:s')
// : '';
//
// $this->lastSize = isset($kv['size'])
// ? $this->formatBytes((int)$kv['size'])
// : '';
//
// $this->lastDuration = isset($kv['duration'])
// ? $this->formatDuration((int)$kv['duration'])
// : '';
//
// // Fortschritt
// $this->progressPercent = (string)$percent;
// $this->progressText = $this->mapStep($step);
//
// // Status
// $this->running = ($state === 'running');
//
// if ($ok === true) {
// $this->statusText = 'erfolgreich';
// $this->statusColor = 'text-emerald-300 border-emerald-400/30 bg-emerald-500/10';
// } elseif ($ok === false) {
// $this->statusText = 'fehlgeschlagen';
// $this->statusColor = 'text-rose-300 border-rose-400/30 bg-rose-500/10';
// } else {
// $this->statusText = 'unbekannt';
// $this->statusColor = 'text-white/60 border-white/20 bg-white/5';
// }
// }
//
// private function formatBytes(int $b): string
// {
// if ($b >= 1024 * 1024 * 1024) return number_format($b / (1024 * 1024 * 1024), 1) . ' GB';
// if ($b >= 1024 * 1024) return number_format($b / (1024 * 1024), 1) . ' MB';
// if ($b >= 1024) return number_format($b / 1024, 0) . ' KB';
// return $b . ' B';
// }
//
// private function formatDuration(int $s): string
// {
// if ($s < 60) return $s . 's';
// $m = intdiv($s, 60);
// $r = $s % 60;
// if ($m < 60) return sprintf('%dm %02ds', $m, $r);
// $h = intdiv($m, 60);
// $m = $m % 60;
// return sprintf('%dh %02dm %02ds', $h, $m, $r);
// }
//
// private function mapStep(?string $step): string
// {
// return match ($step) {
// 'mysqldump' => 'Datenbank wird gesichert...',
// 'maildir' => 'Mail-Verzeichnis wird archiviert...',
// 'app' => 'Anwendungsdaten werden gesichert...',
// 'configs' => 'Konfigurationen werden gesichert...',
// 'compress' => 'Backup wird komprimiert...',
// 'retention' => 'Alte Backups werden gelöscht...',
// 'done' => 'Backup abgeschlossen.',
// default => 'Vorbereitung läuft...',
// };
// }
//}
//
////
////
////namespace App\Livewire\Ui\System;
////
////use Carbon\CarbonImmutable;
////use Livewire\Component;
////
////class BackupStatusCard extends Component
////{
//// public ?string $lastAt = null; // finale Zeit
//// public ?string $lastSize = null; // menschenlesbar
//// public ?string $lastDuration = null; // menschenlesbar
//// public ?bool $ok = null;
////
//// // Live-Status
//// public bool $running = false;
//// public ?string $step = null;
//// public int $percent = 0;
////
//// public function mount(): void
//// {
//// $this->load();
//// }
////
//// public function render()
//// {
//// return view('livewire.ui.system.backup-status-card');
//// }
////
//// public function refresh(): void
//// {
//// $this->load(true);
//// }
////
//// public function runNow(): void
//// {
//// // Script asynchron starten (sudoers muss gesetzt sein)
//// @shell_exec('nohup sudo -n /usr/local/sbin/mailwolt-backup >/dev/null 2>&1 &');
//// // Sofort UI auf "läuft" stellen Poll holt echten Status nach
//// $this->running = true;
//// $this->step = 'start';
//// $this->percent = 1;
//// }
////
//// protected function load(bool $force = false): void
//// {
//// $f = '/var/lib/mailwolt/backup.status';
//// if (!is_file($f)) {
//// return;
//// }
////
//// $data = [];
//// foreach (@file($f, FILE_IGNORE_NEW_LINES) ?: [] as $ln) {
//// if (strpos($ln, '=') !== false) {
//// [$k, $v] = explode('=', $ln, 2);
//// $data[$k] = $v;
//// }
//// }
////
//// $state = $data['state'] ?? null;
//// $this->running = ($state === 'running');
////
//// // Progress
//// $this->step = $data['step'] ?? null;
//// $this->percent = (int)($data['percent'] ?? 0);
////
//// // Finale Werte
//// if ($state === 'done' || $state === 'failed') {
//// $this->ok = ($data['ok'] ?? '') === '1';
//// $ts = $data['finished_at'] ?? $data['start_at'] ?? null;
//// $this->lastAt = $ts ? $this->fmtTs($ts) : null;
////
//// $bytes = (int)($data['size'] ?? 0);
//// $this->lastSize = $bytes ? $this->fmtBytes($bytes) : null;
////
//// $dur = (int)($data['duration'] ?? 0);
//// $this->lastDuration = $dur ? $this->fmtDuration($dur) : null;
//// }
//// }
////
//// protected function fmtTs(string $iso): string
//// {
//// return CarbonImmutable::parse($iso)->tz(config('app.timezone'))
//// ->format('d.m.Y H:i:s');
//// }
////
//// protected function fmtBytes(int $b): string
//// {
//// $u = ['B', 'KB', 'MB', 'GB', 'TB'];
//// $i = 0;
//// while ($b >= 1024 && $i < count($u) - 1) {
//// $b /= 1024;
//// $i++;
//// }
//// return sprintf('%.1f %s', $b, $u[$i]);
//// }
////
//// protected function fmtDuration(int $s): string
//// {
//// if ($s < 60) return $s . 's';
//// $m = intdiv($s, 60);
//// $r = $s % 60;
//// if ($m < 60) return sprintf('%dm %02ds', $m, $r);
//// $h = intdiv($m, 60);
//// $m %= 60;
//// return sprintf('%dh %02dm', $h, $m);
//// }
////}
////
//////
//////namespace App\Livewire\Ui\System;
//////
//////use Carbon\CarbonImmutable;
//////use Illuminate\Support\Str;
//////use Livewire\Component;
//////
//////class BackupStatusCard extends Component
//////{
////// public ?string $lastAt = null; // formatierte Zeit
////// public ?string $lastSize = null; // human readable
////// public ?string $lastDuration = null; // human readable
////// public ?bool $ok = null;
//////
////// // Laufzeit/Progress
////// public string $state = 'idle'; // idle|running|done|error
////// public string $step = ''; // aktueller Schritt
////// public array $steps = [
////// 'mysqldump' => 'Datenbank sichern',
////// 'maildir' => 'Maildir kopieren',
////// 'app' => 'App sichern',
////// 'configs' => 'Configs sichern',
////// 'archive' => 'Archiv erstellen',
////// 'compress' => 'Komprimieren',
////// 'retention' => 'Aufräumen',
////// 'finish' => 'Abschluss',
////// ];
//////
////// protected string $statusFile = '/var/lib/mailwolt/backup.status';
//////
////// public function mount(): void
////// {
////// $this->load(true);
////// }
//////
////// public function render()
////// {
////// return view('livewire.ui.system.backup-status-card');
////// }
//////
////// public function refresh(): void
////// {
////// $this->load(true);
////// }
//////
////// public function runNow(): void
////// {
////// @shell_exec('nohup sudo -n /usr/local/sbin/mailwolt-backup >/dev/null 2>&1 &');
////// // Sofort in "running" gehen Poll übernimmt dann
////// $this->state = 'running';
////// $this->step = 'mysqldump';
////// }
//////
////// public function load(bool $force = false): void
////// {
////// $raw = $this->readStatus();
////// $this->state = $raw['state'] ?? 'idle';
////// $this->step = $raw['step'] ?? '';
//////
////// // Datum/Zeit hübsch
////// if (!empty($raw['time'])) {
////// $this->lastAt = $this->fmtTime($raw['time']);
////// } else {
////// $this->lastAt = null;
////// }
//////
////// // Größe/Dauer hübsch
////// $bytes = isset($raw['size_bytes']) ? (int)$raw['size_bytes'] : null;
////// $secs = isset($raw['dur_seconds']) ? (int)$raw['dur_seconds'] : null;
//////
////// $this->lastSize = $bytes !== null ? $this->humanBytes($bytes) : null;
////// $this->lastDuration = $secs !== null ? $this->humanDuration($secs) : null;
////// $this->ok = isset($raw['ok']) ? ((string)$raw['ok'] === '1') : null;
////// }
//////
////// protected function readStatus(): array
////// {
////// if (!is_file($this->statusFile)) return [];
////// $out = [];
////// foreach (@file($this->statusFile, FILE_IGNORE_NEW_LINES) ?: [] as $ln) {
////// if (!str_contains($ln, '=')) continue;
////// [$k, $v] = array_map('trim', explode('=', $ln, 2));
////// $out[$k] = $v;
////// }
//////
////// // Backward compatibility (alte Keys)
////// if (isset($out['size']) && !isset($out['size_bytes'])) {
////// $out['size_bytes'] = (int)$out['size'];
////// }
////// if (isset($out['dur']) && !isset($out['dur_seconds'])) {
////// $out['dur_seconds'] = (int)$out['dur'];
////// }
//////
////// return $out;
////// }
//////
////// protected function fmtTime(string $iso): string
////// {
////// try {
////// $tz = config('app.timezone', 'UTC');
////// // ISO aus Script ist idealerweise UTC (Z)
////// $dt = CarbonImmutable::parse($iso)->timezone($tz);
////// // z.B. 27.10.2025, 16:48:02 (CET)
////// return $dt->isoFormat('L, LTS') . ' ' . $dt->format('T');
////// } catch (\Throwable) {
////// return $iso;
////// }
////// }
//////
////// protected function humanBytes(int $bytes): string
////// {
////// $units = ['B', 'KB', 'MB', 'GB', 'TB'];
////// $i = 0;
////// while ($bytes >= 1024 && $i < count($units) - 1) {
////// $bytes /= 1024;
////// $i++;
////// }
////// return number_format($bytes, $i === 0 ? 0 : 1, ',', '.') . ' ' . $units[$i];
////// }
//////
////// protected function humanDuration(int $secs): string
////// {
////// if ($secs < 60) return $secs . ' s';
////// $m = intdiv($secs, 60);
////// $s = $secs % 60;
////// if ($m < 60) return sprintf('%d min %02d s', $m, $s);
////// $h = intdiv($m, 60);
////// $m = $m % 60;
////// return sprintf('%d h %02d min', $h, $m);
////// }
//////}
//////
////////
////////namespace App\Livewire\Ui\System;
////////
////////use Livewire\Component;
////////
////////class BackupStatusCard extends Component
////////{
//////// public ?string $lastAt = null;
//////// public ?string $lastSize = null;
//////// public ?string $lastDuration = null;
//////// public ?bool $ok = null;
////////
//////// public function mount(): void { $this->load(); }
//////// public function render() { return view('livewire.ui.system.backup-status-card'); }
//////// public function refresh(): void { $this->load(true); }
////////
//////// public function runNow(): void
//////// {
//////// @shell_exec('nohup sudo -n /usr/local/sbin/mailwolt-backup >/dev/null 2>&1 &');
////////// $this->dispatch('toast', type:'info', title:'Backup gestartet');
//////// }
////////
//////// protected function load(bool $force=false): void
//////// {
//////// // Example: parse a tiny status file your backup script writes.
//////// $f = '/var/lib/mailwolt/backup.status';
//////// if (is_file($f)) {
//////// $lines = @file($f, FILE_IGNORE_NEW_LINES) ?: [];
//////// foreach ($lines as $ln) {
//////// if (str_starts_with($ln,'time=')) $this->lastAt = substr($ln,5);
//////// if (str_starts_with($ln,'size=')) $this->lastSize = substr($ln,5);
//////// if (str_starts_with($ln,'dur=')) $this->lastDuration = substr($ln,4);
//////// if (str_starts_with($ln,'ok=')) $this->ok = (substr($ln,3) === '1');
//////// }
//////// }
//////// }
////////}