Spaces:
Sleeping
Sleeping
Update public/admin.html
Browse files- public/admin.html +133 -4
public/admin.html
CHANGED
|
@@ -1,4 +1,130 @@
|
|
| 1 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
let adminCode = '';
|
| 3 |
let charts = {};
|
| 4 |
let refreshInterval;
|
|
@@ -131,10 +257,10 @@
|
|
| 131 |
const now = Date.now();
|
| 132 |
const windowStart = now - WINDOW_MS;
|
| 133 |
|
| 134 |
-
// --- CPU : fusionner l'historique +
|
| 135 |
const cpuMap = new Map();
|
| 136 |
data.cpuHistory.forEach(m => cpuMap.set(m.timestamp, m.cpu));
|
| 137 |
-
// Ajouter le point actuel si dispo
|
| 138 |
if (data.currentCpu !== undefined) cpuMap.set(now, data.currentCpu);
|
| 139 |
|
| 140 |
const cpuLabels = [];
|
|
@@ -254,7 +380,10 @@
|
|
| 254 |
document.getElementById(tabId).classList.add('active');
|
| 255 |
}
|
| 256 |
|
|
|
|
| 257 |
document.getElementById('adminCode')?.addEventListener('keypress', (e) => {
|
| 258 |
if (e.key === 'Enter') login();
|
| 259 |
});
|
| 260 |
-
</script>
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="fr">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Admin Panel - Translator API</title>
|
| 7 |
+
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
|
| 8 |
+
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@3.0.0/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
|
| 9 |
+
<style>
|
| 10 |
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
| 11 |
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f0f2f5; min-height: 100vh; }
|
| 12 |
+
.header { background: #1a1a2e; color: white; padding: 20px 30px; display: flex; justify-content: space-between; align-items: center; }
|
| 13 |
+
.header h1 { font-size: 24px; }
|
| 14 |
+
.logout-btn { background: #e94560; color: white; border: none; padding: 10px 20px; border-radius: 6px; cursor: pointer; }
|
| 15 |
+
.container { max-width: 1400px; margin: 0 auto; padding: 20px; }
|
| 16 |
+
.tabs { display: flex; gap: 10px; margin-bottom: 20px; background: white; border-radius: 8px; padding: 5px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
|
| 17 |
+
.tab { padding: 12px 24px; background: none; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 500; color: #666; transition: all 0.2s; }
|
| 18 |
+
.tab.active { background: #1a73e8; color: white; }
|
| 19 |
+
.tab:hover:not(.active) { background: #f0f0f0; }
|
| 20 |
+
.tab-content { display: none; }
|
| 21 |
+
.tab-content.active { display: block; }
|
| 22 |
+
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); gap: 20px; margin-bottom: 20px; }
|
| 23 |
+
.card { background: white; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); padding: 20px; }
|
| 24 |
+
.card h3 { margin-bottom: 15px; color: #333; font-size: 16px; }
|
| 25 |
+
.chart-container { height: 300px; position: relative; }
|
| 26 |
+
.workers-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); gap: 15px; }
|
| 27 |
+
.worker-card { background: #f8f9fa; border-radius: 8px; padding: 15px; border: 1px solid #e9ecef; }
|
| 28 |
+
.worker-card h4 { color: #1a73e8; margin-bottom: 10px; }
|
| 29 |
+
.metric { display: flex; justify-content: space-between; margin: 8px 0; font-size: 14px; }
|
| 30 |
+
.metric-label { color: #666; }
|
| 31 |
+
.metric-value { font-weight: 600; }
|
| 32 |
+
.status { display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-right: 8px; }
|
| 33 |
+
.status.online { background: #28a745; }
|
| 34 |
+
.status.offline { background: #dc3545; }
|
| 35 |
+
.logs-table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
| 36 |
+
.logs-table th, .logs-table td { padding: 10px; text-align: left; border-bottom: 1px solid #eee; }
|
| 37 |
+
.logs-table th { background: #f8f9fa; font-weight: 600; color: #333; }
|
| 38 |
+
.logs-table tr:hover { background: #f8f9fa; }
|
| 39 |
+
.logs-table .text-cell { max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
| 40 |
+
.loading { text-align: center; padding: 40px; color: #666; }
|
| 41 |
+
.auth-modal { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 1000; }
|
| 42 |
+
.auth-modal.hidden { display: none; }
|
| 43 |
+
.auth-box { background: white; padding: 30px; border-radius: 12px; width: 100%; max-width: 400px; box-shadow: 0 10px 40px rgba(0,0,0,0.2); }
|
| 44 |
+
.auth-box h2 { margin-bottom: 20px; color: #333; }
|
| 45 |
+
.auth-box input { width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 16px; margin-bottom: 15px; }
|
| 46 |
+
.auth-box button { width: 100%; padding: 12px; background: #1a73e8; color: white; border: none; border-radius: 6px; font-size: 16px; cursor: pointer; }
|
| 47 |
+
.auth-box button:hover { background: #1557b0; }
|
| 48 |
+
.error-msg { color: #dc3545; margin-bottom: 15px; display: none; }
|
| 49 |
+
</style>
|
| 50 |
+
</head>
|
| 51 |
+
<body>
|
| 52 |
+
<div class="auth-modal" id="authModal">
|
| 53 |
+
<div class="auth-box">
|
| 54 |
+
<h2>🔐 Accès Admin</h2>
|
| 55 |
+
<div class="error-msg" id="authError"></div>
|
| 56 |
+
<input type="password" id="adminCode" placeholder="Code d'accès admin" autocomplete="off">
|
| 57 |
+
<button onclick="login()">Accéder</button>
|
| 58 |
+
</div>
|
| 59 |
+
</div>
|
| 60 |
+
|
| 61 |
+
<header class="header">
|
| 62 |
+
<h1>📊 Panel Admin - Translator API</h1>
|
| 63 |
+
<button class="logout-btn" onclick="logout()">Déconnexion</button>
|
| 64 |
+
</header>
|
| 65 |
+
|
| 66 |
+
<div class="container">
|
| 67 |
+
<div class="tabs">
|
| 68 |
+
<button class="tab active" onclick="switchTab('machines')">🖥️ Machines</button>
|
| 69 |
+
<button class="tab" onclick="switchTab('requests')">📈 Requêtes (120s)</button>
|
| 70 |
+
<button class="tab" onclick="switchTab('logs')">📋 Logs</button>
|
| 71 |
+
</div>
|
| 72 |
+
|
| 73 |
+
<div id="machines" class="tab-content active">
|
| 74 |
+
<div class="grid">
|
| 75 |
+
<div class="card">
|
| 76 |
+
<h3>����️ Machine Centrale - CPU</h3>
|
| 77 |
+
<div class="chart-container"><canvas id="cpuChart"></canvas></div>
|
| 78 |
+
</div>
|
| 79 |
+
<div class="card">
|
| 80 |
+
<h3>💾 Machine Centrale - RAM</h3>
|
| 81 |
+
<div class="chart-container"><canvas id="ramChart"></canvas></div>
|
| 82 |
+
</div>
|
| 83 |
+
</div>
|
| 84 |
+
<div class="card">
|
| 85 |
+
<h3>👥 Workers Connectés</h3>
|
| 86 |
+
<div class="workers-list" id="workersList">
|
| 87 |
+
<div class="loading">Chargement...</div>
|
| 88 |
+
</div>
|
| 89 |
+
</div>
|
| 90 |
+
</div>
|
| 91 |
+
|
| 92 |
+
<div id="requests" class="tab-content">
|
| 93 |
+
<div class="grid">
|
| 94 |
+
<div class="card">
|
| 95 |
+
<h3>📊 Requêtes par minute (120s)</h3>
|
| 96 |
+
<div class="chart-container"><canvas id="requestsChart"></canvas></div>
|
| 97 |
+
</div>
|
| 98 |
+
<div class="card">
|
| 99 |
+
<h3>📈 Statistiques</h3>
|
| 100 |
+
<div id="requestStats" style="padding: 10px;">Chargement...</div>
|
| 101 |
+
</div>
|
| 102 |
+
</div>
|
| 103 |
+
</div>
|
| 104 |
+
|
| 105 |
+
<div id="logs" class="tab-content">
|
| 106 |
+
<div class="card">
|
| 107 |
+
<h3>📋 Derniers logs de traduction</h3>
|
| 108 |
+
<table class="logs-table">
|
| 109 |
+
<thead>
|
| 110 |
+
<tr>
|
| 111 |
+
<th>Heure</th>
|
| 112 |
+
<th>IP</th>
|
| 113 |
+
<th>Durée (ms)</th>
|
| 114 |
+
<th>Langues</th>
|
| 115 |
+
<th>Entrée</th>
|
| 116 |
+
<th>Sortie</th>
|
| 117 |
+
</tr>
|
| 118 |
+
</thead>
|
| 119 |
+
<tbody id="logsBody">
|
| 120 |
+
<tr><td colspan="6" class="loading">Chargement...</td></tr>
|
| 121 |
+
</tbody>
|
| 122 |
+
</table>
|
| 123 |
+
</div>
|
| 124 |
+
</div>
|
| 125 |
+
</div>
|
| 126 |
+
|
| 127 |
+
<script>
|
| 128 |
let adminCode = '';
|
| 129 |
let charts = {};
|
| 130 |
let refreshInterval;
|
|
|
|
| 257 |
const now = Date.now();
|
| 258 |
const windowStart = now - WINDOW_MS;
|
| 259 |
|
| 260 |
+
// --- CPU : fusionner l'historique + point actuel, garder fenêtre 120s ---
|
| 261 |
const cpuMap = new Map();
|
| 262 |
data.cpuHistory.forEach(m => cpuMap.set(m.timestamp, m.cpu));
|
| 263 |
+
// Ajouter le point actuel (currentCpu) si dispo
|
| 264 |
if (data.currentCpu !== undefined) cpuMap.set(now, data.currentCpu);
|
| 265 |
|
| 266 |
const cpuLabels = [];
|
|
|
|
| 380 |
document.getElementById(tabId).classList.add('active');
|
| 381 |
}
|
| 382 |
|
| 383 |
+
// Allow Enter key in auth input
|
| 384 |
document.getElementById('adminCode')?.addEventListener('keypress', (e) => {
|
| 385 |
if (e.key === 'Enter') login();
|
| 386 |
});
|
| 387 |
+
</script>
|
| 388 |
+
</body>
|
| 389 |
+
</html>
|