الملفات
2026-07-27 22:32:29 +03:00

215 أسطر
8.8 KiB
HTML

<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<title>SIEM Dashboard | لوحة المراقبة الأمنية</title>
<style>
:root{
--bg:#0b0f0d;
--panel:#101613;
--line:#1d2622;
--text:#d7e4dc;
--dim:#7b9384;
--amber:#f0b429;
--green:#3ddc84;
--red:#ff5c5c;
--crit:#ff2d55;
}
*{box-sizing:border-box;}
body{
margin:0; background:var(--bg); color:var(--text);
font-family:'IBM Plex Mono','Courier New',monospace;
font-size:14px;
}
header{
display:flex; justify-content:space-between; align-items:center;
padding:18px 28px; border-bottom:1px solid var(--line);
background:linear-gradient(180deg,#0d1310,#0b0f0d);
}
header h1{
margin:0; font-size:18px; letter-spacing:2px; font-weight:600;
color:var(--amber); text-transform:uppercase;
}
header h1 span{color:var(--dim); font-weight:400; font-size:12px; letter-spacing:1px;}
.status{display:flex; align-items:center; gap:8px; font-size:12px; color:var(--dim);}
.dot{width:8px; height:8px; border-radius:50%; background:var(--green); animation:pulse 1.6s infinite;}
@keyframes pulse{0%,100%{opacity:1;} 50%{opacity:.25;}}
.toolbar{
display:flex; gap:12px; align-items:center; padding:12px 28px;
border-bottom:1px solid var(--line); flex-wrap:wrap;
}
button, label.filebtn{
background:var(--panel); color:var(--text); border:1px solid var(--line);
padding:7px 14px; border-radius:3px; cursor:pointer; font-family:inherit; font-size:12px;
}
button:hover, label.filebtn:hover{border-color:var(--amber); color:var(--amber);}
#source-note{color:var(--dim); font-size:12px;}
main{padding:20px 28px;}
.cards{display:grid; grid-template-columns:repeat(auto-fit,minmax(160px,1fr)); gap:14px; margin-bottom:22px;}
.card{
background:var(--panel); border:1px solid var(--line); border-radius:4px;
padding:14px 16px;
}
.card .label{color:var(--dim); font-size:11px; text-transform:uppercase; letter-spacing:1px;}
.card .value{font-size:28px; font-weight:700; margin-top:6px;}
.card.high .value{color:var(--red);}
.card.crit .value{color:var(--crit);}
.card.ips .value{color:var(--amber);}
.card.ok .value{color:var(--green);}
.grid2{display:grid; grid-template-columns:1.4fr 1fr; gap:18px;}
@media (max-width:900px){.grid2{grid-template-columns:1fr;}}
.panel{background:var(--panel); border:1px solid var(--line); border-radius:4px; overflow:hidden;}
.panel h2{
margin:0; padding:12px 16px; font-size:12px; letter-spacing:1px;
text-transform:uppercase; color:var(--dim); border-bottom:1px solid var(--line);
}
table{width:100%; border-collapse:collapse; font-size:12.5px;}
th,td{padding:9px 14px; text-align:right; border-bottom:1px solid var(--line);}
th{color:var(--dim); font-weight:500; font-size:11px; text-transform:uppercase;}
tr:hover td{background:#131a16;}
.sev{padding:2px 8px; border-radius:10px; font-size:10.5px; font-weight:700; letter-spacing:.5px;}
.sev.critical{background:rgba(255,45,85,.15); color:var(--crit);}
.sev.high{background:rgba(255,92,92,.15); color:var(--red);}
.sev.medium{background:rgba(240,180,41,.15); color:var(--amber);}
.sev.low{background:rgba(61,220,132,.15); color:var(--green);}
.src-tag{color:var(--dim); font-size:11px;}
.ipcell{font-weight:700; color:var(--text);}
.bar-wrap{padding:14px 16px;}
.bar-row{display:flex; align-items:center; gap:10px; margin-bottom:9px; font-size:12px;}
.bar-row .lbl{width:90px; color:var(--dim); flex-shrink:0;}
.bar-track{flex:1; background:#1a221d; border-radius:3px; height:10px; overflow:hidden;}
.bar-fill{height:100%; background:var(--amber); border-radius:3px;}
.empty{padding:30px; text-align:center; color:var(--dim);}
</style>
</head>
<body>
<header>
<h1>SIEM <span>/ نظام مراقبة الأحداث الأمنية</span></h1>
<div class="status"><span class="dot" id="live-dot"></span><span id="last-update">لم يتم التحميل بعد</span></div>
</header>
<div class="toolbar">
<button id="refresh-btn">تحديث الآن ⟳</button>
<label class="filebtn">
تحميل alerts.json يدويًا
<input type="file" id="file-input" accept=".json" style="display:none;">
</label>
<span id="source-note">يحاول تحميل alerts.json تلقائيًا كل 10 ثوانٍ (يتطلب تشغيل عبر خادم محلي)</span>
</div>
<main>
<div class="cards" id="cards"></div>
<div class="grid2">
<div class="panel">
<h2>التنبيهات (Alerts)</h2>
<div id="alerts-table"></div>
</div>
<div class="panel">
<h2>عناوين IP المشبوهة</h2>
<div id="ips-table"></div>
<div class="panel" style="border:none; margin-top:4px;">
<h2>التنبيهات حسب المصدر</h2>
<div class="bar-wrap" id="source-bars"></div>
</div>
</div>
</div>
</main>
<script>
const SEV_LABEL = {critical:"حرج", high:"عالي", medium:"متوسط", low:"منخفض"};
const SRC_LABEL = {web:"Web Server", auth:"Auth / OS", firewall:"Firewall"};
let autoTimer = null;
function render(data){
document.getElementById("last-update").textContent =
"آخر تحديث: " + new Date(data.generated_at).toLocaleString("ar-EG");
const s = data.stats || {};
const bySeverity = s.by_severity || {};
document.getElementById("cards").innerHTML = `
<div class="card"><div class="label">إجمالي التنبيهات</div><div class="value">${s.total_alerts ?? 0}</div></div>
<div class="card crit"><div class="label">حرجة</div><div class="value">${bySeverity.critical ?? 0}</div></div>
<div class="card high"><div class="label">عالية الخطورة</div><div class="value">${bySeverity.high ?? 0}</div></div>
<div class="card ok"><div class="label">متوسطة/منخفضة</div><div class="value">${(bySeverity.medium ?? 0) + (bySeverity.low ?? 0)}</div></div>
<div class="card ips"><div class="label">IP مشبوهة</div><div class="value">${s.unique_suspicious_ips ?? 0}</div></div>
`;
const alerts = data.alerts || [];
document.getElementById("alerts-table").innerHTML = alerts.length ? `
<table>
<thead><tr><th>الخطورة</th><th>النوع</th><th>IP</th><th>المصدر</th><th>التفاصيل</th></tr></thead>
<tbody>
${alerts.map(a => `
<tr>
<td><span class="sev ${a.severity}">${SEV_LABEL[a.severity] || a.severity}</span></td>
<td>${a.type}</td>
<td class="ipcell">${a.ip}</td>
<td class="src-tag">${SRC_LABEL[a.source] || a.source}</td>
<td>${a.detail}</td>
</tr>`).join("")}
</tbody>
</table>` : `<div class="empty">لا توجد تنبيهات حاليًا</div>`;
const ips = data.suspicious_ips || [];
document.getElementById("ips-table").innerHTML = ips.length ? `
<table>
<thead><tr><th>IP</th><th>عدد التنبيهات</th><th>درجة الخطورة</th></tr></thead>
<tbody>
${ips.map(i => `<tr><td class="ipcell">${i.ip}</td><td>${i.alert_count}</td><td>${i.score}</td></tr>`).join("")}
</tbody>
</table>` : `<div class="empty">لا توجد عناوين مشبوهة</div>`;
const bySource = s.by_source || {};
const maxVal = Math.max(1, ...Object.values(bySource));
document.getElementById("source-bars").innerHTML = Object.keys(SRC_LABEL).map(key => {
const val = bySource[key] || 0;
const pct = Math.round((val / maxVal) * 100);
return `<div class="bar-row">
<div class="lbl">${SRC_LABEL[key]}</div>
<div class="bar-track"><div class="bar-fill" style="width:${pct}%"></div></div>
<div>${val}</div>
</div>`;
}).join("");
}
async function fetchData(){
try{
const res = await fetch("output/alerts.json", {cache:"no-store"});
if(!res.ok) throw new Error("HTTP " + res.status);
const data = await res.json();
render(data);
document.getElementById("live-dot").style.background = "var(--green)";
}catch(e){
document.getElementById("source-note").textContent =
"تعذّر تحميل alerts.json تلقائيًا (" + e.message + "). استخدم زر التحميل اليدوي، أو شغّل خادم محلي: python3 -m http.server";
document.getElementById("live-dot").style.background = "var(--red)";
}
}
document.getElementById("refresh-btn").addEventListener("click", fetchData);
document.getElementById("file-input").addEventListener("change", (ev) => {
const file = ev.target.files[0];
if(!file) return;
const reader = new FileReader();
reader.onload = () => {
try{
render(JSON.parse(reader.result));
document.getElementById("source-note").textContent = "تم التحميل يدويًا من ملف: " + file.name;
}catch(e){
alert("ملف JSON غير صالح: " + e.message);
}
};
reader.readAsText(file);
});
fetchData();
autoTimer = setInterval(fetchData, 10000);
</script>
</body>
</html>