A rule named in the matrix, the log, the tracer, statistics or the dashboard opens the Rules page on its chain, scrolled to it and highlighted (#/rules?rule=ID; the chain is looked up when the link has none). The Rules page shows each rule's ID beside its order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
38 lines
2.6 KiB
JavaScript
38 lines
2.6 KiB
JavaScript
// The log: packets rules (with logging on) and the policies logged,
|
|
// refreshed every few seconds.
|
|
import { h, mount, input, debounce, badge } from "../ui.js";
|
|
import { get, state } from "../api.js";
|
|
import { onLeave } from "../app.js";
|
|
|
|
export async function render(el, params, query) {
|
|
const search = input({ type: "search", placeholder: "address, interface, rule…", value: query.get("q") || "" });
|
|
const out = h("div");
|
|
let live = true;
|
|
const pause = h("button", { onclick: () => { live = !live; pause.textContent = live ? "Pause" : "Resume"; } }, "Pause");
|
|
const addr = (a, port) => port ? (a.includes(":") ? "[" + a + "]:" + port : a + ":" + port) : a;
|
|
const draw = async () => {
|
|
const es = await get("/log", { limit: 300, q: search.value.trim() });
|
|
mount(out, es.length ? h("div", { class: "table-wrap" }, h("table", { class: "activity" },
|
|
h("thead", null, h("tr", null, ["Time", "Why", "In", "Out", "From", "To", "Protocol"].map((t) => h("th", null, t)))),
|
|
h("tbody", null, es.map((e) => h("tr", null,
|
|
h("td", { class: "mono small" }, new Date(e.time).toLocaleTimeString(undefined, { hourCycle: "h23" })),
|
|
h("td", null, e.mark && e.mark.startsWith("r") ? h("a", { href: "#/rules?rule=" + e.mark.slice(1), title: "rule #" + e.mark.slice(1) }, e.prefix) : e.prefix,
|
|
e.shadow && /^(drop|reject)$/.test(e.verdict) ? [" ", badge("would " + e.verdict, "warn")] : null),
|
|
h("td", { class: "mono" }, e.in || ""), h("td", { class: "mono" }, e.out || ""),
|
|
h("td", { class: "mono" }, addr(e.source, e.source_port)), h("td", { class: "mono" }, addr(e.destination, e.destination_port)),
|
|
h("td", null, e.protocol, e.icmp_type ? " " + e.icmp_type : "", e.tcp_flags ? h("span", { class: "muted small" }, " " + e.tcp_flags) : null)))))) :
|
|
h("div", { class: "empty" }, state.me.mode === "shadow"
|
|
? "Nothing logged yet. In shadow mode, what the policies would drop is logged; turn logging on for a rule with Edit → Log."
|
|
: "Nothing logged yet. Turn logging on for a rule (Edit → Log), or for what the policies drop (Settings)."));
|
|
};
|
|
search.addEventListener("input", debounce(draw, 300));
|
|
mount(el,
|
|
h("div", { class: "page-head" }, h("div", { class: "grow" }, h("h1", null, "Log"),
|
|
h("div", { class: "muted" }, "Packets logged by rules and policies, newest first (the last 2000 are kept)."))),
|
|
h("div", { class: "filter-row" }, search, pause),
|
|
h("div", { class: "panel" }, out));
|
|
await draw();
|
|
const t = setInterval(() => { if (live && !document.hidden) draw().catch(() => {}); }, 3000);
|
|
onLeave(() => clearInterval(t));
|
|
}
|