33800 Docs

← Retour
<?php /** * Claude Docs Viewer - Markdown viewer for claude directory * Uses Parsedown for rendering */ require_once __DIR__ . '/vendor/Parsedown.php'; $base_path = '/home/deas8499/backup/configs'; $request = $_GET['path'] ?? ''; $full_path = realpath($base_path . '/' . $request); // Security: ensure path is within base_path and not secrets if ($full_path === false || strpos($full_path, $base_path) !== 0) { $full_path = $base_path; $request = ''; } // Block access to secrets folder if (strpos($full_path, '/secrets') !== false) { $full_path = $base_path; $request = ''; } // Get breadcrumb parts $parts = array_filter(explode('/', $request)); $breadcrumbs = []; $current_path = ''; foreach ($parts as $part) { $current_path .= '/' . $part; $breadcrumbs[] = ['name' => $part, 'path' => $current_path]; } // Is it a file or directory? $is_file = is_file($full_path); $is_markdown = $is_file && preg_match('/\.md$/i', $full_path); $content = ''; $files = []; $dirs = []; /** * Parse filename to extract date and title * Format: JJ-MM-AAAA-HH-MM-description.md or JJ-MM-AAAA-description.md */ function parseFileName($filename) { $name = preg_replace('/\.(md|txt)$/i', '', $filename); // Format: JJ-MM-AAAA-HH-MM-description if (preg_match('/^(\d{2})-(\d{2})-(\d{4})-(\d{2})-(\d{2})-(.+)$/', $name, $m)) { $timestamp = mktime((int)$m[4], (int)$m[5], 0, (int)$m[2], (int)$m[1], (int)$m[3]); $title = str_replace('-', ' ', $m[6]); $title = ucfirst($title); return [ 'timestamp' => $timestamp, 'date' => "{$m[1]}/{$m[2]}/{$m[3]} {$m[4]}:{$m[5]}", 'title' => $title, 'has_date' => true ]; } // Format: JJ-MM-AAAA-description if (preg_match('/^(\d{2})-(\d{2})-(\d{4})-(.+)$/', $name, $m)) { $timestamp = mktime(0, 0, 0, (int)$m[2], (int)$m[1], (int)$m[3]); $title = str_replace('-', ' ', $m[4]); $title = ucfirst($title); return [ 'timestamp' => $timestamp, 'date' => "{$m[1]}/{$m[2]}/{$m[3]}", 'title' => $title, 'has_date' => true ]; } // No date pattern - return as is return [ 'timestamp' => 0, 'date' => '', 'title' => $name, 'has_date' => false ]; } if ($is_file) { $content = file_get_contents($full_path); if ($is_markdown) { $Parsedown = new Parsedown(); $Parsedown->setSafeMode(true); $content = $Parsedown->text($content); } } else { // List directory $items = scandir($full_path); foreach ($items as $item) { if ($item === '.' || $item === '..') continue; if ($item === '.git' || $item === 'secrets') continue; $item_path = $full_path . '/' . $item; if (is_dir($item_path)) { $dirs[] = $item; } else { $parsed = parseFileName($item); $parsed['filename'] = $item; $parsed['is_md'] = preg_match('/\.md$/i', $item); $files[] = $parsed; } } sort($dirs); // Sort files by timestamp descending (newest first) usort($files, function($a, $b) { if ($a['has_date'] && $b['has_date']) { return $b['timestamp'] - $a['timestamp']; } if ($a['has_date']) return -1; if ($b['has_date']) return 1; return strcmp($a['filename'], $b['filename']); }); } $page_title = $request ?: '33800 Docs'; ?> <!DOCTYPE html> <html lang="fr"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title><?= htmlspecialchars($page_title) ?> - 33800 Docs</title> <style> :root { --bg: #1a1a2e; --bg-card: #16213e; --text: #e4e4e4; --text-dim: #8892b0; --accent: #64ffda; --accent-dim: #0a6959; --border: #233554; } * { box-sizing: border-box; } body { font-family: 'Segoe UI', system-ui, sans-serif; background: var(--bg); color: var(--text); margin: 0; padding: 20px; line-height: 1.6; } .container { max-width: 1000px; margin: 0 auto; } header { margin-bottom: 20px; padding-bottom: 15px; border-bottom: 1px solid var(--border); } h1 { margin: 0 0 10px 0; font-size: 1.8em; color: var(--accent); } .breadcrumb { color: var(--text-dim); font-size: 0.9em; } .breadcrumb a { color: var(--accent); text-decoration: none; } .breadcrumb a:hover { text-decoration: underline; } .breadcrumb span { margin: 0 8px; } /* Directory listing */ .listing { display: grid; gap: 8px; } .item { background: var(--bg-card); border: 1px solid var(--border); border-radius: 6px; padding: 12px 16px; display: flex; align-items: center; gap: 12px; transition: border-color 0.2s; } .item:hover { border-color: var(--accent-dim); } .item a { color: var(--text); text-decoration: none; flex: 1; } .item a:hover { color: var(--accent); } .icon { font-size: 1.2em; width: 24px; text-align: center; flex-shrink: 0; } .dir .icon { color: #ffd700; } .file .icon { color: var(--text-dim); } .md .icon { color: var(--accent); } /* File item with date */ .file-info { flex: 1; display: flex; flex-direction: column; gap: 2px; } .file-title { color: var(--text); text-decoration: none; font-weight: 500; } .file-title:hover { color: var(--accent); } .file-date { font-size: 0.8em; color: var(--text-dim); font-family: monospace; } /* Markdown content */ .markdown-body { background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; padding: 30px; } .markdown-body h1, .markdown-body h2, .markdown-body h3 { color: var(--accent); border-bottom: 1px solid var(--border); padding-bottom: 8px; } .markdown-body h1 { font-size: 1.8em; } .markdown-body h2 { font-size: 1.4em; } .markdown-body h3 { font-size: 1.2em; } .markdown-body a { color: var(--accent); } .markdown-body code { background: var(--bg); padding: 2px 6px; border-radius: 4px; font-size: 0.9em; } .markdown-body pre { background: var(--bg); padding: 16px; border-radius: 6px; overflow-x: auto; } .markdown-body pre code { background: none; padding: 0; } .markdown-body table { width: 100%; border-collapse: collapse; margin: 16px 0; } .markdown-body th, .markdown-body td { border: 1px solid var(--border); padding: 8px 12px; text-align: left; } .markdown-body th { background: var(--bg); } .markdown-body blockquote { border-left: 4px solid var(--accent); margin: 16px 0; padding-left: 16px; color: var(--text-dim); } .markdown-body ul, .markdown-body ol { padding-left: 24px; } /* Raw content */ .raw-content { background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; padding: 20px; white-space: pre-wrap; font-family: monospace; overflow-x: auto; } .back-link { display: inline-block; margin-bottom: 15px; color: var(--accent); text-decoration: none; } .back-link:hover { text-decoration: underline; } footer { margin-top: 30px; padding-top: 15px; border-top: 1px solid var(--border); color: var(--text-dim); font-size: 0.85em; text-align: center; } /* Status badges */ .badge { font-size: 0.7em; padding: 2px 8px; border-radius: 10px; margin-left: 8px; font-weight: normal; } .badge-count { background: var(--accent-dim); color: var(--accent); } </style> </head> <body> <div class="container"> <header> <h1>33800 Docs</h1> <nav class="breadcrumb"> <a href="?path=">root</a> <?php foreach ($breadcrumbs as $bc): ?> <span>/</span> <a href="?path=<?= urlencode($bc['path']) ?>"><?= htmlspecialchars($bc['name']) ?></a> <?php endforeach; ?> </nav> </header> <?php if ($is_file): ?> <a href="?path=<?= urlencode(dirname($request)) ?>" class="back-link">← Retour</a> <?php if ($is_markdown): ?> <div class="markdown-body"> <?= $content ?> </div> <?php else: ?> <div class="raw-content"><?= htmlspecialchars($content) ?></div> <?php endif; ?> <?php else: ?> <div class="listing"> <?php foreach ($dirs as $dir): ?> <?php // Count items in directory $dir_path = $full_path . '/' . $dir; $count = count(array_filter(scandir($dir_path), fn($f) => $f !== '.' && $f !== '..' && $f !== '.git')); ?> <div class="item dir"> <span class="icon">📁</span> <a href="?path=<?= urlencode($request . '/' . $dir) ?>"> <?= htmlspecialchars($dir) ?> <span class="badge badge-count"><?= $count ?></span> </a> </div> <?php endforeach; ?> <?php foreach ($files as $file): ?> <div class="item file <?= $file['is_md'] ? 'md' : '' ?>"> <span class="icon"><?= $file['is_md'] ? '📄' : '📋' ?></span> <div class="file-info"> <a href="?path=<?= urlencode($request . '/' . $file['filename']) ?>" class="file-title"> <?= htmlspecialchars($file['title']) ?> </a> <?php if ($file['has_date']): ?> <span class="file-date"><?= $file['date'] ?></span> <?php endif; ?> </div> </div> <?php endforeach; ?> <?php if (empty($dirs) && empty($files)): ?> <p style="color: var(--text-dim);">Dossier vide</p> <?php endif; ?> </div> <?php endif; ?> <footer> 33800 Docs Viewer · Données synchronisées depuis PVE toutes les 2h </footer> </div> </body> </html>