هذا الالتزام موجود في:
2026-07-28 14:17:17 +03:00
التزام 9a49823ced
218 ملفات معدلة مع 29174 إضافات و0 حذوفات

عرض الملف

@@ -0,0 +1,138 @@
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);

عرض الملف

@@ -0,0 +1,132 @@
[
{
"timestamp": "2026-07-27T11:56:17.989937+00:00",
"error": null,
"dns_ms": 49.33,
"ssl_valid": true,
"ssl_days_remaining": 50,
"ssl_expiry": "2026-09-15T13:10:47+00:00",
"status_code": 200,
"latency_ms": 511.77,
"up": true,
"search_status": 200,
"search_latency_ms": 527.81
},
{
"timestamp": "2026-07-27T11:58:23.314840+00:00",
"error": null,
"dns_ms": 37.98,
"ssl_valid": true,
"ssl_days_remaining": 50,
"ssl_expiry": "2026-09-15T13:10:47+00:00",
"status_code": 200,
"latency_ms": 504.57,
"up": true,
"search_status": 200,
"search_latency_ms": 471.32
},
{
"timestamp": "2026-07-27T11:59:24.683553+00:00",
"error": null,
"dns_ms": 0.51,
"ssl_valid": true,
"ssl_days_remaining": 50,
"ssl_expiry": "2026-09-15T13:10:47+00:00",
"status_code": 200,
"latency_ms": 462.48,
"up": true,
"search_status": 200,
"search_latency_ms": 477.04
},
{
"timestamp": "2026-07-27T12:00:25.962461+00:00",
"error": null,
"dns_ms": 31.21,
"ssl_valid": true,
"ssl_days_remaining": 50,
"ssl_expiry": "2026-09-15T13:10:47+00:00",
"status_code": 200,
"latency_ms": 459.58,
"up": true,
"search_status": 200,
"search_latency_ms": 483.24
},
{
"timestamp": "2026-07-27T12:01:27.276170+00:00",
"error": null,
"dns_ms": 0.57,
"ssl_valid": true,
"ssl_days_remaining": 50,
"ssl_expiry": "2026-09-15T13:10:47+00:00",
"status_code": 200,
"latency_ms": 486.89,
"up": true,
"search_status": 200,
"search_latency_ms": 453.1
},
{
"timestamp": "2026-07-27T12:02:28.551923+00:00",
"error": null,
"dns_ms": 30.0,
"ssl_valid": true,
"ssl_days_remaining": 50,
"ssl_expiry": "2026-09-15T13:10:47+00:00",
"status_code": 200,
"latency_ms": 452.81,
"up": true,
"search_status": 200,
"search_latency_ms": 471.59
},
{
"timestamp": "2026-07-27T12:03:29.838129+00:00",
"error": null,
"dns_ms": 0.56,
"ssl_valid": true,
"ssl_days_remaining": 50,
"ssl_expiry": "2026-09-15T13:10:47+00:00",
"status_code": 200,
"latency_ms": 468.29,
"up": true,
"search_status": 200,
"search_latency_ms": 482.41
},
{
"timestamp": "2026-07-27T12:04:31.124093+00:00",
"error": null,
"dns_ms": 37.49,
"ssl_valid": true,
"ssl_days_remaining": 50,
"ssl_expiry": "2026-09-15T13:10:47+00:00",
"status_code": 200,
"latency_ms": 461.85,
"up": true,
"search_status": 200,
"search_latency_ms": 487.46
},
{
"timestamp": "2026-07-27T12:05:32.449489+00:00",
"error": null,
"dns_ms": 0.58,
"ssl_valid": true,
"ssl_days_remaining": 50,
"ssl_expiry": "2026-09-15T13:10:47+00:00",
"status_code": 200,
"latency_ms": 625.12,
"up": true,
"search_status": 200,
"search_latency_ms": 582.92
},
{
"timestamp": "2026-07-27T12:06:34.902831+00:00",
"error": null,
"dns_ms": 34.69,
"ssl_valid": true,
"ssl_days_remaining": 50,
"ssl_expiry": "2026-09-15T13:10:47+00:00",
"status_code": 200,
"latency_ms": 505.42,
"up": true,
"search_status": 200,
"search_latency_ms": 453.19
}
]

