139 أسطر
4.9 KiB
JavaScript
139 أسطر
4.9 KiB
JavaScript
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 =
|
|
`<tr><td colspan="7">${err.message}</td></tr>`;
|
|
}
|
|
}
|
|
|
|
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 = '<tr><td colspan="7">لا توجد بيانات بعد</td></tr>';
|
|
return;
|
|
}
|
|
tbody.innerHTML = rows.map(r => `
|
|
<tr>
|
|
<td>${fmtDate(r.timestamp)}</td>
|
|
<td class="${r.up ? 'status-up' : 'status-down'}">${r.up ? 'UP' : 'DOWN'}</td>
|
|
<td>${r.status_code ?? '--'}</td>
|
|
<td>${r.latency_ms != null ? Math.round(r.latency_ms) + ' ms' : '--'}</td>
|
|
<td>${r.dns_ms != null ? Math.round(r.dns_ms) + ' ms' : '--'}</td>
|
|
<td>${r.ssl_valid ? r.ssl_days_remaining + ' يوم' : 'غير صالحة'}</td>
|
|
<td>${r.search_latency_ms != null ? Math.round(r.search_latency_ms) + ' ms' : '--'}</td>
|
|
</tr>
|
|
`).join('');
|
|
}
|
|
|
|
loadData();
|
|
setInterval(loadData, REFRESH_INTERVAL_MS);
|