Show blocked counts on the Blocking page, and reveal + CNAME on hover
The Blocking page shows how many queries each blocklist, and the rules together, blocked in the last 24 hours (with statistics on). The + CNAME button on A/AAAA rows shows when the row is hovered or focused, and always on touch screens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8f0a0cc233
commit
5b8f681e2a
5 files changed
+32
-7
No files matched your search
@@ -22,7 +22,11 @@ let refreshTimer = null;
|
||||
|
||||
export async function render(el, params, query) {
|
||||
clearTimeout(refreshTimer);
|
||||
const [lists, rules, views, status] = await Promise.all([get("/blocking/lists", draftQuery()), get("/blocking/rules", draftQuery()), get("/views"), get("/blocking/status")]);
|
||||
// Queries blocked in the last day, per list and for the rules together
|
||||
// (null without query statistics, or without permission to see them).
|
||||
const [lists, rules, views, status, hitList] = await Promise.all([get("/blocking/lists", draftQuery()), get("/blocking/rules", draftQuery()), get("/views"), get("/blocking/status"),
|
||||
can("stats") ? get("/stats/breakdown", { group: "blocklist", since: "24h" }).catch(() => null) : null]);
|
||||
const hits = hitList ? Object.fromEntries(hitList.map((e) => [e.key, e.count])) : null;
|
||||
const writable = can("blocking", true);
|
||||
const recursive = lists.concat(rules).find((x) => x.auto_views);
|
||||
const autoViews = recursive ? recursive.views : null;
|
||||
@@ -53,8 +57,8 @@ export async function render(el, params, query) {
|
||||
h("form", { class: "actions", onsubmit: runCheck },
|
||||
h("label", { class: "muted" }, "Check a name"), checkName, h("button", { type: "submit" }, "Check")),
|
||||
checkOut),
|
||||
rulesPanel(rules, views, autoViews, writable),
|
||||
listsPanel(lists, views, autoViews, writable));
|
||||
rulesPanel(rules, views, autoViews, writable, hits),
|
||||
listsPanel(lists, views, autoViews, writable, hits));
|
||||
if (checkName.value) runCheck();
|
||||
|
||||
// Lists are downloaded in the background, and a pause ends by itself:
|
||||
@@ -207,7 +211,12 @@ function viewsCell(x) {
|
||||
return x.auto_views ? [x.views.join(", "), h("span", { class: "muted small" }, " (auto)")] : x.views.join(", ");
|
||||
}
|
||||
|
||||
function rulesPanel(rules, views, autoViews, writable) {
|
||||
// hitsLink shows a count of blocked queries, linking to the statistics.
|
||||
function hitsLink(n) {
|
||||
return h("a", { href: "#/stats?since=24h", title: "queries blocked in the last 24 hours; more on the Statistics page" }, (n || 0).toLocaleString());
|
||||
}
|
||||
|
||||
function rulesPanel(rules, views, autoViews, writable, hits) {
|
||||
const name = input({ placeholder: "ads.example.com", class: "mono" });
|
||||
const action = select([["block", "Block"], ["allow", "Allow"]], "block");
|
||||
const exact = checkbox("only this exact name", false);
|
||||
@@ -230,6 +239,7 @@ function rulesPanel(rules, views, autoViews, writable) {
|
||||
h("div", { class: "f" }, h("label", null, " "), h("button", { class: "primary", type: "submit" }, "Add rule")));
|
||||
return h("div", { class: "panel" },
|
||||
h("h2", null, "Your rules"),
|
||||
hits && rules.some((r) => r.action === "block") ? h("p", { class: "small" }, "Blocked ", hitsLink(hits.rules), " queries in the last 24 hours (all rules together: named doesn't say which rule matched).") : null,
|
||||
h("p", { class: "muted small" }, "A rule covers the domain and every name below it, unless it's for the exact name only. Rules beat every blocklist, and the most specific rule wins: allowing cdn.example.com overrides blocking example.com for cdn.example.com and the names below it."),
|
||||
writable ? [form, err] : null,
|
||||
table([
|
||||
@@ -243,7 +253,7 @@ function rulesPanel(rules, views, autoViews, writable) {
|
||||
].filter(Boolean), rules, { empty: "No rules yet.", rowClass: (r) => r.pending ? "pending-" + r.pending : "" }));
|
||||
}
|
||||
|
||||
function listsPanel(lists, views, autoViews, writable) {
|
||||
function listsPanel(lists, views, autoViews, writable, hits) {
|
||||
const name = input({ placeholder: "hagezi-multi", class: "mono", style: { width: "11em" } });
|
||||
const url = input({ placeholder: "https://…", class: "mono" });
|
||||
const vp = viewPicker(views, autoViews);
|
||||
@@ -273,6 +283,7 @@ function listsPanel(lists, views, autoViews, writable) {
|
||||
{ label: "Name", render: (l) => [h("strong", { class: "mono" }, l.name), l.pending ? [" ", pendingBadge(l.pending)] : null] },
|
||||
{ label: "State", render: (l) => { const [t, k] = stateBadge[l.state] || [l.state, ""]; return [badge(t, k), l.status && l.status.error ? h("div", { class: "small err-text" }, l.status.error) : null]; } },
|
||||
{ label: "Domains", render: (l) => l.status && l.status.updated_at ? l.status.entries.toLocaleString() : "–", tdClass: "right" },
|
||||
hits ? { label: "Blocked (24h)", render: (l) => hitsLink(hits[l.name]), class: "right", tdClass: "right" } : null,
|
||||
views.length ? { label: "Views", render: viewsCell } : null,
|
||||
{ label: "Updated", render: (l) => l.status && l.status.updated_at ? h("span", { title: "checked " + time(l.status.checked_at) }, relTime(l.status.updated_at)) : "–", tdClass: "small" },
|
||||
{ label: "Memory", render: (l) => l.memory_mb ? h("span", { title: "named's memory for this list, estimated (all its views)" }, "~" + l.memory_mb + " MB") : "–", tdClass: "small right" },
|
||||
|
||||
@@ -482,7 +482,7 @@ async function recordsTab(el, zone, query) {
|
||||
{ label: "Comment", render: (r) => [r.pending ? [pendingBadge(r.pending), " "] : null, h("span", { class: "muted" }, r.comment || "")] },
|
||||
{ label: "", render: (r) => r.pending === "delete" ? "" : h("span", null,
|
||||
!reverse && (r.type === "A" || r.type === "AAAA") && !r.disabled
|
||||
? [h("button", { class: "small", title: "Add a CNAME record that points to " + r.name.replace(/\.$/, ""), onclick: () => aliasTo(r) }, "+ CNAME"), " "] : null,
|
||||
? [h("button", { class: "small reveal", title: "Add a CNAME record that points to " + r.name.replace(/\.$/, ""), onclick: () => aliasTo(r) }, "+ CNAME"), " "] : null,
|
||||
h("button", { class: "small", title: "Edit", onclick: () => edit(r) }, "Edit"), " ",
|
||||
r.disabled
|
||||
? h("button", { class: "small toggle", title: "Put the record back in the zone", onclick: () => toggle(r) }, "Enable")
|
||||
|
||||
Reference in new issue
Block a user