diff --git a/app/Livewire/Ui/Nx/Dashboard.php b/app/Livewire/Ui/Nx/Dashboard.php
index c94c0f7..aab2e1f 100644
--- a/app/Livewire/Ui/Nx/Dashboard.php
+++ b/app/Livewire/Ui/Nx/Dashboard.php
@@ -90,6 +90,8 @@ class Dashboard extends Component
foreach ($card['sources'] as $src) {
if ($this->probeSource($src)) { $isOk = true; break; }
}
+ // Optionale Dienste (z.B. ClamAV) nur anzeigen wenn aktiv
+ if (!$isOk && ($card['optional'] ?? false)) continue;
$rows[] = ['label' => $card['label'], 'hint' => $card['hint'], 'ok' => $isOk];
}
}
diff --git a/app/Livewire/Ui/Security/ClamavManager.php b/app/Livewire/Ui/Security/ClamavManager.php
new file mode 100644
index 0000000..bb7fedb
--- /dev/null
+++ b/app/Livewire/Ui/Security/ClamavManager.php
@@ -0,0 +1,142 @@
+refresh();
+ }
+
+ public function refresh(): void
+ {
+ $this->lastError = '';
+ $this->installed = $this->isInstalled();
+ $this->running = $this->installed && $this->serviceActive();
+ $this->enabled = $this->installed && $this->serviceEnabled();
+ $this->ramMb = $this->running ? $this->readRamMb() : null;
+ [$this->dbDate, $this->dbVersion] = $this->readDbInfo();
+ }
+
+ public function enable(): void
+ {
+ if (!$this->installed) {
+ $this->lastError = 'ClamAV ist nicht installiert.';
+ return;
+ }
+ $out = [];
+ @exec('sudo -n /usr/local/sbin/mailwolt-clamav enable 2>&1', $out, $rc);
+ if ($rc !== 0) {
+ $this->lastError = implode(' ', $out) ?: 'Fehler beim Aktivieren.';
+ } else {
+ $this->dispatch('toast', type: 'success', badge: 'ClamAV',
+ title: 'Aktiviert', text: 'ClamAV läuft jetzt und startet automatisch beim Booten.');
+ }
+ $this->refresh();
+ }
+
+ public function disable(): void
+ {
+ $out = [];
+ @exec('sudo -n /usr/local/sbin/mailwolt-clamav disable 2>&1', $out, $rc);
+ if ($rc !== 0) {
+ $this->lastError = implode(' ', $out) ?: 'Fehler beim Deaktivieren.';
+ } else {
+ $this->dispatch('toast', type: 'success', badge: 'ClamAV',
+ title: 'Deaktiviert', text: 'ClamAV wurde gestoppt und aus dem Autostart entfernt.');
+ }
+ $this->refresh();
+ }
+
+ public function updateDb(): void
+ {
+ $out = [];
+ @exec('sudo -n /usr/local/sbin/mailwolt-clamav freshclam 2>&1', $out, $rc);
+ if ($rc !== 0) {
+ $this->lastError = implode(' ', $out) ?: 'Fehler beim Aktualisieren.';
+ } else {
+ $this->dispatch('toast', type: 'success', badge: 'ClamAV',
+ title: 'Datenbank aktualisiert', text: 'Virensignaturen wurden aktualisiert.');
+ }
+ $this->refresh();
+ }
+
+ private function isInstalled(): bool
+ {
+ return is_executable('/usr/bin/clamscan') || is_executable('/usr/sbin/clamd')
+ || @file_exists('/usr/sbin/clamd') || @file_exists('/usr/bin/clamdscan');
+ }
+
+ private function serviceActive(): bool
+ {
+ $exit = null;
+ @exec('systemctl is-active --quiet clamav-daemon 2>/dev/null', $_, $exit);
+ return $exit === 0;
+ }
+
+ private function serviceEnabled(): bool
+ {
+ $exit = null;
+ @exec('systemctl is-enabled --quiet clamav-daemon 2>/dev/null', $_, $exit);
+ return $exit === 0;
+ }
+
+ private function readRamMb(): ?int
+ {
+ $out = [];
+ @exec("ps -C clamd -o rss= 2>/dev/null", $out);
+ $kb = array_sum(array_map('intval', array_filter($out)));
+ return $kb > 0 ? (int) round($kb / 1024) : null;
+ }
+
+ private function readDbInfo(): array
+ {
+ $paths = [
+ '/var/lib/clamav/main.cvd',
+ '/var/lib/clamav/main.cld',
+ '/var/lib/clamav/daily.cvd',
+ '/var/lib/clamav/daily.cld',
+ ];
+ $latest = 0;
+ foreach ($paths as $p) {
+ if (@file_exists($p)) {
+ $mtime = @filemtime($p);
+ if ($mtime > $latest) $latest = $mtime;
+ }
+ }
+ if ($latest === 0) return ['—', '—'];
+ $date = date('d.m.Y H:i', $latest);
+
+ $ver = '—';
+ $out = [];
+ @exec('sigtool --info /var/lib/clamav/daily.cld 2>/dev/null | grep "^Version:" | head -1', $out);
+ if (empty($out)) {
+ @exec('sigtool --info /var/lib/clamav/daily.cvd 2>/dev/null | grep "^Version:" | head -1', $out);
+ }
+ if (!empty($out[0])) {
+ $ver = trim(str_replace('Version:', '', $out[0]));
+ }
+
+ return [$date, $ver];
+ }
+
+ public function render()
+ {
+ return view('livewire.ui.security.clamav-manager');
+ }
+}
diff --git a/config/ui-menu.php b/config/ui-menu.php
index 2f9a5fc..d32dac8 100644
--- a/config/ui-menu.php
+++ b/config/ui-menu.php
@@ -40,6 +40,7 @@ return [
'label' => 'Sicherheit', 'icon' => 'ph-shield', 'items' => [
['label' => 'TLS & Ciphers', 'route' => 'ui.security.tls'],
['label' => 'Ratelimits', 'route' => 'ui.security.abuse'],
+ ['label' => 'Virenschutz', 'route' => 'ui.security.clamav'],
['label' => 'Audit-Logs', 'route' => 'ui.security.audit'],
],
],
diff --git a/config/woltguard.php b/config/woltguard.php
index 552397d..88a45b5 100644
--- a/config/woltguard.php
+++ b/config/woltguard.php
@@ -23,6 +23,7 @@ return [
],
'clamav' => [
'label' => 'ClamAV', 'hint' => 'Virenscanner',
+ 'optional' => true,
'sources' => [
'systemd:clamav-daemon', 'systemd:clamav-daemon@scan', 'systemd:clamd',
'socket:/run/clamav/clamd.ctl', 'pid:/run/clamav/clamd.pid', 'tcp:127.0.0.1:3310',
diff --git a/resources/views/livewire/ui/security/clamav-manager.blade.php b/resources/views/livewire/ui/security/clamav-manager.blade.php
new file mode 100644
index 0000000..8c70d98
--- /dev/null
+++ b/resources/views/livewire/ui/security/clamav-manager.blade.php
@@ -0,0 +1,178 @@
+
clamav clamav-daemon ist nicht verfügbar.apt install clamav clamav-daemon -y
+