// 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", 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)); }