/dev/null', $output, $rc); return $rc === 0 ? trim(implode("\n", $output)) : null; } /** * Check whether a systemd unit is active. * Returns 'active', 'inactive', 'failed', or null when unavailable. */ function systemdStatus(string $unit): ?string { $raw = safeExec("systemctl is-active " . escapeshellarg($unit)); if ($raw === null) return null; return in_array($raw, ['active', 'inactive', 'failed', 'activating', 'deactivating'], true) ? $raw : 'unknown'; } /** * Perform a local HTTP HEAD/GET and return [http_code, latency_ms] or null. */ function localHttpCheck(string $url): ?array { if (!function_exists('curl_init')) return null; $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_NOBODY => true, CURLOPT_TIMEOUT => 5, CURLOPT_CONNECTTIMEOUT => 3, CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => 0, CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 3, ]); $start = microtime(true); curl_exec($ch); $ms = (int) round((microtime(true) - $start) * 1000); $code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE); // curl_close() is a no-op since PHP 8.0 and deprecated in PHP 8.5; omitted. unset($ch); return $code > 0 ? [$code, $ms] : null; } // ── data collection ─────────────────────────────────────────────────────────── $checks = []; // 1. nginx $nginxStatus = systemdStatus('nginx'); $nginxVersion = safeExec('nginx -v 2>&1 | head -1'); $checks['nginx'] = [ 'label' => 'nginx', 'status' => $nginxStatus, 'detail' => $nginxVersion, ]; // 2. php-fpm (try versioned names: php8.2-fpm, php8.3-fpm, php-fpm) $phpFpmStatus = null; $phpFpmUnit = null; foreach (['php8.3-fpm', 'php8.2-fpm', 'php8.1-fpm', 'php-fpm'] as $unit) { $s = systemdStatus($unit); if ($s !== null && $s !== 'unknown') { $phpFpmStatus = $s; $phpFpmUnit = $unit; break; } } $checks['php_fpm'] = [ 'label' => 'php-fpm' . ($phpFpmUnit ? " ($phpFpmUnit)" : ''), 'status' => $phpFpmStatus, 'detail' => null, ]; // 3. Site HTTP ping $siteUrl = (isset($_SERVER['HTTPS']) ? 'https' : 'http') . '://' . ($_SERVER['HTTP_HOST'] ?? 'localhost') . '/'; $httpResult = localHttpCheck($siteUrl); $checks['site_http'] = [ 'label' => 'Site HTTP', 'status' => $httpResult !== null ? ($httpResult[0] < 500 ? 'active' : 'failed') : null, 'detail' => $httpResult !== null ? "HTTP {$httpResult[0]} — {$httpResult[1]} ms" : 'curl indisponible', ]; // 4. Database file require_once APP_ROOT . '/src/Database.php'; $dbPath = APP_ROOT . '/storage/test.db'; $dbExists = file_exists($dbPath); $dbWritable = $dbExists && is_writable($dbPath); $dbSizeBytes = $dbExists ? filesize($dbPath) : null; $dbSizeHuman = $dbSizeBytes !== null ? ($dbSizeBytes > 1048576 ? number_format($dbSizeBytes / 1048576, 1) . ' MB' : number_format($dbSizeBytes / 1024, 1) . ' KB') : 'N/A'; // Quick DB sanity: count rows $dbRowCount = null; if ($dbExists) { try { $db = new Database(); $stmt = $db->getConnection()->query("SELECT COUNT(*) FROM theses"); $dbRowCount = (int) $stmt->fetchColumn(); } catch (Throwable $e) { $dbRowCount = null; } } $checks['database'] = [ 'label' => 'Base de données SQLite', 'status' => $dbExists ? ($dbWritable ? 'active' : 'inactive') : 'failed', 'detail' => $dbExists ? ($dbRowCount !== null ? "$dbRowCount thèses — $dbSizeHuman" : "Lecture impossible — $dbSizeHuman") : 'Fichier introuvable', ]; // 5. Storage directory $storageDir = APP_ROOT . '/storage'; $storageWritable = is_dir($storageDir) && is_writable($storageDir); $bannersDir = $storageDir . '/banners'; $coversDir = $storageDir . '/covers'; $checks['storage'] = [ 'label' => 'Répertoire storage', 'status' => $storageWritable ? 'active' : ($storageDir ? 'inactive' : 'failed'), 'detail' => $storageWritable ? implode(' · ', array_filter([ is_dir($bannersDir) ? ('banners/ ' . count(array_diff(scandir($bannersDir), ['.','..'])) . ' fichiers') : null, is_dir($coversDir) ? ('covers/ ' . count(array_diff(scandir($coversDir), ['.','..'])) . ' fichiers') : null, ])) : 'Non accessible en écriture', ]; // 6. Maintenance mode $maintenanceOn = file_exists(APP_ROOT . '/storage/maintenance.flag'); $checks['maintenance'] = [ 'label' => 'Mode maintenance', 'status' => $maintenanceOn ? 'warn' : 'active', 'detail' => $maintenanceOn ? 'Activé — site public inaccessible' : 'Désactivé', ]; // 7. PHP info $phpInfo = [ 'version' => PHP_VERSION, 'sapi' => PHP_SAPI, 'memory_limit' => ini_get('memory_limit'), 'upload_max' => ini_get('upload_max_filesize'), 'post_max' => ini_get('post_max_size'), 'max_exec' => ini_get('max_execution_time') . 's', ]; // 8. Disk usage (partition containing APP_ROOT) $diskTotal = disk_total_space(APP_ROOT); $diskFree = disk_free_space(APP_ROOT); $diskUsed = $diskTotal - $diskFree; function humanBytes(int $bytes): string { if ($bytes > 1073741824) return number_format($bytes / 1073741824, 1) . ' GB'; if ($bytes > 1048576) return number_format($bytes / 1048576, 1) . ' MB'; return number_format($bytes / 1024, 1) . ' KB'; } $diskPct = $diskTotal > 0 ? (int) round($diskUsed / $diskTotal * 100) : 0; // ── label helpers ───────────────────────────────────────────────────────────── function statusLabel(string $status): string { return match($status) { 'active' => '● En ligne', 'inactive' => '○ Inactif', 'failed' => '✕ Erreur', 'warn' => '⚠ Attention', default => '? Inconnu', }; } function statusClass(string $status): string { return match($status) { 'active' => 'status-ok', 'inactive' => 'status-warn', 'warn' => 'status-warn', 'failed' => 'status-err', default => 'status-unknown', }; } require_once APP_ROOT . '/templates/admin/head.php'; ?>

Statut serveur

Vérification effectuée le Rafraîchir

Services

Environnement PHP

$val): ?>

Espace disque

utilisé (%) libre /