103 lines
2.8 KiB
HTML
103 lines
2.8 KiB
HTML
{% extends "base.html" %} {% block title %}Dashboard — Intrusion Detection{%
|
|
endblock %} {% block content %}
|
|
<h1>Dashboard</h1>
|
|
<p class="muted">
|
|
Localhost-only control panel for the intrusion detection scanner.
|
|
</p>
|
|
|
|
<div class="cards" id="status-cards"></div>
|
|
|
|
<div class="panel">
|
|
<div class="panel-head">
|
|
<h2>Latest scans</h2>
|
|
<a class="btn ghost" href="/scan">Run a scan →</a>
|
|
</div>
|
|
<table class="table">
|
|
<thead>
|
|
<tr>
|
|
<th>Job</th>
|
|
<th>Kind</th>
|
|
<th>State</th>
|
|
<th>Started</th>
|
|
<th>Finished</th>
|
|
<th class="num">Blocked</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="latest-scans"></tbody>
|
|
</table>
|
|
</div>
|
|
{% endblock %} {% block scripts %}
|
|
<script>
|
|
async function loadDashboard() {
|
|
try {
|
|
const s = await api("/api/status");
|
|
renderCards(s);
|
|
renderScans(s.latest_scans || []);
|
|
} catch (e) {
|
|
toast(e.message, "error");
|
|
}
|
|
}
|
|
|
|
function renderCards(s) {
|
|
const cards = [
|
|
{
|
|
label: "Scan running",
|
|
value: s.scan_running ? "Yes" : "No",
|
|
tone: s.scan_running ? "warn" : "ok",
|
|
},
|
|
{ label: "Blocklist IPv4", value: s.blocklist_ipv4_count ?? "—" },
|
|
{ label: "Blocklist IPv6", value: s.blocklist_ipv6_count ?? "—" },
|
|
{
|
|
label: "Firewall snapshot",
|
|
value:
|
|
s.firewall_snapshot && s.firewall_snapshot.exists
|
|
? "Present"
|
|
: "None",
|
|
tone:
|
|
s.firewall_snapshot && s.firewall_snapshot.exists ? "ok" : "muted",
|
|
},
|
|
];
|
|
const box = document.getElementById("status-cards");
|
|
box.innerHTML = "";
|
|
for (const c of cards) {
|
|
box.append(
|
|
el(
|
|
"div",
|
|
{ class: "card" },
|
|
el("div", { class: "card-label", text: c.label }),
|
|
el("div", { class: `card-value ${c.tone || ""}`, text: c.value }),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
function renderScans(scans) {
|
|
const body = document.getElementById("latest-scans");
|
|
body.innerHTML = "";
|
|
if (!scans.length) {
|
|
body.innerHTML =
|
|
'<tr><td colspan="6" class="muted">No scans yet — run one from the Scan page.</td></tr>';
|
|
return;
|
|
}
|
|
for (const j of scans) {
|
|
const r = j.result || {};
|
|
body.append(
|
|
el(
|
|
"tr",
|
|
el("td", { class: "mono", text: j.job_id }),
|
|
el("td", { text: j.kind }),
|
|
el("td", {}, badge(j.state)),
|
|
el("td", { text: fmtTime(j.started) }),
|
|
el("td", { text: fmtTime(j.finished) }),
|
|
el("td", { class: "num", text: r.blocked_targets ?? "—" }),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
loadDashboard();
|
|
// Keep the latest-scans table fresh without hammering /api/scan/status.
|
|
setInterval(loadDashboard, 15000);
|
|
</script>
|
|
{% endblock %}
|