Files
wevads-platform/scripts/standalone-index.php
2026-02-26 04:53:11 +01:00

517 lines
25 KiB
PHP
Executable File

<?php
/**
* WEVAL - Index Centralisé des Pages Standalone
* Auto-mise à jour: Scanne automatiquement les fichiers
*/
// Configuration des catégories (patterns de détection)
$categories = [
'monitoring' => [
'name' => 'Administration & Monitoring',
'icon' => 'fa-tachometer-alt',
'color' => '#22d3ee',
'patterns' => ['system-', 'monitoring', 'security', 'session', 'console', 'whitelist', 'api-security', 'quick-access', 'audit']
],
'huawei' => [
'name' => 'Huawei Cloud',
'icon' => 'fa-cloud',
'color' => '#ef4444',
'patterns' => ['huawei-']
],
'office' => [
'name' => 'Office 365 / Microsoft',
'icon' => 'fa-microsoft',
'color' => '#0078d4',
'patterns' => ['office-', 'microsoft-', 'azure-']
],
'cloudflare' => [
'name' => 'Cloudflare & DNS',
'icon' => 'fa-shield-alt',
'color' => '#f59e0b',
'patterns' => ['cloudflare-', 'domain', 'freedns', 'dkim', 'ptr', 'dns-']
],
'tracking' => [
'name' => 'Tracking & Analytics',
'icon' => 'fa-chart-line',
'color' => '#10b981',
'patterns' => ['tracking-', 'stats-', 'analytics-']
],
'email' => [
'name' => 'Email & Delivery',
'icon' => 'fa-envelope',
'color' => '#8b5cf6',
'patterns' => ['email-', 'smtp-', 'deliverability', 'bounce-', 'warmup-', 'pmta-']
],
'database' => [
'name' => 'Base de Données',
'icon' => 'fa-database',
'color' => '#06b6d4',
'patterns' => ['adminer', 'database-', 'db-', 'test-bd', 'sql-', 'postgres-']
],
'chatbot' => [
'name' => 'Chatbot & AI',
'icon' => 'fa-robot',
'color' => '#ec4899',
'patterns' => ['chatbot-', 'ai-', 'ollama-', 'gpt-']
],
'tools' => [
'name' => 'Outils & Utilitaires',
'icon' => 'fa-tools',
'color' => '#64748b',
'patterns' => ['tool-', 'util-', 'helper-', 'check', 's3_', 'upload', 'import', 'export']
],
'docs' => [
'name' => 'Documentation',
'icon' => 'fa-book',
'color' => '#a855f7',
'patterns' => ['doc', 'architecture', 'help-', 'guide-', 'readme']
],
'auth' => [
'name' => 'Authentification',
'icon' => 'fa-lock',
'color' => '#eab308',
'patterns' => ['login', 'auth-', 'logout', 'password', 'register']
],
'test' => [
'name' => 'Tests & Debug',
'icon' => 'fa-flask',
'color' => '#f97316',
'patterns' => ['test', 'debug', 'phpinfo', 'error_', 'check-']
],
];
// Fichiers à exclure
$excludeFiles = [
'index.php',
'standalone-index.php',
'.htaccess',
'click.php', // 404
'lead.php', // 404
'open.php', // endpoint tracking
'unsub.php', // endpoint tracking
];
// Fichiers endpoints (tracking) - à afficher séparément
$endpointFiles = ['open.php', 'unsub.php', 'click.php', 'lead.php', 'pixel.php', 'redirect.php'];
// Scanner le répertoire
function scanPublicFiles($dir, $excludeFiles) {
$files = [];
$items = scandir($dir);
foreach ($items as $item) {
if ($item[0] === '.') continue;
$path = $dir . '/' . $item;
if (is_file($path)) {
$ext = pathinfo($item, PATHINFO_EXTENSION);
if (in_array($ext, ['php', 'html']) && !in_array($item, $excludeFiles)) {
$files[] = [
'name' => $item,
'path' => $path,
'ext' => $ext,
'size' => filesize($path),
'modified' => filemtime($path),
];
}
}
}
return $files;
}
// Catégoriser un fichier
function categorizeFile($filename, $categories) {
$filename_lower = strtolower($filename);
foreach ($categories as $key => $cat) {
foreach ($cat['patterns'] as $pattern) {
if (strpos($filename_lower, strtolower($pattern)) !== false) {
return $key;
}
}
}
return 'other';
}
// Vérifier l'accessibilité
function checkFileAccessibility($filename) {
$url = "http://127.0.0.1:5821/" . $filename;
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_NOBODY => true,
CURLOPT_TIMEOUT => 2,
CURLOPT_FOLLOWLOCATION => false,
]);
curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return $code;
}
// Générer une description basée sur le nom
function generateDescription($filename) {
$name = pathinfo($filename, PATHINFO_FILENAME);
$name = str_replace(['-', '_'], ' ', $name);
$name = ucwords($name);
return $name;
}
// Scanner les fichiers
$publicDir = __DIR__;
$allFiles = scanPublicFiles($publicDir, $excludeFiles);
// Organiser par catégorie
$categorizedFiles = [];
foreach ($categories as $key => $cat) {
$categorizedFiles[$key] = [];
}
$categorizedFiles['other'] = [];
foreach ($allFiles as $file) {
$category = categorizeFile($file['name'], $categories);
$file['description'] = generateDescription($file['name']);
$categorizedFiles[$category][] = $file;
}
// Trier chaque catégorie
foreach ($categorizedFiles as &$files) {
usort($files, function($a, $b) {
return strcmp($a['name'], $b['name']);
});
}
// Statistiques
$totalFiles = count($allFiles);
$lastUpdate = date('d/m/Y H:i:s');
$serverIp = '89.167.40.150';
$port = '5821';
// AJAX pour vérification status
if (isset($_GET['check']) && isset($_GET['file'])) {
header('Content-Type: application/json');
$code = checkFileAccessibility($_GET['file']);
echo json_encode(['code' => $code, 'status' => ($code == 200 || $code == 302) ? 'ok' : 'error']);
exit;
}
// Recherche
$searchQuery = $_GET['q'] ?? '';
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WEVAL - Index des Pages</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Segoe UI', sans-serif; background: linear-gradient(135deg, #0f172a, #1e293b); min-height: 100vh; color: #e2e8f0; }
.header { background: linear-gradient(135deg, #0891b2, #0e7490); padding: 20px 30px; position: sticky; top: 0; z-index: 100; }
.header-content { max-width: 1600px; margin: 0 auto; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 15px; }
.header h1 { font-size: 24px; display: flex; align-items: center; gap: 12px; }
.header-stats { display: flex; gap: 20px; align-items: center; }
.stat-pill { background: rgba(255,255,255,0.15); padding: 8px 16px; border-radius: 20px; font-size: 13px; display: flex; align-items: center; gap: 8px; }
.stat-pill i { color: #22d3ee; }
.search-box { display: flex; gap: 10px; }
.search-box input { padding: 10px 15px; border-radius: 8px; border: none; background: rgba(255,255,255,0.1); color: #fff; width: 250px; font-size: 14px; }
.search-box input::placeholder { color: rgba(255,255,255,0.5); }
.search-box input:focus { outline: none; background: rgba(255,255,255,0.2); }
.btn { padding: 10px 18px; border: none; border-radius: 8px; cursor: pointer; font-size: 13px; font-weight: 600; text-decoration: none; display: inline-flex; align-items: center; gap: 8px; transition: all 0.2s; }
.btn-primary { background: rgba(255,255,255,0.15); color: #fff; }
.btn-primary:hover { background: rgba(255,255,255,0.25); }
.container { max-width: 1600px; margin: 0 auto; padding: 25px; }
.quick-links { display: flex; gap: 10px; margin-bottom: 25px; flex-wrap: wrap; }
.quick-link { padding: 8px 16px; background: linear-gradient(135deg, #1e293b, #334155); border-radius: 8px; color: #94a3b8; text-decoration: none; font-size: 12px; border: 1px solid rgba(255,255,255,0.1); transition: all 0.2s; display: flex; align-items: center; gap: 6px; }
.quick-link:hover { border-color: #22d3ee; color: #22d3ee; }
.quick-link i { font-size: 14px; }
.category { margin-bottom: 25px; }
.category-header { display: flex; align-items: center; gap: 12px; padding: 15px 20px; background: linear-gradient(135deg, #1e293b, #334155); border-radius: 12px 12px 0 0; border: 1px solid rgba(255,255,255,0.1); border-bottom: none; cursor: pointer; }
.category-header:hover { background: linear-gradient(135deg, #334155, #475569); }
.category-icon { width: 40px; height: 40px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 18px; }
.category-title { font-size: 16px; font-weight: 600; flex: 1; }
.category-count { background: rgba(255,255,255,0.1); padding: 4px 12px; border-radius: 12px; font-size: 12px; color: #94a3b8; }
.category-toggle { color: #64748b; transition: transform 0.3s; }
.category-toggle.collapsed { transform: rotate(-90deg); }
.category-content { background: rgba(15, 23, 42, 0.5); border: 1px solid rgba(255,255,255,0.1); border-top: none; border-radius: 0 0 12px 12px; overflow: hidden; }
.category-content.hidden { display: none; }
.file-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1px; background: rgba(255,255,255,0.05); }
.file-item { background: #1e293b; padding: 15px; display: flex; align-items: center; gap: 12px; transition: all 0.2s; }
.file-item:hover { background: #334155; }
.file-icon { width: 36px; height: 36px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 14px; flex-shrink: 0; }
.file-icon.php { background: linear-gradient(135deg, #8b5cf6, #7c3aed); }
.file-icon.html { background: linear-gradient(135deg, #f59e0b, #d97706); }
.file-info { flex: 1; min-width: 0; }
.file-name { font-size: 13px; font-weight: 600; color: #f8fafc; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.file-desc { font-size: 11px; color: #64748b; margin-top: 2px; }
.file-meta { font-size: 10px; color: #475569; margin-top: 4px; display: flex; gap: 10px; }
.file-actions { display: flex; gap: 6px; }
.file-btn { width: 32px; height: 32px; border-radius: 6px; border: none; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 12px; transition: all 0.2s; }
.file-btn-open { background: #10b981; color: #fff; }
.file-btn-open:hover { background: #059669; }
.file-btn-copy { background: #3b82f6; color: #fff; }
.file-btn-copy:hover { background: #2563eb; }
.file-btn-new { background: #8b5cf6; color: #fff; }
.file-btn-new:hover { background: #7c3aed; }
.status-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.status-ok { background: #10b981; }
.status-error { background: #ef4444; }
.status-checking { background: #f59e0b; animation: pulse 1s infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
.empty-category { padding: 30px; text-align: center; color: #64748b; font-size: 13px; }
.footer { text-align: center; padding: 30px; color: #64748b; font-size: 12px; }
.toast { position: fixed; bottom: 20px; right: 20px; background: #10b981; color: #fff; padding: 12px 20px; border-radius: 8px; font-size: 13px; display: none; z-index: 1000; }
.toast.show { display: block; animation: slideIn 0.3s ease; }
@keyframes slideIn { from { transform: translateX(100px); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
.new-badge { background: #ef4444; color: #fff; font-size: 9px; padding: 2px 6px; border-radius: 4px; margin-left: 8px; animation: pulse 2s infinite; }
@media (max-width: 768px) {
.header-content { flex-direction: column; text-align: center; }
.file-grid { grid-template-columns: 1fr; }
.search-box input { width: 100%; }
}
</style>
</head>
<body>
<div class="header">
<div class="header-content">
<h1><i class="fas fa-th-large"></i> WEVAL - Index des Pages</h1>
<div class="header-stats">
<div class="stat-pill"><i class="fas fa-file-code"></i> <?= $totalFiles ?> fichiers</div>
<div class="stat-pill"><i class="fas fa-clock"></i> <?= $lastUpdate ?></div>
</div>
<div class="search-box">
<input type="text" id="searchInput" placeholder="Rechercher..." value="<?= htmlspecialchars($searchQuery) ?>">
<a href="dashboard.html" class="btn btn-primary"><i class="fas fa-home"></i> Dashboard</a>
<button class="btn btn-primary" onclick="checkAllStatus()"><i class="fas fa-sync"></i> Vérifier</button>
</div>
</div>
</div>
<div class="container">
<!-- Quick Links -->
<div class="quick-links">
<a href="system-monitoring.php" class="quick-link"><i class="fas fa-chart-line" style="color:#22d3ee"></i> Monitoring</a>
<a href="documentation.php" class="quick-link"><i class="fas fa-book" style="color:#a855f7"></i> Documentation</a>
<a href="huawei-accounts.php" class="quick-link"><i class="fas fa-cloud" style="color:#ef4444"></i> Huawei</a>
<a href="office-management.php" class="quick-link"><i class="fab fa-microsoft" style="color:#0078d4"></i> Office 365</a>
<a href="cloudflare-accounts.php" class="quick-link"><i class="fas fa-shield-alt" style="color:#f59e0b"></i> Cloudflare</a>
<a href="tracking-dashboard.php" class="quick-link"><i class="fas fa-chart-bar" style="color:#10b981"></i> Tracking</a>
<a href="wevupadminer.php" class="quick-link"><i class="fas fa-database" style="color:#06b6d4"></i> Adminer</a>
<a href="security-report.php" class="quick-link"><i class="fas fa-shield-alt" style="color:#ef4444"></i> Sécurité</a>
</div>
<!-- Categories -->
<?php foreach ($categories as $catKey => $catInfo): ?>
<?php if (!empty($categorizedFiles[$catKey])): ?>
<div class="category" data-category="<?= $catKey ?>">
<div class="category-header" onclick="toggleCategory('<?= $catKey ?>')">
<div class="category-icon" style="background: <?= $catInfo['color'] ?>20; color: <?= $catInfo['color'] ?>">
<i class="fas <?= $catInfo['icon'] ?>"></i>
</div>
<div class="category-title"><?= $catInfo['name'] ?></div>
<div class="category-count"><?= count($categorizedFiles[$catKey]) ?> fichiers</div>
<i class="fas fa-chevron-down category-toggle" id="toggle-<?= $catKey ?>"></i>
</div>
<div class="category-content" id="content-<?= $catKey ?>">
<div class="file-grid">
<?php foreach ($categorizedFiles[$catKey] as $file): ?>
<?php
$isNew = (time() - $file['modified']) < 86400; // Nouveau si < 24h
$sizeKb = round($file['size'] / 1024, 1);
?>
<div class="file-item" data-filename="<?= htmlspecialchars($file['name']) ?>">
<div class="status-dot status-checking" id="status-<?= md5($file['name']) ?>"></div>
<div class="file-icon <?= $file['ext'] ?>">
<i class="fas <?= $file['ext'] === 'php' ? 'fa-php' : 'fa-code' ?>"></i>
</div>
<div class="file-info">
<div class="file-name">
<?= htmlspecialchars($file['name']) ?>
<?php if ($isNew): ?><span class="new-badge">NEW</span><?php endif; ?>
</div>
<div class="file-desc"><?= htmlspecialchars($file['description']) ?></div>
<div class="file-meta">
<span><i class="fas fa-weight"></i> <?= $sizeKb ?> KB</span>
<span><i class="fas fa-clock"></i> <?= date('d/m H:i', $file['modified']) ?></span>
</div>
</div>
<div class="file-actions">
<a href="<?= htmlspecialchars($file['name']) ?>" class="file-btn file-btn-open" title="Ouvrir"><i class="fas fa-external-link-alt"></i></a>
<button class="file-btn file-btn-copy" onclick="copyUrl('<?= htmlspecialchars($file['name']) ?>')" title="Copier URL"><i class="fas fa-copy"></i></button>
<a href="<?= htmlspecialchars($file['name']) ?>" target="_blank" class="file-btn file-btn-new" title="Nouvel onglet"><i class="fas fa-plus"></i></a>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
<?php endif; ?>
<?php endforeach; ?>
<!-- Other (non catégorisé) -->
<?php if (!empty($categorizedFiles['other'])): ?>
<div class="category" data-category="other">
<div class="category-header" onclick="toggleCategory('other')">
<div class="category-icon" style="background: rgba(100,116,139,0.2); color: #64748b">
<i class="fas fa-folder"></i>
</div>
<div class="category-title">Autres</div>
<div class="category-count"><?= count($categorizedFiles['other']) ?> fichiers</div>
<i class="fas fa-chevron-down category-toggle" id="toggle-other"></i>
</div>
<div class="category-content" id="content-other">
<div class="file-grid">
<?php foreach ($categorizedFiles['other'] as $file): ?>
<?php
$isNew = (time() - $file['modified']) < 86400;
$sizeKb = round($file['size'] / 1024, 1);
?>
<div class="file-item" data-filename="<?= htmlspecialchars($file['name']) ?>">
<div class="status-dot status-checking" id="status-<?= md5($file['name']) ?>"></div>
<div class="file-icon <?= $file['ext'] ?>">
<i class="fas <?= $file['ext'] === 'php' ? 'fa-php' : 'fa-code' ?>"></i>
</div>
<div class="file-info">
<div class="file-name">
<?= htmlspecialchars($file['name']) ?>
<?php if ($isNew): ?><span class="new-badge">NEW</span><?php endif; ?>
</div>
<div class="file-desc"><?= htmlspecialchars($file['description']) ?></div>
<div class="file-meta">
<span><i class="fas fa-weight"></i> <?= $sizeKb ?> KB</span>
<span><i class="fas fa-clock"></i> <?= date('d/m H:i', $file['modified']) ?></span>
</div>
</div>
<div class="file-actions">
<a href="<?= htmlspecialchars($file['name']) ?>" class="file-btn file-btn-open" title="Ouvrir"><i class="fas fa-external-link-alt"></i></a>
<button class="file-btn file-btn-copy" onclick="copyUrl('<?= htmlspecialchars($file['name']) ?>')" title="Copier URL"><i class="fas fa-copy"></i></button>
<a href="<?= htmlspecialchars($file['name']) ?>" target="_blank" class="file-btn file-btn-new" title="Nouvel onglet"><i class="fas fa-plus"></i></a>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
<?php endif; ?>
<div class="footer">
<p>Index auto-généré • <?= $totalFiles ?> fichiers détectés • Serveur: <?= $serverIp ?>:<?= $port ?></p>
<p style="margin-top:5px;">Dernière mise à jour: <?= $lastUpdate ?></p>
</div>
</div>
<div class="toast" id="toast">URL copiée!</div>
<script>
const baseUrl = 'http://<?= $serverIp ?>:<?= $port ?>/';
// Toggle catégorie
function toggleCategory(cat) {
const content = document.getElementById('content-' + cat);
const toggle = document.getElementById('toggle-' + cat);
content.classList.toggle('hidden');
toggle.classList.toggle('collapsed');
}
// Copier URL
function copyUrl(filename) {
const url = baseUrl + filename;
navigator.clipboard.writeText(url).then(() => {
showToast('URL copiée: ' + filename);
});
}
// Toast
function showToast(msg) {
const toast = document.getElementById('toast');
toast.textContent = msg;
toast.classList.add('show');
setTimeout(() => toast.classList.remove('show'), 2000);
}
// Vérifier status d'un fichier
async function checkStatus(filename) {
const hash = '<?php echo "' + md5('" + filename + "') + '"; ?>';
// Simplified: use md5 hash calculation in JS
const statusDot = document.querySelector(`[data-filename="${filename}"] .status-dot`);
if (!statusDot) return;
try {
const response = await fetch(`?check=1&file=${encodeURIComponent(filename)}`);
const data = await response.json();
statusDot.className = 'status-dot ' + (data.status === 'ok' ? 'status-ok' : 'status-error');
} catch (e) {
statusDot.className = 'status-dot status-error';
}
}
// Vérifier tous les status
async function checkAllStatus() {
const items = document.querySelectorAll('.file-item');
for (const item of items) {
const filename = item.dataset.filename;
await checkStatus(filename);
await new Promise(r => setTimeout(r, 50)); // Délai pour éviter surcharge
}
showToast('Vérification terminée!');
}
// Recherche
document.getElementById('searchInput').addEventListener('input', function(e) {
const query = e.target.value.toLowerCase();
document.querySelectorAll('.file-item').forEach(item => {
const filename = item.dataset.filename.toLowerCase();
const match = filename.includes(query);
item.style.display = match ? 'flex' : 'none';
});
// Afficher/masquer catégories vides
document.querySelectorAll('.category').forEach(cat => {
const visibleItems = cat.querySelectorAll('.file-item[style="display: flex"], .file-item:not([style])');
const hasVisible = Array.from(cat.querySelectorAll('.file-item')).some(item =>
item.style.display !== 'none'
);
cat.style.display = hasVisible ? 'block' : 'none';
});
});
// Vérification initiale au chargement
document.addEventListener('DOMContentLoaded', () => {
// Vérifier les 10 premiers fichiers automatiquement
const items = Array.from(document.querySelectorAll('.file-item')).slice(0, 10);
items.forEach((item, i) => {
setTimeout(() => checkStatus(item.dataset.filename), i * 100);
});
});
</script>
<div id="chatbot-container"></div>
<link rel="stylesheet" href="/styles/chatbot.css">
<script src="/js/chatbot.js"></script>
<script>document.addEventListener("DOMContentLoaded", function() { WEVAL_Chat.init(); });</script>
<?php include("includes/chatbot-widget.php"); ?>
</body>
</html>