[ 'label' => 'Postfix', 'hint' => 'MTA / Versand', 'sources' => ['systemd:postfix'], ], 'dovecot' => [ 'label' => 'Dovecot', 'hint' => 'IMAP / POP3', 'sources' => ['systemd:dovecot', 'tcp:127.0.0.1:993'], ], 'rspamd' => [ 'label' => 'Rspamd', 'hint' => 'Spamfilter', 'sources' => ['systemd:rspamd', 'tcp:127.0.0.1:11333', 'tcp:127.0.0.1:11334'], ], 'clamav' => [ 'label' => 'ClamAV', 'hint' => 'Virenscanner', // mehrere mögliche Units + Socket/PID + TCP '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', ], ], // Daten & Cache 'db' => [ 'label' => 'Datenbank', 'hint' => 'MySQL / MariaDB', 'sources' => ['db'], ], 'redis' => [ 'label' => 'Redis', 'hint' => 'Cache / Queue', 'sources' => ['tcp:127.0.0.1:6379', 'systemd:redis-server', 'systemd:redis'], ], // Web / PHP 'php-fpm' => [ 'label' => 'PHP-FPM', 'hint' => 'PHP Runtime', 'sources' => [ 'systemd:php8.3-fpm', 'systemd:php8.2-fpm', 'systemd:php8.1-fpm', 'systemd:php-fpm', 'socket:/run/php/php8.3-fpm.sock', 'socket:/run/php/php8.2-fpm.sock', 'socket:/run/php/php8.1-fpm.sock', 'socket:/run/php/php-fpm.sock', 'tcp:127.0.0.1:9000', ], ], 'nginx' => [ 'label' => 'Nginx', 'hint' => 'Webserver', 'sources' => ['systemd:nginx', 'tcp:127.0.0.1:80'], ], // MailWolt (Units ODER laufende artisan-Prozesse) 'mw-queue' => [ 'label' => 'MailWolt Queue', 'hint' => 'Job Worker', 'sources' => [ 'systemd:mailwolt-queue', 'proc:/php.*artisan(\.php)?\s+queue:work', ], ], 'mw-schedule' => [ 'label' => 'MailWolt Schedule', 'hint' => 'Task Scheduler', 'sources' => [ 'systemd:mailwolt-schedule', 'proc:/php.*artisan(\.php)?\s+schedule:work', ], ], 'mw-ws' => [ 'label' => 'MailWolt WebSocket', 'hint' => 'Echtzeit Updates', 'sources' => ['systemd:mailwolt-ws', 'tcp:127.0.0.1:8080'], ], // Sonstiges 'fail2ban' => [ 'label' => 'Fail2Ban', 'hint' => 'SSH / Mail Protection', 'sources' => ['systemd:fail2ban'], ], 'journal' => [ 'label' => 'System Logs', 'hint' => 'Journal', 'sources' => ['systemd:systemd-journald', 'systemd:rsyslog'], ], ]; public function mount(): void { $this->load(); } public function refresh(): void { Cache::forget('health:services.v2'); $this->load(); } public function load(): void { $data = Cache::remember('health:services.v2', 15, function () { $out = []; foreach ($this->cards as $key => $card) { $ok = false; foreach ($card['sources'] as $src) { if ($this->check($src)) { $ok = true; break; } } $out[$key] = ['label' => $card['label'], 'hint' => $card['hint'], 'ok' => $ok]; } return $out; }); $this->servicesCompact = collect($data)->map(function ($row) { $ok = (bool)$row['ok']; return [ 'label' => $row['label'], 'hint' => $row['hint'], 'ok' => $ok, 'dotClass' => $ok ? 'bg-emerald-400' : 'bg-rose-400', 'pillText' => $ok ? 'Online' : 'Offline', 'pillClass' => $ok ? 'text-emerald-300 border-emerald-400/30 bg-emerald-500/10' : 'text-rose-300 border-rose-400/30 bg-rose-500/10', ]; })->values()->all(); } public function render() { return view('livewire.ui.system.services-card'); } // ───────────── Probes ───────────── protected function check(string $source): bool { if (str_starts_with($source, 'systemd:')) { return $this->probeSystemd(substr($source, 8)); } if (str_starts_with($source, 'tcp:')) { [$host, $port] = explode(':', substr($source, 4), 2); return $this->probeTcp($host, (int)$port); } if (str_starts_with($source, 'socket:')) { $path = substr($source, 7); return is_string($path) && @file_exists($path); } if (str_starts_with($source, 'pid:')) { $path = substr($source, 4); if (!@is_file($path)) return false; $pid = (int)trim(@file_get_contents($path) ?: ''); return $pid > 1 && @posix_kill($pid, 0); } if (str_starts_with($source, 'proc:')) { $regex = substr($source, 5); return $this->probeProcessRegex($regex); } if ($source === 'db') { return $this->probeDatabase(); } return false; } protected function probeSystemd(string $unit): bool { // versuche /bin und /usr/bin $bin = file_exists('/bin/systemctl') ? '/bin/systemctl' : (file_exists('/usr/bin/systemctl') ? '/usr/bin/systemctl' : 'systemctl'); $cmd = sprintf('%s is-active --quiet %s 2>/dev/null', escapeshellcmd($bin), escapeshellarg($unit)); $exit = null; @exec($cmd, $_, $exit); return $exit === 0; } protected function probeTcp(string $host, int $port, int $timeout = 1): bool { $fp = @fsockopen($host, $port, $e1, $e2, $timeout); if (is_resource($fp)) { fclose($fp); return true; } return false; // Alternative: stream_socket_client("tcp://$host:$port", …) } protected function probeProcessRegex(string $regex): bool { // /proc durchsuchen – leichtgewichtig und ohne ps-Depend $regex = '#' . $regex . '#i'; foreach (@scandir('/proc') ?: [] as $d) { if (!ctype_digit($d)) continue; $cmd = @file_get_contents("/proc/$d/cmdline"); if ($cmd === false || $cmd === '') continue; $cmd = str_replace("\0", ' ', $cmd); if (preg_match($regex, $cmd)) return true; } return false; } protected function probeDatabase(): bool { try { DB::connection()->getPdo(); return true; } catch (\Throwable) { return false; } } } // //namespace App\Livewire\Ui\System; // //use Carbon\Carbon; //use Illuminate\Support\Facades\Cache; //use Livewire\Component; // //class ServicesCard extends Component //{ // public array $services = []; // public array $servicesCompact = []; // // // Mapping für schöne Labels/Hints // protected array $nameMap = [ // // Mail // 'postfix' => ['label' => 'Postfix', 'hint' => 'MTA / Versand'], // 'dovecot' => ['label' => 'Dovecot', 'hint' => 'IMAP / POP3'], // 'rspamd' => ['label' => 'Rspamd', 'hint' => 'Spamfilter'], // 'clamav' => ['label' => 'ClamAV', 'hint' => 'Virenscanner'], // // // Daten & Cache // 'db' => ['label' => 'Datenbank', 'hint' => 'MySQL / MariaDB'], // '127.0.0.1:6379' => ['label' => 'Redis', 'hint' => 'Cache / Queue'], // // // Web / PHP // 'php8.2-fpm' => ['label' => 'PHP-FPM', 'hint' => 'PHP Runtime'], // 'nginx' => ['label' => 'Nginx', 'hint' => 'Webserver'], // // // MailWolt // 'mailwolt-queue' => ['label' => 'MailWolt Queue', 'hint' => 'Job Worker'], // 'mailwolt-schedule'=> ['label' => 'MailWolt Schedule', 'hint' => 'Task Scheduler'], // 'mailwolt-ws' => ['label' => 'MailWolt WebSocket','hint' => 'Echtzeit Updates'], // // // Sonstiges // 'fail2ban' => ['label' => 'Fail2Ban', 'hint' => 'SSH / Mail Protection'], // 'systemd-journald'=> ['label' => 'System Logs', 'hint' => 'Journal'], // 'rsyslog' => ['label' => 'Rsyslog', 'hint' => 'Logging'], // // // WebSocket/TCP // '127.0.0.1:8080' => ['label' => 'Reverb', 'hint' => 'WebSocket Server'], // ]; // // public function mount(): void // { // $this->load(); // } // // public function load(): void // { // $this->services = Cache::get('health:services', []); // $meta = Cache::get('health:meta', []); // $updated = $meta['updated_at'] ?? null; // // $existing = collect($this->services)->keyBy('name'); // // $this->servicesCompact = collect($this->nameMap) // ->map(function ($meta, $key) use ($existing) { // $srv = $existing->get($key, []); // $ok = (bool)($srv['ok'] ?? false); // // return [ // 'label' => $meta['label'], // 'hint' => $meta['hint'], // 'ok' => $ok, // 'dotClass' => $ok ? 'bg-emerald-400' : 'bg-rose-400', // 'pillText' => $ok ? 'Aktiv' : 'Offline', // 'pillClass' => $ok // ? 'text-emerald-300 border-emerald-400/30 bg-emerald-500/10' // : 'text-rose-300 border-rose-400/30 bg-rose-500/10', // ]; // }) // ->values() // ->all(); // } // // public function render() // { // return view('livewire.ui.system.services-card'); // } //}