Skip to content

Commit 50914f1

Browse files
committed
Enhance LogViewer and NetworkView components for improved log filtering and selection
- Added support for filtering network logs based on selected nodes and links in the NetworkView component. - Updated LogViewer to display selected node or link information, allowing users to clear filters easily. - Refactored LogViewer props to include new selection handling functions for better user interaction.
1 parent bdfb07a commit 50914f1

File tree

2 files changed

+78
-3
lines changed

2 files changed

+78
-3
lines changed

frontend/src/components/LogViewer.tsx

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import {
77
Download,
88
Search,
99
ArrowDown,
10-
Loader
10+
Loader,
11+
X
1112
} from 'lucide-react'
1213
import type { NetworkFlowLog, TrafficFlow } from '@/types/tailscale'
1314

@@ -43,8 +44,11 @@ interface LogViewerProps {
4344
searchQuery?: string
4445
protocolFilters?: Set<string>
4546
trafficTypeFilters?: Set<string>
47+
selectedNode?: { id: string; displayName: string; ips?: string[]; ip: string } | null
48+
selectedLink?: { source: string; target: string; originalSource: string; originalTarget: string } | null
4649
onSelectLog?: (entry: LogEntry) => void
4750
onHeightChange?: (height: number) => void
51+
onClearSelection?: () => void
4852
}
4953

5054
// Worker for heavy data processing
@@ -183,8 +187,11 @@ const LogViewer: React.FC<LogViewerProps> = ({
183187
searchQuery = '',
184188
protocolFilters = new Set(),
185189
trafficTypeFilters = new Set(),
190+
selectedNode,
191+
selectedLink,
186192
onSelectLog,
187-
onHeightChange
193+
onHeightChange,
194+
onClearSelection
188195
}) => {
189196
const [isExpanded, setIsExpanded] = useState(false)
190197
const [panelHeight, setPanelHeight] = useState(300)
@@ -460,6 +467,23 @@ const LogViewer: React.FC<LogViewerProps> = ({
460467
{isExpanded ? <ChevronDown className="w-4 h-4" /> : <ChevronUp className="w-4 h-4" />}
461468
<span>Network Logs ({filteredLogs.length.toLocaleString()} entries)</span>
462469
{isProcessing && <Loader className="w-4 h-4 animate-spin" />}
470+
{(selectedNode || selectedLink) && (
471+
<div className="flex items-center gap-1 px-2 py-0.5 bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 text-xs rounded-full">
472+
<span>{selectedNode ? `Filtered by ${selectedNode.displayName}` : 'Filtered by connection'}</span>
473+
{onClearSelection && (
474+
<button
475+
onClick={(e) => {
476+
e.stopPropagation()
477+
onClearSelection()
478+
}}
479+
className="hover:bg-blue-200 dark:hover:bg-blue-800 rounded-full p-0.5"
480+
title="Clear filter"
481+
>
482+
<X className="w-3 h-3" />
483+
</button>
484+
)}
485+
</div>
486+
)}
463487
</button>
464488

465489
{isExpanded && (

frontend/src/pages/NetworkView.tsx

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,47 @@ const NetworkView: React.FC = () => {
558558
}
559559
}, [networkLogs, devices, servicesRecords])
560560

561+
// Filter network logs based on selected node/link
562+
const filteredNetworkLogs = useMemo(() => {
563+
if (!selectedNode && !selectedLink) {
564+
return networkLogs
565+
}
566+
567+
return networkLogs.filter(log => {
568+
const allTraffic = [
569+
...(log.virtualTraffic || []).map((t: any) => ({ ...t, type: 'virtual' })),
570+
...(log.subnetTraffic || []).map((t: any) => ({ ...t, type: 'subnet' })),
571+
...(log.physicalTraffic || []).map((t: any) => ({ ...t, type: 'physical' }))
572+
]
573+
574+
return allTraffic.some(traffic => {
575+
const srcIP = extractIP(traffic.src)
576+
const dstIP = extractIP(traffic.dst)
577+
578+
if (selectedNode) {
579+
// Check if this traffic involves the selected node
580+
const nodeIPs = selectedNode.ips || [selectedNode.ip]
581+
return nodeIPs.some(ip => ip === srcIP || ip === dstIP)
582+
}
583+
584+
if (selectedLink) {
585+
// Check if this traffic matches the selected link
586+
const linkSourceIP = typeof selectedLink.source === 'string' ?
587+
selectedLink.originalSource :
588+
selectedLink.originalSource
589+
const linkTargetIP = typeof selectedLink.target === 'string' ?
590+
selectedLink.originalTarget :
591+
selectedLink.originalTarget
592+
593+
return (srcIP === linkSourceIP && dstIP === linkTargetIP) ||
594+
(srcIP === linkTargetIP && dstIP === linkSourceIP)
595+
}
596+
597+
return false
598+
})
599+
})
600+
}, [networkLogs, selectedNode, selectedLink])
601+
561602
// Apply filters
562603
const filteredData = useMemo(() => {
563604
let filteredNodes = nodes.filter(node => {
@@ -1339,12 +1380,22 @@ const NetworkView: React.FC = () => {
13391380

13401381
{/* Log Viewer */}
13411382
<LogViewer
1342-
networkLogs={networkLogs as any[]}
1383+
networkLogs={filteredNetworkLogs as any[]}
13431384
devices={devices}
13441385
searchQuery={searchQuery}
13451386
protocolFilters={protocolFilters}
13461387
trafficTypeFilters={trafficTypeFilters}
13471388
onHeightChange={setLogViewerHeight}
1389+
selectedNode={selectedNode}
1390+
selectedLink={selectedLink ? {
1391+
...selectedLink,
1392+
source: typeof selectedLink.source === 'string' ? selectedLink.source : selectedLink.source.id,
1393+
target: typeof selectedLink.target === 'string' ? selectedLink.target : selectedLink.target.id
1394+
} : null}
1395+
onClearSelection={() => {
1396+
setSelectedNode(null)
1397+
setSelectedLink(null)
1398+
}}
13481399
onSelectLog={(logEntry) => {
13491400
// Highlight the corresponding nodes in the graph
13501401
const srcNode = nodes.find(n =>

0 commit comments

Comments
 (0)