الملفات
2026-07-28 20:41:37 +03:00

172 أسطر
6.0 KiB
HTML

<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<title>لوحة مراقبة mithal.space</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.4/chart.umd.min.js"></script>
<style>
:root {
--up: #16a34a; --down: #dc2626; --warn: #f59e0b;
--bg: #0f172a; --card: #1e293b; --text: #e2e8f0; --muted: #94a3b8;
}
* { box-sizing: border-box; }
body { background: var(--bg); color: var(--text); font-family: "Segoe UI", Tahoma, sans-serif; margin:0; padding:24px; }
h1 { margin-bottom:4px; }
.sub { color: var(--muted); margin-bottom:24px; }
.grid { display:grid; grid-template-columns: repeat(auto-fit, minmax(220px,1fr)); gap:16px; margin-bottom:20px; }
.card { background: var(--card); border-radius:12px; padding:20px; border:1px solid #334155; }
.card h3 { margin:0 0 8px; color: var(--muted); font-size:14px; font-weight:500; }
.card .value { font-size:26px; font-weight:700; }
.badge { display:inline-block; padding:4px 12px; border-radius:999px; font-size:14px; font-weight:700; }
.badge.up { background:rgba(22,163,74,.15); color:var(--up); }
.badge.down { background:rgba(220,38,38,.15); color:var(--down); }
.badge.warn { background:rgba(245,158,11,.15); color:var(--warn); }
.chart-card { background:var(--card); border-radius:12px; padding:20px; border:1px solid #334155; margin-bottom:20px; }
table { width:100%; border-collapse: collapse; font-size:13px; }
th, td { padding:8px 10px; text-align:right; border-bottom:1px solid #334155; }
th { color: var(--muted); font-weight:600; }
.log-card { background:var(--card); border-radius:12px; padding:20px; border:1px solid #334155; }
</style>
</head>
<body>
<h1>📡 لوحة مراقبة mithal.space</h1>
<div class="sub">تحديث تلقائي كل دقيقة (يقرأ من history.json / latest.json الناتجة عن collector.py)</div>
<div class="grid">
<div class="card">
<h3>Uptime (آخر 24 ساعة)</h3>
<div class="value" id="uptimePct">--%</div>
</div>
<div class="card">
<h3>الحالة الحالية</h3>
<div class="value"><span id="statusBadge" class="badge down">--</span></div>
</div>
<div class="card">
<h3>شهادة SSL</h3>
<div class="value" id="sslInfo">--</div>
</div>
<div class="card">
<h3>زمن تحليل DNS</h3>
<div class="value" id="dnsMs">-- ms</div>
</div>
</div>
<div class="chart-card">
<h3 style="color:var(--muted); margin-top:0;">زمن الاستجابة — آخر ساعة</h3>
<canvas id="latencyChart" height="90"></canvas>
</div>
<div class="log-card">
<h3 style="color:var(--muted); margin-top:0;">آخر 10 فحوصات</h3>
<table>
<thead>
<tr><th>الوقت</th><th>الحالة</th><th>HTTP</th><th>زمن الاستجابة</th><th>DNS</th><th>البحث</th></tr>
</thead>
<tbody id="logBody"></tbody>
</table>
</div>
<script>
let chart = null;
async function loadData() {
try {
const [historyRes, latestRes] = await Promise.all([
fetch('history.json', { cache: 'no-store' }),
fetch('latest.json', { cache: 'no-store' })
]);
const history = await historyRes.json();
const latest = await latestRes.json();
renderSummary(history, latest);
renderChart(history);
renderLog(history);
} catch (err) {
document.getElementById('statusBadge').textContent = 'لا يوجد اتصال بالبيانات';
console.error(err);
}
}
function renderSummary(history, latest) {
// Uptime % لآخر 24 ساعة
const last24h = history.slice(-1440);
const upCount = last24h.filter(r => r.up).length;
const pct = last24h.length ? ((upCount / last24h.length) * 100).toFixed(2) : '0.00';
document.getElementById('uptimePct').textContent = pct + '%';
// الحالة الحالية
const badge = document.getElementById('statusBadge');
badge.textContent = latest.up ? 'يعمل ✅' : 'متعطل ❌';
badge.className = 'badge ' + (latest.up ? 'up' : 'down');
// SSL
const sslEl = document.getElementById('sslInfo');
if (latest.ssl_valid) {
const days = latest.ssl_days_left;
sslEl.textContent = `صالحة (${days} يوم متبقي)`;
sslEl.parentElement.querySelector('.value').style.color = days < 14 ? 'var(--warn)' : 'var(--text)';
} else {
sslEl.textContent = 'غير صالحة ⚠️';
}
// DNS
document.getElementById('dnsMs').textContent = (latest.dns_ms ?? '--') + ' ms';
}
function renderChart(history) {
const lastHour = history.slice(-60); // فحص كل دقيقة → آخر 60 نقطة = ساعة
const labels = lastHour.map(r => new Date(r.timestamp).toLocaleTimeString('ar'));
const data = lastHour.map(r => r.latency_ms >= 0 ? r.latency_ms : null);
if (chart) { chart.destroy(); }
chart = new Chart(document.getElementById('latencyChart'), {
type: 'line',
data: {
labels,
datasets: [{
label: 'زمن الاستجابة (ms)',
data,
borderColor: '#2563eb',
backgroundColor: 'rgba(37,99,235,.15)',
tension: 0.3,
fill: true,
pointRadius: 2
}]
},
options: {
responsive: true,
scales: {
x: { ticks: { color: '#94a3b8' }, grid: { color: '#334155' } },
y: { ticks: { color: '#94a3b8' }, grid: { color: '#334155' } }
},
plugins: { legend: { labels: { color: '#e2e8f0' } } }
}
});
}
function renderLog(history) {
const last10 = history.slice(-10).reverse();
const body = document.getElementById('logBody');
body.innerHTML = '';
for (const r of last10) {
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${new Date(r.timestamp).toLocaleString('ar')}</td>
<td>${r.up ? '✅ يعمل' : '❌ متعطل'}</td>
<td>${r.status_code ?? '--'}</td>
<td>${r.latency_ms >= 0 ? r.latency_ms + ' ms' : '--'}</td>
<td>${r.dns_ms >= 0 ? r.dns_ms + ' ms' : '--'}</td>
<td>${r.search_latency_ms >= 0 ? r.search_latency_ms + ' ms' : '--'}</td>
`;
body.appendChild(tr);
}
}
loadData();
setInterval(loadData, 60000);
</script>
</body>
</html>