Refactor web_scan.py for improved readability and maintainability; add cron job script for npm log backups; update start.ps1 for consistent formatting.
This commit is contained in:
1 parent
0d1cd21468
commit
c2eba88ac8
113 files changed
+772606
-772579
No files matched your search
+176
-176
@@ -1,176 +1,176 @@
|
||||
{% extends "base.html" %} {% block title %}Firewall — Intrusion Detection{%
|
||||
endblock %} {% block content %}
|
||||
<h1>Firewall</h1>
|
||||
<p class="muted">
|
||||
Current MikroTik state (from the latest <code>data/mikrotik</code> snapshot).
|
||||
Sync previews or applies the blocklist diff.
|
||||
</p>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-actions" style="margin-top: 0">
|
||||
<button class="btn" id="preview-btn">Preview sync (dry-run)</button>
|
||||
<button class="btn danger" id="apply-btn">Apply sync</button>
|
||||
<span class="muted" id="fw-result"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-head"><h2>Address lists</h2></div>
|
||||
<div id="address-lists"></div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-head"><h2>Firewall rules</h2></div>
|
||||
<div id="firewall-rules"></div>
|
||||
</div>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script>
|
||||
async function loadState() {
|
||||
try {
|
||||
const s = await api("/api/firewall/state");
|
||||
renderAddressLists(s.address_lists || {});
|
||||
renderRules("firewall-rules", s.firewall_rules || {});
|
||||
} catch (e) {
|
||||
toast(e.message, "error");
|
||||
}
|
||||
}
|
||||
|
||||
function renderAddressLists(addr) {
|
||||
const box = document.getElementById("address-lists");
|
||||
box.innerHTML = "";
|
||||
const byList = {};
|
||||
for (const version of ["ipv4", "ipv6"]) {
|
||||
for (const e of addr[version] || []) {
|
||||
const name = e.list || "?";
|
||||
(byList[name] = byList[name] || { ipv4: [], ipv6: [] })[version].push(
|
||||
e.address,
|
||||
);
|
||||
}
|
||||
}
|
||||
const names = Object.keys(byList);
|
||||
if (!names.length) {
|
||||
box.innerHTML =
|
||||
'<p class="muted">No address-list snapshot available. Run <code>app.py --update-firewall</code> first.</p>';
|
||||
return;
|
||||
}
|
||||
for (const name of names) {
|
||||
const rows = [];
|
||||
for (const version of ["ipv4", "ipv6"]) {
|
||||
const entries = (byList[name][version] || []).sort();
|
||||
rows.push(
|
||||
el(
|
||||
"tr",
|
||||
el("td", { text: version }),
|
||||
el("td", { class: "mono", text: entries.join(", ") || "—" }),
|
||||
),
|
||||
);
|
||||
}
|
||||
box.append(
|
||||
el(
|
||||
"div",
|
||||
{ style: "margin-bottom:1rem" },
|
||||
el("div", {
|
||||
style: "font-weight:600;margin-bottom:.4rem",
|
||||
text: name,
|
||||
}),
|
||||
el(
|
||||
"table",
|
||||
{ class: "table" },
|
||||
el(
|
||||
"thead",
|
||||
{},
|
||||
el(
|
||||
"tr",
|
||||
{},
|
||||
el("th", { text: "Version" }),
|
||||
el("th", { text: "Addresses" }),
|
||||
),
|
||||
),
|
||||
el("tbody", {}, ...rows),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function renderRules(boxId, rules) {
|
||||
const box = document.getElementById(boxId);
|
||||
box.innerHTML = "";
|
||||
for (const version of ["ipv4", "ipv6"]) {
|
||||
const list = rules[version] || [];
|
||||
const rows = list.map((r) =>
|
||||
el(
|
||||
"tr",
|
||||
el("td", {
|
||||
class: "mono",
|
||||
text: r.address ? r.address : r["src-address-list"] || "",
|
||||
}),
|
||||
el("td", { text: r.chain || "—" }),
|
||||
el("td", { text: r.action || "—" }),
|
||||
el("td", { text: r.comment || "" }),
|
||||
),
|
||||
);
|
||||
box.append(
|
||||
el(
|
||||
"div",
|
||||
{ style: "margin-bottom:1rem" },
|
||||
el("div", {
|
||||
style: "font-weight:600;margin-bottom:.4rem",
|
||||
text: `IPv${version} (${list.length})`,
|
||||
}),
|
||||
rows.length
|
||||
? el(
|
||||
"table",
|
||||
{ class: "table" },
|
||||
el(
|
||||
"thead",
|
||||
{},
|
||||
el(
|
||||
"tr",
|
||||
{},
|
||||
el("th", { text: "Address / List" }),
|
||||
el("th", { text: "Chain" }),
|
||||
el("th", { text: "Action" }),
|
||||
el("th", { text: "Comment" }),
|
||||
),
|
||||
),
|
||||
el("tbody", {}, ...rows),
|
||||
)
|
||||
: el("p", { class: "muted", text: "No rules in snapshot." }),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function sync(dryRun) {
|
||||
const out = document.getElementById("fw-result");
|
||||
out.textContent = "Working…";
|
||||
try {
|
||||
const changes = await api(`/api/firewall/${dryRun ? "sync" : "apply"}`, {
|
||||
method: "POST",
|
||||
body: {},
|
||||
});
|
||||
const verb = dryRun ? "Would" : "";
|
||||
out.textContent =
|
||||
`${verb} add ${(changes.added_ipv4 || []).length} IPv4 + ${(changes.added_ipv6 || []).length} IPv6; ` +
|
||||
`remove ${(changes.removed_ipv4 || []).length} IPv4 + ${(changes.removed_ipv6 || []).length} IPv6.`;
|
||||
toast(
|
||||
(dryRun ? "Preview" : "Applied") + " firewall sync",
|
||||
dryRun ? "warn" : "ok",
|
||||
);
|
||||
if (!dryRun) loadState();
|
||||
} catch (e) {
|
||||
out.textContent = "";
|
||||
toast(e.message, "error");
|
||||
}
|
||||
}
|
||||
|
||||
document
|
||||
.getElementById("preview-btn")
|
||||
.addEventListener("click", () => sync(true));
|
||||
document
|
||||
.getElementById("apply-btn")
|
||||
.addEventListener("click", () => sync(false));
|
||||
loadState();
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% extends "base.html" %} {% block title %}Firewall — Intrusion Detection{%
|
||||
endblock %} {% block content %}
|
||||
<h1>Firewall</h1>
|
||||
<p class="muted">
|
||||
Current MikroTik state (from the latest <code>data/mikrotik</code> snapshot).
|
||||
Sync previews or applies the blocklist diff.
|
||||
</p>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-actions" style="margin-top: 0">
|
||||
<button class="btn" id="preview-btn">Preview sync (dry-run)</button>
|
||||
<button class="btn danger" id="apply-btn">Apply sync</button>
|
||||
<span class="muted" id="fw-result"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-head"><h2>Address lists</h2></div>
|
||||
<div id="address-lists"></div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-head"><h2>Firewall rules</h2></div>
|
||||
<div id="firewall-rules"></div>
|
||||
</div>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script>
|
||||
async function loadState() {
|
||||
try {
|
||||
const s = await api("/api/firewall/state");
|
||||
renderAddressLists(s.address_lists || {});
|
||||
renderRules("firewall-rules", s.firewall_rules || {});
|
||||
} catch (e) {
|
||||
toast(e.message, "error");
|
||||
}
|
||||
}
|
||||
|
||||
function renderAddressLists(addr) {
|
||||
const box = document.getElementById("address-lists");
|
||||
box.innerHTML = "";
|
||||
const byList = {};
|
||||
for (const version of ["ipv4", "ipv6"]) {
|
||||
for (const e of addr[version] || []) {
|
||||
const name = e.list || "?";
|
||||
(byList[name] = byList[name] || { ipv4: [], ipv6: [] })[version].push(
|
||||
e.address,
|
||||
);
|
||||
}
|
||||
}
|
||||
const names = Object.keys(byList);
|
||||
if (!names.length) {
|
||||
box.innerHTML =
|
||||
'<p class="muted">No address-list snapshot available. Run <code>app.py --update-firewall</code> first.</p>';
|
||||
return;
|
||||
}
|
||||
for (const name of names) {
|
||||
const rows = [];
|
||||
for (const version of ["ipv4", "ipv6"]) {
|
||||
const entries = (byList[name][version] || []).sort();
|
||||
rows.push(
|
||||
el(
|
||||
"tr",
|
||||
el("td", { text: version }),
|
||||
el("td", { class: "mono", text: entries.join(", ") || "—" }),
|
||||
),
|
||||
);
|
||||
}
|
||||
box.append(
|
||||
el(
|
||||
"div",
|
||||
{ style: "margin-bottom:1rem" },
|
||||
el("div", {
|
||||
style: "font-weight:600;margin-bottom:.4rem",
|
||||
text: name,
|
||||
}),
|
||||
el(
|
||||
"table",
|
||||
{ class: "table" },
|
||||
el(
|
||||
"thead",
|
||||
{},
|
||||
el(
|
||||
"tr",
|
||||
{},
|
||||
el("th", { text: "Version" }),
|
||||
el("th", { text: "Addresses" }),
|
||||
),
|
||||
),
|
||||
el("tbody", {}, ...rows),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function renderRules(boxId, rules) {
|
||||
const box = document.getElementById(boxId);
|
||||
box.innerHTML = "";
|
||||
for (const version of ["ipv4", "ipv6"]) {
|
||||
const list = rules[version] || [];
|
||||
const rows = list.map((r) =>
|
||||
el(
|
||||
"tr",
|
||||
el("td", {
|
||||
class: "mono",
|
||||
text: r.address ? r.address : r["src-address-list"] || "",
|
||||
}),
|
||||
el("td", { text: r.chain || "—" }),
|
||||
el("td", { text: r.action || "—" }),
|
||||
el("td", { text: r.comment || "" }),
|
||||
),
|
||||
);
|
||||
box.append(
|
||||
el(
|
||||
"div",
|
||||
{ style: "margin-bottom:1rem" },
|
||||
el("div", {
|
||||
style: "font-weight:600;margin-bottom:.4rem",
|
||||
text: `IPv${version} (${list.length})`,
|
||||
}),
|
||||
rows.length
|
||||
? el(
|
||||
"table",
|
||||
{ class: "table" },
|
||||
el(
|
||||
"thead",
|
||||
{},
|
||||
el(
|
||||
"tr",
|
||||
{},
|
||||
el("th", { text: "Address / List" }),
|
||||
el("th", { text: "Chain" }),
|
||||
el("th", { text: "Action" }),
|
||||
el("th", { text: "Comment" }),
|
||||
),
|
||||
),
|
||||
el("tbody", {}, ...rows),
|
||||
)
|
||||
: el("p", { class: "muted", text: "No rules in snapshot." }),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function sync(dryRun) {
|
||||
const out = document.getElementById("fw-result");
|
||||
out.textContent = "Working…";
|
||||
try {
|
||||
const changes = await api(`/api/firewall/${dryRun ? "sync" : "apply"}`, {
|
||||
method: "POST",
|
||||
body: {},
|
||||
});
|
||||
const verb = dryRun ? "Would" : "";
|
||||
out.textContent =
|
||||
`${verb} add ${(changes.added_ipv4 || []).length} IPv4 + ${(changes.added_ipv6 || []).length} IPv6; ` +
|
||||
`remove ${(changes.removed_ipv4 || []).length} IPv4 + ${(changes.removed_ipv6 || []).length} IPv6.`;
|
||||
toast(
|
||||
(dryRun ? "Preview" : "Applied") + " firewall sync",
|
||||
dryRun ? "warn" : "ok",
|
||||
);
|
||||
if (!dryRun) loadState();
|
||||
} catch (e) {
|
||||
out.textContent = "";
|
||||
toast(e.message, "error");
|
||||
}
|
||||
}
|
||||
|
||||
document
|
||||
.getElementById("preview-btn")
|
||||
.addEventListener("click", () => sync(true));
|
||||
document
|
||||
.getElementById("apply-btn")
|
||||
.addEventListener("click", () => sync(false));
|
||||
loadState();
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in new issue
Block a user