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

280 lines
8.6 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\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');
//// }
//// }
//// }
////}