&1") ?? ''); $fixes[] = ['title'=>$title, 'cmd'=>$cmd, 'output'=>substr($out,0,200), 'time'=>date('H:i:s')]; lg("AUTO-FIX: $title"); return $out; } function al($level, $msg) { global $alerts; $alerts[] = ['level'=>$level, 'msg'=>$msg, 'time'=>date('H:i:s')]; } function sentinel($cmd) { $url = "http://10.1.0.3:5890/api/sentinel-brain.php?action=exec&cmd=" . urlencode($cmd); $r = @file_get_contents($url, false, stream_context_create(['http'=>['timeout'=>8]])); $d = @json_decode($r, true); return $d['output'] ?? ''; } function port_open($host, $port) { $c = @fsockopen($host, $port, $e, $err, 2); if ($c) { fclose($c); return true; } return false; } // ═══ 1. S204 SERVICES ═══ $s204_checks = [ 'nginx'=>80, 'php-fpm'=>9000, 'postgresql'=>5432, 'ollama'=>11434, 'qdrant'=>6333, 'deerflow'=>2024, /* 'authentik'=>9090 RETIRE OPUS46 20avr - service volontairement DISABLED dans /opt/authentik/docker-compose.yml.DISABLED */ 'mirofish'=>5001, ]; foreach ($s204_checks as $svc=>$port) { if (!port_open('127.0.0.1', $port)) { al('critical', "S204:$svc DOWN (:$port)"); // Auto-restart known services $restart_map = ['nginx'=>'nginx','php-fpm'=>'php8.5-fpm','ollama'=>'ollama']; if (isset($restart_map[$svc])) { fx("Restart $svc", "systemctl restart {$restart_map[$svc]}"); } } } // ═══ 2. S95 MTA CHECK ═══ $s95_ss = sentinel("ss -tln"); $s95_checks = ['pmta'=>25, 'kumomta'=>587, 'postfix'=>2525, 'sentinel'=>5890, 'adx'=>5821]; foreach ($s95_checks as $svc=>$port) { if (strpos($s95_ss, ":$port ") === false) { al('critical', "S95:$svc DOWN (:$port)"); if ($svc === 'kumomta') sentinel("sudo systemctl start kumomta"); elseif ($svc === 'postfix') sentinel("sudo systemctl start postfix"); $fixes[] = ['title'=>"S95 restart $svc", 'time'=>date('H:i:s')]; } } // ═══ 3. DISK CHECK ═══ $disk = (int)trim(shell_exec("df / | awk 'NR==2{print $5}' | tr -d '%'")); if ($disk >= 90) { fx("Disk cleanup $disk%", "find /var/log -name '*.gz' -delete; journalctl --vacuum-size=200M; docker image prune -af; pip cache purge 2>/dev/null"); al('warning', "Disk $disk% — cleaned"); } elseif ($disk >= 85) { fx("Disk light cleanup $disk%", "find /var/log -name '*.gz' -mtime +7 -delete; find /tmp -mtime +3 -delete 2>/dev/null"); } lg("Disk: $disk%"); // ═══ 4. SSL CERT ═══ $ssl_exp = trim(shell_exec("openssl x509 -in /var/www/weval/ssl/fullchain.pem -noout -enddate 2>/dev/null | cut -d= -f2")); if ($ssl_exp) { $days = (int)((strtotime($ssl_exp) - time()) / 86400); lg("SSL: {$days}d remaining"); if ($days < 3) { fx("SSL renewal", "openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /var/www/weval/ssl/privkey.pem -out /var/www/weval/ssl/fullchain.pem -subj '/CN=weval-consulting.com' -addext 'subjectAltName=DNS:weval-consulting.com,DNS:*.weval-consulting.com' && systemctl reload nginx"); al('critical', "SSL expired — renewed"); } elseif ($days < 14) { al('warning', "SSL expires in {$days} days"); } } // ═══ 5. DOCKER HEALTH ═══ $dead = trim(shell_exec("docker ps -f status=exited --format '{{.Names}}' 2>/dev/null | head -5")); if ($dead) { foreach (explode("\n", $dead) as $c) { if (trim($c)) { fx("Docker restart $c", "docker restart $c"); } } } $docker_count = (int)trim(shell_exec("docker ps -q 2>/dev/null | wc -l")); lg("Docker: $docker_count containers"); // ═══ 6. OLLAMA MODELS ═══ $ollama = @json_decode(@file_get_contents('http://127.0.0.1:11434/api/tags'), true); if ($ollama) { $total_gb = 0; $models = []; foreach ($ollama['models'] ?? [] as $m) { $total_gb += $m['size'] / (1024**3); $models[] = $m['name']; } lg("Ollama: " . count($models) . " models, " . round($total_gb,1) . "GB"); if ($total_gb > 25) { // Remove known redundant models foreach (['weval-brain-v2:latest','qwen2.5:7b','mistral:latest'] as $old) { if (in_array($old, $models)) { fx("Remove Ollama $old", "curl -s -X DELETE http://127.0.0.1:11434/api/delete -d '{\"name\":\"$old\"}'"); } } } } // ═══ 7. RAM CHECK ═══ $ram = (int)trim(shell_exec("free | awk '/Mem/{printf(\"%.0f\", $3/$2*100)}'")); if ($ram > 95) { fx("RAM flush $ram%", "sync; echo 3 > /proc/sys/vm/drop_caches"); al('warning', "RAM $ram% — flushed"); } lg("RAM: $ram%"); // === 8. TOKEN EXPIRY CHECK === $tokData = @json_decode(@file_get_contents('http://127.0.0.1/api/wevia-action-engine.php?action=tokens_check'), true); if ($tokData && ($tokData['tokens'] ?? false)) { foreach ($tokData['tokens'] as $k=>$v) { if (!($v['valid'] ?? false)) { al('warning', "Token $k expired"); } } } // ═══ 9. ARCHITECTURE SCORE ═══ $arch = @json_decode(@file_get_contents('/var/www/html/api/architecture-index.json'), true); if ($arch) { $score = $arch['recommendations']['score'] ?? 0; lg("Arch score: $score/100"); if ($score < 90) al('warning', "Architecture score $score/100"); } // ═══ SAVE STATUS ═══ $status = [ 'timestamp' => $ts, 'version' => '1.0', 'disk' => $disk, 'ram' => $ram, 'docker' => $docker_count, 'ssl_days' => $days ?? -1, 'ollama_models' => count($models ?? []), 'arch_score' => $score ?? 0, 'fixes_count' => count($fixes), 'alerts_count' => count($alerts), 'fixes' => $fixes, 'alerts' => $alerts, 'log' => $log, 's204_services' => count($s204_checks), 's95_mta' => count($s95_checks), ]; file_put_contents('/var/www/html/api/wevia-autonomy-status.json', json_encode($status, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); // Log fixes to KB if (!empty($fixes)) { $c = @pg_connect("host=127.0.0.1 dbname=adx_system user=admin password=admin123"); if ($c) { $fact = "AUTONOMY " . date('dM H:i') . ": " . count($fixes) . " fixes. " . implode('; ', array_map(fn($f)=>$f['title'], $fixes)); @pg_query($c, "INSERT INTO kb_learnings (category,fact,source,confidence,created_at) VALUES ('AUTO-FIX','" . pg_escape_string($c, substr($fact,0,500)) . "','autonomy-ctrl',0.95,NOW())"); pg_close($c); } } // Output for cron log echo date('H:i') . " D:{$disk}% R:{$ram}% Dk:$docker_count SSL:{$days}d F:" . count($fixes) . " A:" . count($alerts) . "\n";