true, 'ms' => $ms]; } return ['up' => false, 'ms' => $ms, 'err' => $errstr]; } function probe_http($url, $timeout = 3) { $start = microtime(true); $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => $timeout, CURLOPT_NOBODY => false, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, ]); curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); $ms = round((microtime(true) - $start) * 1000); curl_close($ch); return ['up' => ($code >= 200 && $code < 500), 'ms' => $ms, 'code' => $code]; } function probe_systemd($unit) { exec("systemctl is-active $unit 2>&1", $o); $status = trim($o[0] ?? ''); return ['up' => ($status === 'active'), 'status' => $status]; } function probe_docker($name) { exec("docker ps --filter name=$name --format '{{.Status}}' 2>&1", $o); $status = trim(implode(' ', $o)); $up = (strpos($status, 'Up') !== false); return ['up' => $up, 'status' => $status ?: 'not found']; } // 23 services from admin-v2 — verify each $services = [ // systemd / nginx / FPM ['n' => 'Nginx', 'p' => ':80/:443', 't' => 'system', 'probe' => 'http://localhost'], ['n' => 'Apache', 'p' => ':5890/:8443', 't' => 'system', 'probe' => 'http://localhost:5890'], ['n' => 'PHP-FPM 8.5', 'p' => ':socket', 't' => 'systemd', 'systemd' => 'php8.5-fpm'], // LLM + APIs ['n' => 'Sovereign API', 'p' => ':4000', 't' => 'systemd', 'probe' => 'http://localhost:4000/v1/models'], ['n' => 'Ollama', 'p' => ':11434', 't' => 'systemd', 'probe' => 'http://localhost:11434/api/tags'], ['n' => 'Paperclip', 'p' => ':3100', 't' => 'systemd', 'probe' => 'http://localhost:3100'], ['n' => 'DeerFlow', 'p' => ':3002/:3003', 't' => 'systemd', 'probe' => 'http://localhost:3002'], // Docker containers ['n' => 'Loki', 'p' => ':3100 (container)', 't' => 'docker', 'docker' => 'loki'], ['n' => 'Gitea', 'p' => ':3300', 't' => 'docker', 'docker' => 'gitea'], ['n' => 'Qdrant', 'p' => ':6333', 't' => 'docker', 'docker' => 'qdrant'], ['n' => 'Mattermost', 'p' => ':8065', 't' => 'docker', 'docker' => 'mattermost'], ['n' => 'Twenty CRM', 'p' => ':3000', 't' => 'docker', 'docker' => 'twenty'], ['n' => 'Langfuse', 'p' => ':3333', 't' => 'docker', 'docker' => 'langfuse'], ['n' => 'Prometheus', 'p' => ':9090', 't' => 'docker', 'docker' => 'prometheus'], ['n' => 'Uptime-Kuma', 'p' => ':3001', 't' => 'docker', 'docker' => 'uptime-kuma'], ['n' => 'Vaultwarden', 'p' => ':8222', 't' => 'docker', 'docker' => 'vaultwarden'], ['n' => 'Redis-Weval', 'p' => ':6379', 't' => 'docker', 'docker' => 'redis-weval'], ['n' => 'SearXNG', 'p' => ':8080', 't' => 'docker', 'docker' => 'searxng'], ['n' => 'Plausible', 'p' => ':8000', 't' => 'docker', 'docker' => 'plausible'], ['n' => 'n8n', 'p' => ':5678', 't' => 'docker', 'docker' => 'n8n'], ['n' => 'Listmonk', 'p' => ':9000', 't' => 'docker', 'docker' => 'listmonk'], // PG + Email infra ['n' => 'PostgreSQL', 'p' => ':5432', 't' => 'systemd', 'systemd' => 'postgresql'], // Security ['n' => 'Fail2Ban', 'p' => 'daemon', 't' => 'systemd', 'systemd' => 'fail2ban'], ]; $results = []; $up_count = 0; $total_ms = 0; foreach ($services as $s) { $r = null; if (!empty($s['probe'])) { $r = probe_http($s['probe']); } elseif (!empty($s['docker'])) { $r = probe_docker($s['docker']); } elseif (!empty($s['systemd'])) { $r = probe_systemd($s['systemd']); } $entry = [ 'n' => $s['n'], 'p' => $s['p'], 't' => $s['t'], 's' => ($r && !empty($r['up'])) ? 'up' : 'down', ]; if (isset($r['ms'])) { $entry['ms'] = $r['ms']; $total_ms += $r['ms']; } if (isset($r['status'])) $entry['detail'] = $r['status']; if (isset($r['code'])) $entry['code'] = $r['code']; if ($entry['s'] === 'up') $up_count++; $results[] = $entry; } $total = count($results); $uptime_pct = $total > 0 ? round(($up_count / $total) * 100, 1) : 0; echo json_encode([ 'v' => 'V96.20-services-live-opus', 'ts' => date('c'), 'total' => $total, 'up' => $up_count, 'down' => $total - $up_count, 'uptime_pct' => $uptime_pct, 'avg_latency_ms' => $total > 0 ? round($total_ms / $total) : 0, 'services' => $results, ], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);