add visualizations, better scaling
This commit is contained in:
@@ -4,8 +4,6 @@ import {
|
||||
Button,
|
||||
Card,
|
||||
Col,
|
||||
Descriptions,
|
||||
Modal,
|
||||
Row,
|
||||
Segmented,
|
||||
Select,
|
||||
@@ -21,6 +19,7 @@ import {
|
||||
} from 'antd';
|
||||
import { ReactElement, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { clearPackets, fetchPackets, getPacketsWebSocketUrl } from '../api/apiClient';
|
||||
import PacketInspectModal from './PacketInspectModal';
|
||||
import type { PacketRow } from '../types/packets';
|
||||
|
||||
const { Text, Title } = Typography;
|
||||
@@ -927,8 +926,6 @@ export default function PacketViewer(): ReactElement {
|
||||
const [paused, setPaused] = useState<boolean>(false);
|
||||
const wsRef = useRef<WebSocket | null>(null);
|
||||
const [hexModalOpen, setHexModalOpen] = useState(false);
|
||||
const [rawHex, setRawHex] = useState<string | null>(null);
|
||||
const [rawB64ForDownload, setRawB64ForDownload] = useState<string | null>(null);
|
||||
const [selectedPacket, setSelectedPacket] = useState<PacketRow | null>(null);
|
||||
const queuedDuringPause = useRef<PacketRow[]>([]);
|
||||
|
||||
@@ -1070,27 +1067,10 @@ export default function PacketViewer(): ReactElement {
|
||||
message.info('No inspect data available for this packet');
|
||||
return;
|
||||
}
|
||||
setRawHex(row.raw_b64 ? base64ToHex(row.raw_b64) : '(no raw bytes available)');
|
||||
setRawB64ForDownload(row.raw_b64 ?? null);
|
||||
setSelectedPacket(row);
|
||||
setHexModalOpen(true);
|
||||
};
|
||||
|
||||
const downloadRaw = () => {
|
||||
if (!rawB64ForDownload) return;
|
||||
const blob = base64ToBlob(rawB64ForDownload);
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `packet_${selectedPacket?.id ?? 'pkt'}.bin`;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
|
||||
const reducedMetadata = useMemo(() => buildReducedMetadata(selectedPacket), [selectedPacket]);
|
||||
const fullPacketJson = useMemo(() => formatJson(selectedPacket), [selectedPacket]);
|
||||
const decodedPayload = useMemo(() => getDecodedPayload(selectedPacket), [selectedPacket]);
|
||||
|
||||
const tableRows = useMemo<PacketTableRow[]>(() => {
|
||||
const grouped = new Map<string, PacketRow[]>();
|
||||
const standalone: PacketTableRow[] = [];
|
||||
@@ -1489,113 +1469,14 @@ export default function PacketViewer(): ReactElement {
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<Modal
|
||||
title={`Inspect packet ${selectedPacket?.id ?? ''}`}
|
||||
<PacketInspectModal
|
||||
packet={selectedPacket}
|
||||
open={hexModalOpen}
|
||||
onCancel={() => {
|
||||
onClose={() => {
|
||||
setHexModalOpen(false);
|
||||
setSelectedPacket(null);
|
||||
}}
|
||||
footer={
|
||||
<Space>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setHexModalOpen(false);
|
||||
}}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
<Button icon={<DownloadOutlined />} onClick={downloadRaw} type="primary">
|
||||
Download raw
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
width={1100}
|
||||
>
|
||||
<Tabs
|
||||
items={[
|
||||
{
|
||||
key: 'summary',
|
||||
label: 'Summary',
|
||||
children: renderProtocolSummary(selectedPacket),
|
||||
},
|
||||
{
|
||||
key: 'metadata',
|
||||
label: 'Metadata',
|
||||
children: (
|
||||
<Space direction="vertical" size="middle" style={{ width: '100%' }}>
|
||||
<Descriptions bordered size="small" column={2}>
|
||||
<Descriptions.Item label="Timestamp">{formatTimestamp(selectedPacket?.timestamp)}</Descriptions.Item>
|
||||
<Descriptions.Item label="Flow ID">{selectedPacket?.flow_id ?? getFlowId(selectedPacket ?? {}) ?? '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="Protocol">{selectedPacket ? formatProtocolLabel(selectedPacket) : '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="Application">{selectedPacket?.app_protocol ?? selectedPacket?.app_master_protocol ?? '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="Host">{selectedPacket?.app_hostname ?? '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="Verdict">{selectedPacket?.verdict ?? '-'}</Descriptions.Item>
|
||||
<Descriptions.Item label="Source">
|
||||
{selectedPacket?.src_ip ? `${selectedPacket.src_ip}${selectedPacket?.src_port ? `:${selectedPacket.src_port}` : ''}` : '-'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="Destination">
|
||||
{selectedPacket?.dst_ip ? `${selectedPacket.dst_ip}${selectedPacket?.dst_port ? `:${selectedPacket.dst_port}` : ''}` : '-'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="Path">
|
||||
{[selectedPacket?.ingress_if, selectedPacket?.egress_if].filter(Boolean).join(' -> ') || '-'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="Capture Sources">
|
||||
{selectedPacket?.capture_sources?.join(', ') || '-'}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
<div>
|
||||
<Text strong>Reduced metadata</Text>
|
||||
<pre style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word', fontSize: 12, marginTop: 8 }}>
|
||||
{formatJson(reducedMetadata)}
|
||||
</pre>
|
||||
</div>
|
||||
{decodedPayload?.headersText ? (
|
||||
<div>
|
||||
<Text strong>Decoded headers</Text>
|
||||
<pre style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word', fontSize: 12, marginTop: 8 }}>
|
||||
{decodedPayload.headersText}
|
||||
</pre>
|
||||
</div>
|
||||
) : null}
|
||||
{decodedPayload?.bodyText ? (
|
||||
<div>
|
||||
<Text strong>Decoded body</Text>
|
||||
<pre style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word', fontSize: 12, marginTop: 8 }}>
|
||||
{decodedPayload.bodyText}
|
||||
</pre>
|
||||
</div>
|
||||
) : decodedPayload?.payloadText ? (
|
||||
<div>
|
||||
<Text strong>Decoded payload</Text>
|
||||
<pre style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word', fontSize: 12, marginTop: 8 }}>
|
||||
{decodedPayload.payloadText}
|
||||
</pre>
|
||||
</div>
|
||||
) : null}
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'raw',
|
||||
label: 'Raw',
|
||||
children: (
|
||||
<pre style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word', fontSize: 12 }}>
|
||||
{rawHex}
|
||||
</pre>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'full',
|
||||
label: 'Full JSON',
|
||||
children: (
|
||||
<pre style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word', fontSize: 12 }}>
|
||||
{fullPacketJson}
|
||||
</pre>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Modal>
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user