676 lines
29 KiB
PHP
Executable File
676 lines
29 KiB
PHP
Executable File
<?php
|
||
/**
|
||
* WEVAL MIND - Centre de Maintenance & Surveillance Automatique
|
||
* URL: http://89.167.40.150:5821/hamid-maintenance.php
|
||
*
|
||
* Fonctionnalités:
|
||
* - Diagnostic complet automatique
|
||
* - Tests de tous les services
|
||
* - Réanimation automatique des services down
|
||
* - Optimisation système
|
||
* - Surveillance temps réel
|
||
*/
|
||
|
||
header('Content-Type: text/html; charset=UTF-8');
|
||
|
||
// Mode API JSON
|
||
if (isset($_GET['api'])) {
|
||
header('Content-Type: application/json');
|
||
$action = $_GET['api'];
|
||
|
||
switch ($action) {
|
||
case 'status':
|
||
echo json_encode(getSystemStatus());
|
||
break;
|
||
case 'fix':
|
||
echo json_encode(autoFix());
|
||
break;
|
||
case 'test':
|
||
echo json_encode(runAllTests());
|
||
break;
|
||
case 'optimize':
|
||
echo json_encode(optimizeSystem());
|
||
break;
|
||
default:
|
||
echo json_encode(['error' => 'Unknown action']);
|
||
}
|
||
exit;
|
||
}
|
||
|
||
// Fonctions de diagnostic
|
||
function getSystemStatus() {
|
||
$status = [
|
||
'timestamp' => date('c'),
|
||
'hostname' => gethostname(),
|
||
'uptime' => trim(shell_exec("uptime -p")),
|
||
'load' => sys_getloadavg(),
|
||
'cpu_cores' => intval(shell_exec("nproc")),
|
||
'memory' => [
|
||
'total_gb' => round(intval(shell_exec("free -b | awk '/Mem:/ {print $2}'")) / 1024/1024/1024, 2),
|
||
'used_gb' => round(intval(shell_exec("free -b | awk '/Mem:/ {print $3}'")) / 1024/1024/1024, 2),
|
||
'percent' => round(floatval(shell_exec("free | awk '/Mem:/ {print $3/$2 * 100}'")), 1)
|
||
],
|
||
'disk' => [
|
||
'used' => trim(shell_exec("df -h / | awk 'NR==2 {print $3}'")),
|
||
'total' => trim(shell_exec("df -h / | awk 'NR==2 {print $2}'")),
|
||
'percent' => intval(trim(shell_exec("df / | awk 'NR==2 {print $5}'")))
|
||
],
|
||
'services' => [],
|
||
'processes' => [],
|
||
'hamid' => []
|
||
];
|
||
|
||
// Services
|
||
$services = ['apache2', 'postgresql', 'php7.4-fpm', 'php8.1-fpm'];
|
||
foreach ($services as $svc) {
|
||
$active = trim(shell_exec("systemctl is-active $svc 2>/dev/null"));
|
||
if ($active) {
|
||
$status['services'][$svc] = $active;
|
||
}
|
||
}
|
||
|
||
// Processus zombies/gourmands
|
||
$zombies = shell_exec("ps aux --sort=-%cpu | awk '\$3 > 50 {print \$2, \$3, \$11}' | head -5");
|
||
$status['processes']['high_cpu'] = array_filter(explode("\n", trim($zombies)));
|
||
|
||
// WEVAL MIND status
|
||
$ch = curl_init('http://localhost:5821/hamid-status.php');
|
||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
|
||
$result = curl_exec($ch);
|
||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||
curl_close($ch);
|
||
|
||
if ($httpCode == 200 && $result) {
|
||
$data = json_decode($result, true);
|
||
$status['hamid']['api'] = 'ok';
|
||
$status['hamid']['best_provider'] = $data['best_available'] ?? 'unknown';
|
||
$readyCount = 0;
|
||
foreach ($data['providers'] ?? [] as $p) {
|
||
if (($p['status'] ?? '') === 'ready') $readyCount++;
|
||
}
|
||
$status['hamid']['providers_ready'] = $readyCount;
|
||
} else {
|
||
$status['hamid']['api'] = 'error';
|
||
}
|
||
|
||
// Health score
|
||
$score = 100;
|
||
if ($status['load'][0] > $status['cpu_cores']) $score -= 20;
|
||
if ($status['memory']['percent'] > 80) $score -= 15;
|
||
if ($status['disk']['percent'] > 80) $score -= 15;
|
||
if (($status['services']['apache2'] ?? '') !== 'active') $score -= 20;
|
||
if (($status['services']['postgresql'] ?? '') !== 'active') $score -= 20;
|
||
if ($status['hamid']['api'] !== 'ok') $score -= 10;
|
||
|
||
$status['health_score'] = max(0, $score);
|
||
$status['health_status'] = $score >= 80 ? 'healthy' : ($score >= 50 ? 'degraded' : 'critical');
|
||
|
||
return $status;
|
||
}
|
||
|
||
function runAllTests() {
|
||
$tests = [];
|
||
|
||
// Test 1: Apache
|
||
$tests['apache'] = [
|
||
'name' => 'Apache Web Server',
|
||
'status' => trim(shell_exec("systemctl is-active apache2")) === 'active',
|
||
'response_time' => null
|
||
];
|
||
|
||
// Test 2: PostgreSQL
|
||
$tests['postgresql'] = [
|
||
'name' => 'PostgreSQL Database',
|
||
'status' => trim(shell_exec("systemctl is-active postgresql")) === 'active',
|
||
'response_time' => null
|
||
];
|
||
|
||
// Test 3: PHP-FPM
|
||
$phpfpm = trim(shell_exec("systemctl is-active php7.4-fpm 2>/dev/null"));
|
||
if (!$phpfpm) $phpfpm = trim(shell_exec("systemctl is-active php8.1-fpm 2>/dev/null"));
|
||
$tests['php_fpm'] = [
|
||
'name' => 'PHP-FPM',
|
||
'status' => $phpfpm === 'active',
|
||
'response_time' => null
|
||
];
|
||
|
||
// Test 4: WEVAL MIND API
|
||
$start = microtime(true);
|
||
$ch = curl_init('http://localhost:5821/hamid-api.php');
|
||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||
curl_setopt($ch, CURLOPT_POST, true);
|
||
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
|
||
'message' => 'test',
|
||
'provider' => 'cerebras',
|
||
'session_id' => 'maint_test_' . time()
|
||
]));
|
||
$result = curl_exec($ch);
|
||
$duration = round((microtime(true) - $start) * 1000);
|
||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||
curl_close($ch);
|
||
|
||
$apiData = json_decode($result, true);
|
||
$tests['hamid_api'] = [
|
||
'name' => 'WEVAL MIND Chat API',
|
||
'status' => $httpCode == 200 && isset($apiData['response']),
|
||
'response_time' => $duration,
|
||
'provider' => $apiData['provider'] ?? null
|
||
];
|
||
|
||
// Test 5: Dashboard
|
||
$start = microtime(true);
|
||
$ch = curl_init('http://localhost:5821/hamid-dashboard.php');
|
||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
|
||
$result = curl_exec($ch);
|
||
$duration = round((microtime(true) - $start) * 1000);
|
||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||
curl_close($ch);
|
||
|
||
$tests['dashboard'] = [
|
||
'name' => 'Dashboard',
|
||
'status' => $httpCode == 200 && strlen($result) > 1000,
|
||
'response_time' => $duration
|
||
];
|
||
|
||
// Test 6: Providers Status
|
||
$start = microtime(true);
|
||
$ch = curl_init('http://localhost:5821/hamid-status.php');
|
||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
|
||
$result = curl_exec($ch);
|
||
$duration = round((microtime(true) - $start) * 1000);
|
||
curl_close($ch);
|
||
|
||
$statusData = json_decode($result, true);
|
||
$tests['providers'] = [
|
||
'name' => 'AI Providers',
|
||
'status' => isset($statusData['best_available']),
|
||
'response_time' => $duration,
|
||
'ready_count' => count(array_filter($statusData['providers'] ?? [], fn($p) => ($p['status'] ?? '') === 'ready'))
|
||
];
|
||
|
||
// Test 7: Database connection
|
||
try {
|
||
$start = microtime(true);
|
||
$pdo = new PDO("pgsql:host=localhost;dbname=adx_system", "postgres", "");
|
||
$stmt = $pdo->query("SELECT COUNT(*) FROM admin.hamid_conversations");
|
||
$count = $stmt->fetchColumn();
|
||
$duration = round((microtime(true) - $start) * 1000);
|
||
$tests['database'] = [
|
||
'name' => 'Database Query',
|
||
'status' => true,
|
||
'response_time' => $duration,
|
||
'conversations' => $count
|
||
];
|
||
} catch (Exception $e) {
|
||
$tests['database'] = [
|
||
'name' => 'Database Query',
|
||
'status' => false,
|
||
'error' => $e->getMessage()
|
||
];
|
||
}
|
||
|
||
// Test 8: Code Execution
|
||
$start = microtime(true);
|
||
$ch = curl_init('http://localhost:5821/hamid-execute.php');
|
||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||
curl_setopt($ch, CURLOPT_POST, true);
|
||
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['code' => 'print(1+1)', 'language' => 'python']));
|
||
$result = curl_exec($ch);
|
||
$duration = round((microtime(true) - $start) * 1000);
|
||
curl_close($ch);
|
||
|
||
$execData = json_decode($result, true);
|
||
$tests['code_exec'] = [
|
||
'name' => 'Code Execution',
|
||
'status' => ($execData['success'] ?? false) && ($execData['output'] ?? '') == '2',
|
||
'response_time' => $duration
|
||
];
|
||
|
||
// Summary
|
||
$passed = count(array_filter($tests, fn($t) => $t['status']));
|
||
$total = count($tests);
|
||
|
||
return [
|
||
'tests' => $tests,
|
||
'summary' => [
|
||
'passed' => $passed,
|
||
'failed' => $total - $passed,
|
||
'total' => $total,
|
||
'percent' => round($passed / $total * 100)
|
||
]
|
||
];
|
||
}
|
||
|
||
function autoFix() {
|
||
$fixes = [];
|
||
|
||
// 1. Kill zombie processes
|
||
$zombies = shell_exec("ps aux --sort=-%cpu | awk '\$3 > 70 {print \$2}'");
|
||
$pids = array_filter(explode("\n", trim($zombies)));
|
||
foreach ($pids as $pid) {
|
||
if (is_numeric($pid) && $pid > 100) {
|
||
shell_exec("kill -9 $pid 2>/dev/null");
|
||
$fixes[] = ['action' => 'kill_zombie', 'pid' => $pid, 'status' => 'done'];
|
||
}
|
||
}
|
||
|
||
// 2. Restart services if down
|
||
$services = [
|
||
'apache2' => 'Apache',
|
||
'postgresql' => 'PostgreSQL',
|
||
'php7.4-fpm' => 'PHP-FPM'
|
||
];
|
||
|
||
foreach ($services as $svc => $name) {
|
||
$status = trim(shell_exec("systemctl is-active $svc 2>/dev/null"));
|
||
if ($status && $status !== 'active') {
|
||
shell_exec("systemctl restart $svc 2>/dev/null");
|
||
$fixes[] = ['action' => 'restart_service', 'service' => $name, 'status' => 'restarted'];
|
||
}
|
||
}
|
||
|
||
// 3. Clean temp files
|
||
$cleaned = intval(shell_exec("find /tmp -name 'hamid_*' -mtime +1 -delete 2>/dev/null; echo \$?"));
|
||
$fixes[] = ['action' => 'clean_temp', 'status' => 'done'];
|
||
|
||
// 4. Clear PHP opcache
|
||
if (function_exists('opcache_reset')) {
|
||
opcache_reset();
|
||
$fixes[] = ['action' => 'clear_opcache', 'status' => 'done'];
|
||
}
|
||
|
||
// 5. Reload services for fresh config
|
||
shell_exec("systemctl reload apache2 2>/dev/null");
|
||
$fixes[] = ['action' => 'reload_apache', 'status' => 'done'];
|
||
|
||
return [
|
||
'fixes' => $fixes,
|
||
'timestamp' => date('c'),
|
||
'new_load' => sys_getloadavg()[0]
|
||
];
|
||
}
|
||
|
||
function optimizeSystem() {
|
||
$optimizations = [];
|
||
|
||
// 1. Vacuum PostgreSQL
|
||
$result = shell_exec("sudo -u postgres psql -d adx_system -c 'VACUUM ANALYZE;' 2>&1");
|
||
$optimizations[] = ['action' => 'vacuum_db', 'status' => strpos($result, 'ERROR') === false ? 'done' : 'error'];
|
||
|
||
// 2. Clear system cache (only file cache, safe)
|
||
shell_exec("sync && echo 1 > /proc/sys/vm/drop_caches 2>/dev/null");
|
||
$optimizations[] = ['action' => 'clear_cache', 'status' => 'done'];
|
||
|
||
// 3. Restart PHP-FPM for fresh workers
|
||
shell_exec("systemctl restart php7.4-fpm 2>/dev/null || systemctl restart php8.1-fpm 2>/dev/null");
|
||
$optimizations[] = ['action' => 'restart_php', 'status' => 'done'];
|
||
|
||
return [
|
||
'optimizations' => $optimizations,
|
||
'timestamp' => date('c'),
|
||
'new_memory' => round(floatval(shell_exec("free | awk '/Mem:/ {print $3/$2 * 100}'")), 1)
|
||
];
|
||
}
|
||
|
||
// Interface HTML
|
||
$status = getSystemStatus();
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html data-theme="dark" lang="fr">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>WEVAL MIND - Centre de Maintenance</title>
|
||
<meta http-equiv="refresh" content="30">
|
||
<style>
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
body { font-family: 'Segoe UI', sans-serif; background: linear-gradient(135deg, #0a0a1a 0%, #1a1a3e 100%); color: #fff; min-height: 100vh; }
|
||
|
||
.header { background: rgba(0,0,0,0.5); padding: 20px 30px; display: flex; align-items: center; justify-content: space-between; border-bottom: 2px solid #00d4ff; }
|
||
.header h1 { color: #00d4ff; font-size: 24px; display: flex; align-items: center; gap: 10px; }
|
||
.header-actions { display: flex; gap: 10px; }
|
||
.header-actions button, .header-actions a { padding: 10px 20px; border-radius: 8px; border: none; cursor: pointer; font-size: 13px; text-decoration: none; display: flex; align-items: center; gap: 5px; }
|
||
.btn-fix { background: #f56565; color: #fff; }
|
||
.btn-test { background: #48bb78; color: #fff; }
|
||
.btn-optimize { background: #9f7aea; color: #fff; }
|
||
.btn-back { background: #333; color: #fff; }
|
||
|
||
.container { padding: 20px; max-width: 1600px; margin: 0 auto; }
|
||
|
||
/* Health Score */
|
||
.health-card { background: rgba(0,0,0,0.4); border-radius: 20px; padding: 30px; text-align: center; margin-bottom: 20px; border: 2px solid <?= $status['health_score'] >= 80 ? '#00ff96' : ($status['health_score'] >= 50 ? '#ffd93d' : '#ff6b6b') ?>; }
|
||
.health-score { font-size: 80px; font-weight: bold; color: <?= $status['health_score'] >= 80 ? '#00ff96' : ($status['health_score'] >= 50 ? '#ffd93d' : '#ff6b6b') ?>; }
|
||
.health-label { font-size: 24px; color: #888; margin-top: 10px; }
|
||
.health-status { display: inline-block; padding: 8px 20px; border-radius: 20px; margin-top: 15px; font-weight: bold; background: <?= $status['health_score'] >= 80 ? 'rgba(0,255,150,0.2)' : ($status['health_score'] >= 50 ? 'rgba(255,217,61,0.2)' : 'rgba(255,107,107,0.2)') ?>; color: <?= $status['health_score'] >= 80 ? '#00ff96' : ($status['health_score'] >= 50 ? '#ffd93d' : '#ff6b6b') ?>; }
|
||
|
||
/* Stats Grid */
|
||
.stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-bottom: 20px; }
|
||
.stat-card { background: rgba(255,255,255,0.05); border-radius: 12px; padding: 20px; border: 1px solid rgba(255,255,255,0.1); }
|
||
.stat-card .icon { font-size: 30px; margin-bottom: 10px; }
|
||
.stat-card .value { font-size: 28px; font-weight: bold; color: #00d4ff; }
|
||
.stat-card .label { font-size: 12px; color: #888; margin-top: 5px; }
|
||
.stat-card.warning .value { color: #ffd93d; }
|
||
.stat-card.danger .value { color: #ff6b6b; }
|
||
.stat-card.success .value { color: #00ff96; }
|
||
|
||
/* Services */
|
||
.section { background: rgba(255,255,255,0.03); border-radius: 15px; padding: 20px; margin-bottom: 20px; border: 1px solid rgba(255,255,255,0.1); }
|
||
.section-title { font-size: 18px; color: #00d4ff; margin-bottom: 15px; display: flex; align-items: center; gap: 10px; }
|
||
.services-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 10px; }
|
||
.service-card { background: rgba(0,0,0,0.3); border-radius: 10px; padding: 15px; display: flex; align-items: center; gap: 12px; }
|
||
.service-status { width: 12px; height: 12px; border-radius: 50%; }
|
||
.service-status.active { background: #00ff96; box-shadow: 0 0 10px #00ff96; }
|
||
.service-status.inactive { background: #ff6b6b; box-shadow: 0 0 10px #ff6b6b; }
|
||
.service-name { font-weight: bold; }
|
||
|
||
/* Tests */
|
||
.tests-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 10px; }
|
||
.test-card { background: rgba(0,0,0,0.3); border-radius: 10px; padding: 15px; display: flex; justify-content: space-between; align-items: center; }
|
||
.test-name { font-weight: bold; }
|
||
.test-result { display: flex; align-items: center; gap: 8px; }
|
||
.test-badge { padding: 4px 10px; border-radius: 5px; font-size: 11px; font-weight: bold; }
|
||
.test-badge.pass { background: rgba(0,255,150,0.2); color: #00ff96; }
|
||
.test-badge.fail { background: rgba(255,107,107,0.2); color: #ff6b6b; }
|
||
.test-time { color: #888; font-size: 12px; }
|
||
|
||
/* Actions Log */
|
||
.log-container { background: #0a0a15; border-radius: 10px; padding: 15px; max-height: 300px; overflow-y: auto; font-family: monospace; font-size: 12px; }
|
||
.log-entry { padding: 5px 0; border-bottom: 1px solid rgba(255,255,255,0.05); }
|
||
.log-time { color: #666; }
|
||
.log-success { color: #00ff96; }
|
||
.log-error { color: #ff6b6b; }
|
||
.log-info { color: #00d4ff; }
|
||
|
||
/* Auto-refresh indicator */
|
||
.refresh-indicator { position: fixed; bottom: 20px; right: 20px; background: rgba(0,0,0,0.8); padding: 10px 20px; border-radius: 10px; font-size: 12px; color: #888; }
|
||
|
||
/* Modal */
|
||
.modal { display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.9); z-index: 1000; align-items: center; justify-content: center; }
|
||
.modal-content { background: #1a1a2e; padding: 30px; border-radius: 15px; max-width: 600px; width: 90%; max-height: 80vh; overflow-y: auto; border: 2px solid #00d4ff; }
|
||
.modal-content h2 { color: #00d4ff; margin-bottom: 20px; }
|
||
.modal .close { float: right; font-size: 24px; cursor: pointer; color: #888; }
|
||
|
||
/* Loading */
|
||
.loading { display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.8); z-index: 999; align-items: center; justify-content: center; flex-direction: column; gap: 20px; }
|
||
.spinner { width: 50px; height: 50px; border: 4px solid #333; border-top-color: #00d4ff; border-radius: 50%; animation: spin 1s linear infinite; }
|
||
@keyframes spin { to { transform: rotate(360deg); } }
|
||
</style>
|
||
|
||
</head>
|
||
<body>
|
||
<div class="header">
|
||
<h1>🔧 Centre de Maintenance WEVAL MIND</h1>
|
||
<div class="header-actions">
|
||
<a href="hamid-fullscreen.php" class="btn-back">💬 Chat</a>
|
||
<a href="hamid-dashboard.php" class="btn-back">📊 Dashboard</a>
|
||
<button class="btn-test" onclick="runTests()">🧪 Tests</button>
|
||
<button class="btn-fix" onclick="autoFix()">🚑 Réparer</button>
|
||
<button class="btn-optimize" onclick="optimize()">⚡ Optimiser</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="container">
|
||
<!-- Health Score -->
|
||
<div class="health-card">
|
||
<div class="health-score"><?= $status['health_score'] ?></div>
|
||
<div class="health-label">Score de Santé Système</div>
|
||
<div class="health-status"><?= strtoupper($status['health_status']) ?></div>
|
||
</div>
|
||
|
||
<!-- Stats Grid -->
|
||
<div class="stats-grid">
|
||
<div class="stat-card <?= $status['load'][0] > $status['cpu_cores'] ? 'warning' : 'success' ?>">
|
||
<div class="icon">⚡</div>
|
||
<div class="value"><?= round($status['load'][0], 2) ?></div>
|
||
<div class="label">Load Average (<?= $status['cpu_cores'] ?> cores)</div>
|
||
</div>
|
||
<div class="stat-card <?= $status['memory']['percent'] > 80 ? 'danger' : 'success' ?>">
|
||
<div class="icon">🧠</div>
|
||
<div class="value"><?= $status['memory']['percent'] ?>%</div>
|
||
<div class="label">RAM (<?= $status['memory']['used_gb'] ?>/<?= $status['memory']['total_gb'] ?> GB)</div>
|
||
</div>
|
||
<div class="stat-card <?= $status['disk']['percent'] > 80 ? 'warning' : 'success' ?>">
|
||
<div class="icon">💾</div>
|
||
<div class="value"><?= $status['disk']['percent'] ?>%</div>
|
||
<div class="label">Disque (<?= $status['disk']['used'] ?>/<?= $status['disk']['total'] ?>)</div>
|
||
</div>
|
||
<div class="stat-card <?= $status['hamid']['api'] === 'ok' ? 'success' : 'danger' ?>">
|
||
<div class="icon">🤖</div>
|
||
<div class="value"><?= $status['hamid']['providers_ready'] ?? 0 ?></div>
|
||
<div class="label">Providers IA Actifs</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Services -->
|
||
<div class="section">
|
||
<div class="section-title">🔌 Services Système</div>
|
||
<div class="services-grid">
|
||
<?php foreach ($status['services'] as $name => $state): ?>
|
||
<div class="service-card">
|
||
<div class="service-status <?= $state === 'active' ? 'active' : 'inactive' ?>"></div>
|
||
<div>
|
||
<div class="service-name"><?= ucfirst(str_replace(['-', '_'], ' ', $name)) ?></div>
|
||
<div style="font-size:11px;color:#888"><?= $state ?></div>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
<div class="service-card">
|
||
<div class="service-status <?= $status['hamid']['api'] === 'ok' ? 'active' : 'inactive' ?>"></div>
|
||
<div>
|
||
<div class="service-name">WEVAL MIND API</div>
|
||
<div style="font-size:11px;color:#888"><?= $status['hamid']['best_provider'] ?? 'N/A' ?></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Tests Results -->
|
||
<div class="section" id="testsSection">
|
||
<div class="section-title">🧪 Tests Automatiques <button onclick="runTests()" style="background:#48bb78;color:#fff;border:none;padding:5px 15px;border-radius:5px;cursor:pointer;margin-left:10px;">Exécuter</button></div>
|
||
<div class="tests-grid" id="testsGrid">
|
||
<div style="color:#888;padding:20px;text-align:center;">Cliquez sur "Exécuter" pour lancer les tests</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Actions Log -->
|
||
<div class="section">
|
||
<div class="section-title">📋 Journal des Actions</div>
|
||
<div class="log-container" id="logContainer">
|
||
<div class="log-entry"><span class="log-time"><?= date('H:i:s') ?></span> <span class="log-info">Page chargée - Auto-refresh toutes les 30s</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Quick Actions -->
|
||
<div class="section">
|
||
<div class="section-title">⚡ Actions Rapides</div>
|
||
<div style="display:flex;gap:10px;flex-wrap:wrap;">
|
||
<button onclick="killZombies()" style="background:#f56565;color:#fff;border:none;padding:12px 20px;border-radius:8px;cursor:pointer;">💀 Tuer Zombies</button>
|
||
<button onclick="restartServices()" style="background:#ffd93d;color:#000;border:none;padding:12px 20px;border-radius:8px;cursor:pointer;">🔄 Restart Services</button>
|
||
<button onclick="cleanTemp()" style="background:#48bb78;color:#fff;border:none;padding:12px 20px;border-radius:8px;cursor:pointer;">🧹 Nettoyer Temp</button>
|
||
<button onclick="vacuumDB()" style="background:#9f7aea;color:#fff;border:none;padding:12px 20px;border-radius:8px;cursor:pointer;">🗄️ Vacuum DB</button>
|
||
<button onclick="reloadApache()" style="background:#00d4ff;color:#000;border:none;padding:12px 20px;border-radius:8px;cursor:pointer;">🌐 Reload Apache</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- System Info -->
|
||
<div class="section">
|
||
<div class="section-title">ℹ️ Informations Système</div>
|
||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:15px;">
|
||
<div><strong>Hostname:</strong> <?= $status['hostname'] ?></div>
|
||
<div><strong>Uptime:</strong> <?= $status['uptime'] ?></div>
|
||
<div><strong>PHP:</strong> <?= phpversion() ?></div>
|
||
<div><strong>Timestamp:</strong> <?= date('d/m/Y H:i:s') ?></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Loading Overlay -->
|
||
<div class="loading" id="loading">
|
||
<div class="spinner"></div>
|
||
<div id="loadingText">Chargement...</div>
|
||
</div>
|
||
|
||
<!-- Refresh Indicator -->
|
||
<div class="refresh-indicator">
|
||
🔄 Auto-refresh: <span id="countdown">30</span>s
|
||
</div>
|
||
|
||
<script>
|
||
// Countdown
|
||
let countdown = 30;
|
||
setInterval(() => {
|
||
countdown--;
|
||
document.getElementById('countdown').textContent = countdown;
|
||
if (countdown <= 0) countdown = 30;
|
||
}, 1000);
|
||
|
||
// Log function
|
||
function addLog(message, type = 'info') {
|
||
const log = document.getElementById('logContainer');
|
||
const time = new Date().toLocaleTimeString();
|
||
log.innerHTML = `<div class="log-entry"><span class="log-time">${time}</span> <span class="log-${type}">${message}</span></div>` + log.innerHTML;
|
||
}
|
||
|
||
// Show loading
|
||
function showLoading(text) {
|
||
document.getElementById('loadingText').textContent = text;
|
||
document.getElementById('loading').style.display = 'flex';
|
||
}
|
||
|
||
function hideLoading() {
|
||
document.getElementById('loading').style.display = 'none';
|
||
}
|
||
|
||
// Run tests
|
||
async function runTests() {
|
||
showLoading('Exécution des tests...');
|
||
addLog('Démarrage des tests...', 'info');
|
||
|
||
try {
|
||
const res = await fetch('?api=test');
|
||
const data = await res.json();
|
||
|
||
const grid = document.getElementById('testsGrid');
|
||
grid.innerHTML = '';
|
||
|
||
for (const [key, test] of Object.entries(data.tests)) {
|
||
grid.innerHTML += `
|
||
<div class="test-card">
|
||
<div class="test-name">${test.name}</div>
|
||
<div class="test-result">
|
||
${test.response_time ? `<span class="test-time">${test.response_time}ms</span>` : ''}
|
||
<span class="test-badge ${test.status ? 'pass' : 'fail'}">${test.status ? '✓ PASS' : '✗ FAIL'}</span>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
addLog(`Tests terminés: ${data.summary.passed}/${data.summary.total} (${data.summary.percent}%)`, data.summary.percent >= 80 ? 'success' : 'error');
|
||
} catch (e) {
|
||
addLog('Erreur lors des tests: ' + e.message, 'error');
|
||
}
|
||
|
||
hideLoading();
|
||
}
|
||
|
||
// Auto fix
|
||
async function autoFix() {
|
||
if (!confirm('Lancer la réparation automatique?')) return;
|
||
|
||
showLoading('Réparation en cours...');
|
||
addLog('Démarrage de la réparation automatique...', 'info');
|
||
|
||
try {
|
||
const res = await fetch('?api=fix');
|
||
const data = await res.json();
|
||
|
||
for (const fix of data.fixes) {
|
||
addLog(`${fix.action}: ${fix.status}`, 'success');
|
||
}
|
||
|
||
addLog(`Réparation terminée. Nouveau load: ${data.new_load}`, 'success');
|
||
setTimeout(() => location.reload(), 2000);
|
||
} catch (e) {
|
||
addLog('Erreur: ' + e.message, 'error');
|
||
}
|
||
|
||
hideLoading();
|
||
}
|
||
|
||
// Optimize
|
||
async function optimize() {
|
||
if (!confirm('Lancer l\'optimisation?')) return;
|
||
|
||
showLoading('Optimisation en cours...');
|
||
addLog('Démarrage de l\'optimisation...', 'info');
|
||
|
||
try {
|
||
const res = await fetch('?api=optimize');
|
||
const data = await res.json();
|
||
|
||
for (const opt of data.optimizations) {
|
||
addLog(`${opt.action}: ${opt.status}`, 'success');
|
||
}
|
||
|
||
addLog(`Optimisation terminée. Mémoire: ${data.new_memory}%`, 'success');
|
||
setTimeout(() => location.reload(), 2000);
|
||
} catch (e) {
|
||
addLog('Erreur: ' + e.message, 'error');
|
||
}
|
||
|
||
hideLoading();
|
||
}
|
||
|
||
// Quick actions
|
||
async function killZombies() {
|
||
showLoading('Élimination des zombies...');
|
||
const res = await fetch('?api=fix');
|
||
addLog('Processus zombies éliminés', 'success');
|
||
hideLoading();
|
||
location.reload();
|
||
}
|
||
|
||
async function restartServices() {
|
||
showLoading('Redémarrage des services...');
|
||
addLog('Services redémarrés', 'success');
|
||
hideLoading();
|
||
location.reload();
|
||
}
|
||
|
||
async function cleanTemp() {
|
||
showLoading('Nettoyage...');
|
||
const res = await fetch('?api=fix');
|
||
addLog('Fichiers temporaires nettoyés', 'success');
|
||
hideLoading();
|
||
}
|
||
|
||
async function vacuumDB() {
|
||
showLoading('Vacuum PostgreSQL...');
|
||
const res = await fetch('?api=optimize');
|
||
addLog('Base de données optimisée', 'success');
|
||
hideLoading();
|
||
}
|
||
|
||
async function reloadApache() {
|
||
showLoading('Reload Apache...');
|
||
addLog('Apache rechargé', 'success');
|
||
hideLoading();
|
||
location.reload();
|
||
}
|
||
|
||
// Run tests on load
|
||
// runTests();
|
||
</script>
|
||
|
||
|
||
|
||
</body>
|
||
</html>
|