const REFRESH_INTERVAL_MS = 60000; let chart = null; function fmtDate(iso) { const d = new Date(iso); return d.toLocaleString('ar-EG', { hour12: false }); } function fmtTimeShort(iso) { const d = new Date(iso); return d.toLocaleTimeString('ar-EG', { hour12: false }); } async function loadData() { try { const res = await fetch('data/metrics_history.json', { cache: 'no-store' }); if (!res.ok) throw new Error('metrics file not found yet'); const history = await res.json(); if (!history.length) throw new Error('no data yet'); render(history); } catch (err) { document.getElementById('overallStatus').textContent = 'بانتظار أول فحص...'; document.getElementById('logTableBody').innerHTML = `${err.message}`; } } function render(history) { const latest = history[history.length - 1]; // ---- Overall status pill ---- const statusPill = document.getElementById('overallStatus'); statusPill.textContent = latest.up ? 'الموقع شغال (UP)' : 'الموقع متوقف (DOWN)'; statusPill.className = 'status-pill ' + (latest.up ? 'up' : 'down'); // ---- Uptime (last 24h) ---- const now = new Date(); const last24h = history.filter(r => (now - new Date(r.timestamp)) <= 24 * 60 * 60 * 1000); const upCount = last24h.filter(r => r.up).length; const uptimePct = last24h.length ? ((upCount / last24h.length) * 100).toFixed(2) : '--'; document.getElementById('uptimeValue').textContent = uptimePct + '%'; document.getElementById('uptimeDetail').textContent = `${upCount} من ${last24h.length} فحص`; // ---- Latest latency ---- document.getElementById('latencyValue').textContent = latest.latency_ms != null ? Math.round(latest.latency_ms) + ' ms' : '--'; document.getElementById('latencyDetail').textContent = 'آخر فحص: ' + fmtTimeShort(latest.timestamp); // ---- SSL ---- const sslEl = document.getElementById('sslValue'); const sslDetail = document.getElementById('sslDetail'); if (latest.ssl_valid) { sslEl.textContent = latest.ssl_days_remaining + ' يوم متبقي'; sslEl.style.color = latest.ssl_days_remaining < 14 ? '#e74c3c' : '#2ecc71'; sslDetail.textContent = 'تنتهي في: ' + fmtDate(latest.ssl_expiry); } else { sslEl.textContent = 'غير صالحة'; sslEl.style.color = '#e74c3c'; sslDetail.textContent = 'فشل التحقق من الشهادة'; } // ---- DNS ---- document.getElementById('dnsValue').textContent = latest.dns_ms != null ? Math.round(latest.dns_ms) + ' ms' : '--'; document.getElementById('dnsDetail').textContent = 'آخر فحص: ' + fmtTimeShort(latest.timestamp); // ---- Search response ---- document.getElementById('searchValue').textContent = latest.search_latency_ms != null ? Math.round(latest.search_latency_ms) + ' ms' : '--'; document.getElementById('searchDetail').textContent = 'Status: ' + (latest.search_status ?? '--'); // ---- Chart: last hour of latency ---- const lastHour = history.filter(r => (now - new Date(r.timestamp)) <= 60 * 60 * 1000); renderChart(lastHour); // ---- Log table: last 10 checks ---- renderLogTable(history.slice(-10).reverse()); } function renderChart(data) { const labels = data.map(r => fmtTimeShort(r.timestamp)); const values = data.map(r => r.latency_ms); const ctx = document.getElementById('latencyChart'); if (chart) { chart.data.labels = labels; chart.data.datasets[0].data = values; chart.update(); return; } chart = new Chart(ctx, { type: 'line', data: { labels, datasets: [{ label: 'زمن الاستجابة (ms)', data: values, borderColor: '#4f8cff', backgroundColor: 'rgba(79,140,255,0.1)', tension: 0.3, fill: true, pointRadius: 2, }] }, options: { responsive: true, plugins: { legend: { display: false } }, scales: { x: { ticks: { color: '#5b6579', maxTicksLimit: 8 }, grid: { color: '#232d42' } }, y: { ticks: { color: '#5b6579' }, grid: { color: '#232d42' }, beginAtZero: true } } } }); } function renderLogTable(rows) { const tbody = document.getElementById('logTableBody'); if (!rows.length) { tbody.innerHTML = 'لا توجد بيانات بعد'; return; } tbody.innerHTML = rows.map(r => ` ${fmtDate(r.timestamp)} ${r.up ? 'UP' : 'DOWN'} ${r.status_code ?? '--'} ${r.latency_ms != null ? Math.round(r.latency_ms) + ' ms' : '--'} ${r.dns_ms != null ? Math.round(r.dns_ms) + ' ms' : '--'} ${r.ssl_valid ? r.ssl_days_remaining + ' يوم' : 'غير صالحة'} ${r.search_latency_ms != null ? Math.round(r.search_latency_ms) + ' ms' : '--'} `).join(''); } loadData(); setInterval(loadData, REFRESH_INTERVAL_MS);