feat: add localhost web UI for scans, blocklist, and firewall management
This commit is contained in:
1 parent
a9dc560f80
commit
4ef6892916
19 files changed
+1934
-117
No files matched your search
@@ -0,0 +1,100 @@
|
||||
{% 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();
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in new issue
Block a user