From 0bb3603740816a8efefd8159281e4847dab00a31 Mon Sep 17 00:00:00 2001 From: wweir Date: Fri, 7 Aug 2026 15:51:20 +0800 Subject: [PATCH] feat(web): show blocked domains in traffic page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a third 拦截 view to the Traffic console listing domains blocked since start, ordered by count, with a search filter and live SSE updates. The view hides the sort and client-chip controls that only apply to traffic domains. --- web/src/lib/api.ts | 7 ++++ web/src/views/Traffic.svelte | 72 ++++++++++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/web/src/lib/api.ts b/web/src/lib/api.ts index adc670a..5a88b06 100644 --- a/web/src/lib/api.ts +++ b/web/src/lib/api.ts @@ -28,6 +28,12 @@ export interface ClientStat { lastSeen: string; } +export interface BlockedStat { + domain: string; + count: number; + lastSeen: string; +} + export interface TrafficSnapshot { uptime: number; dnsQueries: number; @@ -40,6 +46,7 @@ export interface TrafficSnapshot { connsPerSec: number; }; ruleHits: { block: number; direct: number; proxy: number }; + blocked: BlockedStat[]; system: { goroutines: number; heapAlloc: number }; bytesUp: number; bytesDown: number; diff --git a/web/src/views/Traffic.svelte b/web/src/views/Traffic.svelte index fc4f66c..b51b175 100644 --- a/web/src/views/Traffic.svelte +++ b/web/src/views/Traffic.svelte @@ -20,7 +20,7 @@ { value: 'conns', label: '连接数' }, ] - type View = 'live' | 'totals' + type View = 'live' | 'totals' | 'blocked' let view = $state('live') // Live view is fed by the SSE traffic events; totals are the cumulative @@ -35,6 +35,15 @@ let sort: SortMode = $state('bytes') let client = $state('') + // Blocked domains are global: block decisions carry no source or client + // dimension, so the list ignores the page's traffic filters. + const blocked = $derived(live.traffic?.blocked ?? []) + const visibleBlocked = $derived.by(() => { + const q = filter.trim().toLowerCase() + if (!q) return blocked + return blocked.filter((b) => b.domain.toLowerCase().includes(q)) + }) + const activeSortLabel = $derived(sortModes.find((m) => m.value === sort)?.label ?? '流量') // Chart band: the history series is process-global, not affected by the @@ -120,6 +129,16 @@ > 总计 + {#if view === 'live'}
{/if} - {#if visibleDomains.length === domains.length} + {#if view === 'blocked'} + {formatCount(visibleBlocked.length)} blocked + {:else if visibleDomains.length === domains.length} {formatCount(visibleDomains.length)} domains {:else} {formatCount(visibleDomains.length)} / {formatCount(domains.length)} domains {/if}
- {#if clients.length > 0} + {#if view !== 'blocked' && clients.length > 0}
+ {#if view === 'blocked'} + {#if blocked.length === 0} +
+
+ {:else} + + + + + Domain + Blocked + Last seen + + + + {#if visibleBlocked.length === 0} + + + 没有匹配“{filter}”的域名。 + + + {/if} + {#each visibleBlocked as b (b.domain)} + + {b.domain} + {formatCount(b.count)} + + {formatTime(b.lastSeen)} + + + {/each} + + + + {/if} + {:else} {#if domains.length === 0}