' . htmlspecialchars((string)($translations['purge_admin_access_denied'] ?? 'Accès refusé.'), ENT_QUOTES, 'UTF-8') . '
'; return; } // reauth (10 min) $mustReauth=function(int $ttl=600){$t=(int)($_SESSION['admin_reauth_at']??0);return ($t===0||(time()-$t)>$ttl);}; if ($mustReauth()){ $qs=http_build_query(['page'=>'reauth','redir'=>($_SERVER['REQUEST_URI']??'index.php?page=maintenance')]); header('Location:?'.$qs); exit; } if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } $CSRF = $_SESSION['csrf_token']; $e = static fn($s) => htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); $siteRoot = realpath(__DIR__ . '/../../'); $logsDir = $siteRoot . '/logs'; @is_dir($logsDir) || @mkdir($logsDir, 0755, true); // Fichiers autorisés $files = [ 'php_errors' => $logsDir . '/php_errors.log', 'app' => $logsDir . '/app.log', ]; // Sélection $key = $_GET['file'] ?? 'php_errors'; if (!isset($files[$key])) { $key = 'php_errors'; } $path = $files[$key]; // Actions pré-dispatch $op = $_GET['op'] ?? ''; if ($op === 'download' && hash_equals($CSRF, $_GET['csrf'] ?? '')) { if (is_file($path) && is_readable($path)) { header('Content-Type: text/plain; charset=utf-8'); header('Content-Disposition: attachment; filename="'.$key.'.log"'); readfile($path); } else { header('Content-Type: text/plain; charset=utf-8'); echo (string)($translations['purge_logs_file_unavailable_plain'] ?? 'Fichier indisponible.'); } return; } if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'truncate') { if (!hash_equals($CSRF, $_POST['csrf'] ?? '')) { echo '
' . $e($translations['purge_csrf_invalid'] ?? 'Token CSRF invalide.') . '
'; return; } if (is_file($path) && is_writable($path)) { $fp = @fopen($path, 'w'); if ($fp) { fclose($fp); } } else if (!file_exists($path)) { // crée le fichier si absent @touch($path); } // redirection douce (évite le repost) $qs = http_build_query(['page'=>'logs','file'=>$key]); header('Location: ?'.$qs); return; } // Helpers $humanSize = static function(int $bytes): string { $units = ['B','KB','MB','GB','TB']; $i=0; while ($bytes >= 1024 && $i < count($units)-1) { $bytes/=1024; $i++; } return number_format($bytes, ($i?1:0)).' '.$units[$i]; }; $tail = static function(string $file, int $lines=300, int $maxBytes=1048576): string { if (!is_file($file) || !is_readable($file)) return ''; $size = filesize($file); if ($size === 0) return ''; $bytesToRead = min($size, $maxBytes); $fh = fopen($file, 'rb'); if (!$fh) return ''; // positionne le pointeur en fin-moins-bytesToRead fseek($fh, -$bytesToRead, SEEK_END); $data = fread($fh, $bytesToRead); fclose($fh); // Normalise fin de fichier + découpe $data = ltrim($data); // évite premier morceau tronqué $linesArr = preg_split('/\R/', $data); $linesArr = array_slice($linesArr, -$lines); return implode("\n", $linesArr); }; $exists = is_file($path); $readable = $exists && is_readable($path); $writable = $exists ? is_writable($path) : is_writable(dirname($path)); $size = $exists ? filesize($path) : 0; $mtime = $exists ? date('Y-m-d H:i:s', filemtime($path)) : '—'; $content = $readable ? $tail($path, (int)($_GET['n'] ?? 300)) : ''; ?>

{DIR} (0755) et le fichier (0644).'); echo str_replace('{DIR}', $e($logsDir), $tpl); ?>