عرض الملف

@@ -0,0 +1,11 @@
timestamp,up,status_code,latency_ms,dns_ms,ssl_valid,ssl_days_remaining,ssl_expiry,search_status,search_latency_ms,error
2026-07-27T11:56:17.989937+00:00,True,200,511.77,49.33,True,50,2026-09-15T13:10:47+00:00,200,527.81,
2026-07-27T11:58:23.314840+00:00,True,200,504.57,37.98,True,50,2026-09-15T13:10:47+00:00,200,471.32,
2026-07-27T11:59:24.683553+00:00,True,200,462.48,0.51,True,50,2026-09-15T13:10:47+00:00,200,477.04,
2026-07-27T12:00:25.962461+00:00,True,200,459.58,31.21,True,50,2026-09-15T13:10:47+00:00,200,483.24,
2026-07-27T12:01:27.276170+00:00,True,200,486.89,0.57,True,50,2026-09-15T13:10:47+00:00,200,453.1,
2026-07-27T12:02:28.551923+00:00,True,200,452.81,30.0,True,50,2026-09-15T13:10:47+00:00,200,471.59,
2026-07-27T12:03:29.838129+00:00,True,200,468.29,0.56,True,50,2026-09-15T13:10:47+00:00,200,482.41,
2026-07-27T12:04:31.124093+00:00,True,200,461.85,37.49,True,50,2026-09-15T13:10:47+00:00,200,487.46,
2026-07-27T12:05:32.449489+00:00,True,200,625.12,0.58,True,50,2026-09-15T13:10:47+00:00,200,582.92,
2026-07-27T12:06:34.902831+00:00,True,200,505.42,34.69,True,50,2026-09-15T13:10:47+00:00,200,453.19,
1 timestamp up status_code latency_ms dns_ms ssl_valid ssl_days_remaining ssl_expiry search_status search_latency_ms error
2 2026-07-27T11:56:17.989937+00:00 True 200 511.77 49.33 True 50 2026-09-15T13:10:47+00:00 200 527.81
3 2026-07-27T11:58:23.314840+00:00 True 200 504.57 37.98 True 50 2026-09-15T13:10:47+00:00 200 471.32
4 2026-07-27T11:59:24.683553+00:00 True 200 462.48 0.51 True 50 2026-09-15T13:10:47+00:00 200 477.04
5 2026-07-27T12:00:25.962461+00:00 True 200 459.58 31.21 True 50 2026-09-15T13:10:47+00:00 200 483.24
6 2026-07-27T12:01:27.276170+00:00 True 200 486.89 0.57 True 50 2026-09-15T13:10:47+00:00 200 453.1
7 2026-07-27T12:02:28.551923+00:00 True 200 452.81 30.0 True 50 2026-09-15T13:10:47+00:00 200 471.59
8 2026-07-27T12:03:29.838129+00:00 True 200 468.29 0.56 True 50 2026-09-15T13:10:47+00:00 200 482.41
9 2026-07-27T12:04:31.124093+00:00 True 200 461.85 37.49 True 50 2026-09-15T13:10:47+00:00 200 487.46
10 2026-07-27T12:05:32.449489+00:00 True 200 625.12 0.58 True 50 2026-09-15T13:10:47+00:00 200 582.92
11 2026-07-27T12:06:34.902831+00:00 True 200 505.42 34.69 True 50 2026-09-15T13:10:47+00:00 200 453.19

عرض الملف

