162 أسطر
4.7 KiB
HTML
162 أسطر
4.7 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ar" dir="rtl">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>لوحة مراقبة التطبيق - Ghaymah</title>
|
|
<style>
|
|
:root {
|
|
--up: #16a34a;
|
|
--down: #dc2626;
|
|
--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; }
|
|
.controls { margin-bottom: 20px; display: flex; gap: 8px; }
|
|
input {
|
|
flex: 1;
|
|
padding: 10px;
|
|
border-radius: 8px;
|
|
border: 1px solid #334155;
|
|
background: #0b1220;
|
|
color: var(--text);
|
|
}
|
|
button {
|
|
padding: 10px 16px;
|
|
border-radius: 8px;
|
|
border: none;
|
|
background: #2563eb;
|
|
color: white;
|
|
cursor: pointer;
|
|
}
|
|
.grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
gap: 16px;
|
|
}
|
|
.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: 28px; 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); }
|
|
#log {
|
|
margin-top: 20px;
|
|
background: var(--card);
|
|
border-radius: 12px;
|
|
padding: 16px;
|
|
max-height: 260px;
|
|
overflow-y: auto;
|
|
font-family: monospace;
|
|
font-size: 13px;
|
|
}
|
|
.log-line { padding: 4px 0; border-bottom: 1px solid #334155; color: var(--muted); }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<h1>📊 لوحة مراقبة التطبيق</h1>
|
|
<div class="sub">يتم الفحص تلقائياً كل 5 ثوانٍ عبر /health و /metrics</div>
|
|
|
|
<div class="controls">
|
|
<input id="appUrl" value="https://task1-deploy-b18316770d08.hosted.ghaymah.systems" placeholder="رابط التطبيق مثال: https://myapp.ghaymah.systems">
|
|
<button onclick="startMonitoring()">ابدأ المراقبة</button>
|
|
</div>
|
|
|
|
<div class="grid">
|
|
<div class="card">
|
|
<h3>الحالة</h3>
|
|
<div class="value"><span id="statusBadge" class="badge down">غير معروف</span></div>
|
|
</div>
|
|
<div class="card">
|
|
<h3>زمن الاستجابة</h3>
|
|
<div class="value" id="latency">-- ms</div>
|
|
</div>
|
|
<div class="card">
|
|
<h3>عدد الطلبات المخدومة</h3>
|
|
<div class="value" id="reqCount">--</div>
|
|
</div>
|
|
<div class="card">
|
|
<h3>مدة تشغيل التطبيق</h3>
|
|
<div class="value" id="uptime">--</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="log">
|
|
<div class="log-line">جاهز... اضغط "ابدأ المراقبة"</div>
|
|
</div>
|
|
|
|
<script>
|
|
let timer = null;
|
|
|
|
function fmtUptime(sec) {
|
|
const h = Math.floor(sec / 3600);
|
|
const m = Math.floor((sec % 3600) / 60);
|
|
const s = Math.floor(sec % 60);
|
|
return `${h}س ${m}د ${s}ث`;
|
|
}
|
|
|
|
function logLine(text, ok) {
|
|
const log = document.getElementById('log');
|
|
const div = document.createElement('div');
|
|
div.className = 'log-line';
|
|
div.style.color = ok ? '#16a34a' : '#dc2626';
|
|
div.textContent = text;
|
|
log.prepend(div);
|
|
while (log.children.length > 20) log.removeChild(log.lastChild);
|
|
}
|
|
|
|
async function checkOnce() {
|
|
const base = document.getElementById('appUrl').value.replace(/\/$/, '');
|
|
const start = performance.now();
|
|
try {
|
|
const res = await fetch(base + '/health', { cache: 'no-store' });
|
|
const latencyMs = Math.round(performance.now() - start);
|
|
const data = await res.json();
|
|
|
|
document.getElementById('statusBadge').textContent = res.ok ? 'يعمل ✅' : 'متعطل ❌';
|
|
document.getElementById('statusBadge').className = 'badge ' + (res.ok ? 'up' : 'down');
|
|
document.getElementById('latency').textContent = latencyMs + ' ms';
|
|
document.getElementById('reqCount').textContent = data.requests_served ?? '--';
|
|
document.getElementById('uptime').textContent = data.uptime_seconds ? fmtUptime(data.uptime_seconds) : '--';
|
|
|
|
logLine(`${new Date().toLocaleTimeString('ar')} — OK (${latencyMs}ms)`, true);
|
|
} catch (err) {
|
|
document.getElementById('statusBadge').textContent = 'متعطل ❌';
|
|
document.getElementById('statusBadge').className = 'badge down';
|
|
document.getElementById('latency').textContent = '-- ms';
|
|
logLine(`${new Date().toLocaleTimeString('ar')} — فشل الاتصال: ${err.message}`, false);
|
|
}
|
|
}
|
|
|
|
function startMonitoring() {
|
|
if (timer) clearInterval(timer);
|
|
checkOnce();
|
|
timer = setInterval(checkOnce, 5000);
|
|
}
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|