@@ -0,0 +1,82 @@
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>لوحة مراقبة mithal.space</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>لوحة مراقبة mithal.space</h1>
<div class="status-pill" id="overallStatus">جاري التحميل...</div>
</header>
<section class="grid">
<div class="card">
<div class="card-label">Uptime (آخر 24 ساعة)</div>
<div class="big-value" id="uptimeValue">--%</div>
<div class="sub-value" id="uptimeDetail">-- فحص من --</div>
</div>
<div class="card">
<div class="card-label">آخر زمن استجابة</div>
<div class="big-value" id="latencyValue">-- ms</div>
<div class="sub-value" id="latencyDetail">HTTP GET /</div>
</div>
<div class="card">
<div class="card-label">شهادة SSL</div>
<div class="big-value" id="sslValue">--</div>
<div class="sub-value" id="sslDetail">تنتهي في --</div>
</div>
<div class="card">
<div class="card-label">زمن تحليل DNS</div>
<div class="big-value" id="dnsValue">-- ms</div>
<div class="sub-value" id="dnsDetail">آخر فحص</div>
</div>
<div class="card">
<div class="card-label">زمن استجابة البحث</div>
<div class="big-value" id="searchValue">-- ms</div>
<div class="sub-value" id="searchDetail">GET /search?q=test</div>
</div>
</section>
<section class="chart-section">
<h2>زمن الاستجابة — آخر ساعة</h2>
<canvas id="latencyChart" height="90"></canvas>
</section>
<section class="log-section">
<h2>سجل آخر 10 فحوصات</h2>
<table id="logTable">
<thead>
<tr>
<th>الوقت</th>
<th>الحالة</th>
<th>Status</th>
<th>Latency</th>
<th>DNS</th>
<th>SSL (أيام متبقية)</th>
<th>بحث</th>
</tr>
</thead>
<tbody id="logTableBody">
<tr><td colspan="7">جاري التحميل...</td></tr>
</tbody>
</table>
</section>
<footer>
يتحدث تلقائيًا كل 60 ثانية · مصدر البيانات: <code>data/metrics_history.json</code>
</footer>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.min.js"></script>
<script src="app.js"></script>
</body>
</html>

عرض الملف

@@ -0,0 +1,126 @@
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: 'Segoe UI', Tahoma, Arial, sans-serif;
background: #0f1420;
color: #e7ecf5;
padding: 24px;
}
header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 24px;
flex-wrap: wrap;
gap: 12px;
}
h1 {
font-size: 22px;
margin: 0;
}
h2 {
font-size: 16px;
margin: 0 0 14px;
color: #c7cedb;
}
.status-pill {
padding: 8px 18px;
border-radius: 999px;
font-size: 13px;
font-weight: 600;
background: #2a3448;
color: #8b96ab;
}
.status-pill.up {
background: #123a26;
color: #2ecc71;
}
.status-pill.down {
background: #3a1414;
color: #e74c3c;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
gap: 16px;
margin-bottom: 28px;
}
.card {
background: #171f30;
border: 1px solid #2a3448;
border-radius: 12px;
padding: 18px;
}
.card-label {
color: #8b96ab;
font-size: 12px;
margin-bottom: 10px;
}
.big-value {
font-size: 26px;
font-weight: 700;
}
.sub-value {
color: #5b6579;
font-size: 12px;
margin-top: 6px;
}
.chart-section, .log-section {
background: #171f30;
border: 1px solid #2a3448;
border-radius: 12px;
padding: 20px;
margin-bottom: 24px;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
}
th, td {
padding: 8px 10px;
text-align: right;
border-bottom: 1px solid #232d42;
}
th {
color: #8b96ab;
font-weight: 600;
font-size: 12px;
}
td.status-up {
color: #2ecc71;
}
td.status-down {
color: #e74c3c;
}
footer {
text-align: center;
color: #5b6579;
font-size: 12px;
padding: 16px 0;
}
footer code {
color: #8b96ab;
}