File size: 5,585 Bytes
136c0f7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | document.addEventListener("DOMContentLoaded", () => {
let table = null;
const allPackets = [];
const protoCounts = { TCP: 0, UDP: 0, DNS: 0, Other: 0 };
const srcCounts = {};
let protocolChart = null;
// --- New variables for smoother, dynamic updates ---
let uiUpdateScheduled = false;
const UI_UPDATE_INTERVAL = 1000; // Update heavy visuals once per second
function initChart() {
const ctx = document.getElementById('protocolChart')?.getContext('2d');
if (!ctx) return;
protocolChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['TCP', 'UDP', 'DNS', 'Other'],
datasets: [{ data: [0, 0, 0, 0], backgroundColor: ['#0d6efd', '#ffc107', '#dc3545', '#6f42c1'] }]
},
options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom' } } }
});
}
function initDataTable() {
const cols = ["Time", "Source", "Destination", "Protocol", "Length"];
let headerRow = '<tr>';
cols.forEach(txt => headerRow += `<th>${txt}</th>`);
headerRow += '</tr>';
$("#table-header").html(headerRow);
table = $('#packet-table').DataTable({
deferRender: true,
scroller: true,
scrollY: "60vh",
scrollCollapse: true,
paging: true,
lengthChange: false,
info: true,
order: [[0, 'desc']],
language: { search: "", searchPlaceholder: "Search..." },
createdRow: function(row, data, dataIndex) {
if (allPackets[dataIndex]) {
$(row).data('packet', allPackets[dataIndex]);
}
}
});
}
function getProtocol(pkt) {
if (pkt.proto === "DNS") return "DNS";
if (pkt.proto === 6) return "TCP";
if (pkt.proto === 17) return "UDP";
return "Other";
}
// --- Main Update Logic ---
// This function updates all the heavy visual elements
function updateHeavyVisuals() {
// Update KPI cards (except total, which is updated instantly)
$('#count-TCP').text(protoCounts.TCP);
$('#count-UDP').text(protoCounts.UDP);
$('#count-DNS').text(protoCounts.DNS);
if (protocolChart) {
protocolChart.data.datasets[0].data = [protoCounts.TCP, protoCounts.UDP, protoCounts.DNS, protoCounts.Other];
protocolChart.update('none'); // 'none' for smooth animation
}
const topSources = Object.entries(srcCounts).sort(([, a], [, b]) => b - a).slice(0, 5);
const ul = $("#top-sources").empty();
topSources.forEach(([ip, count]) => {
$("<li>").addClass("list-group-item d-flex justify-content-between align-items-center").html(`${ip} <span class="badge bg-primary rounded-pill">${count}</span>`).appendTo(ul);
});
}
// This function processes all historical data once on load
function processHistoricalData(historicalPackets) {
historicalPackets.forEach(pkt => {
allPackets.push(pkt);
const proto = getProtocol(pkt);
if (protoCounts.hasOwnProperty(proto)) protoCounts[proto]++;
srcCounts[pkt.src] = (srcCounts[pkt.src] || 0) + 1;
});
const rowsToAdd = allPackets.map(p => {
const time = new Date(p.timestamp * 1000).toLocaleTimeString();
return [time, p.src, p.dst, getProtocol(p), p.length || '-'];
});
table.rows.add(rowsToAdd).draw();
$('#count-All').text(allPackets.length);
updateHeavyVisuals(); // Update heavy visuals once after history is loaded
}
// --- INITIALIZATION AND DATA LOADING ---
initChart();
initDataTable();
fetch("/api/packets")
.then(r => r.ok ? r.json() : [])
.then(processHistoricalData)
.catch(err => console.error("Error loading history:", err));
// --- LIVE SOCKET.IO UPDATES ---
const socket = io();
socket.on('new_packet', pkt => {
// 1. Instantly update the in-memory data
allPackets.push(pkt);
const proto = getProtocol(pkt);
if (protoCounts.hasOwnProperty(proto)) protoCounts[proto]++;
srcCounts[pkt.src] = (srcCounts[pkt.src] || 0) + 1;
// 2. Instantly update the total count and add the row to the table
$('#count-All').text(allPackets.length);
const time = new Date(pkt.timestamp * 1000).toLocaleTimeString();
// ** THIS IS THE BUG FIX **
// It was `p.length` before, which is undefined. It is now `pkt.length`.
const rowData = [time, pkt.src, pkt.dst, getProtocol(pkt), pkt.length || '-'];
table.row.add(rowData).draw(false);
// 3. Schedule a throttled update for the heavy visuals
if (!uiUpdateScheduled) {
uiUpdateScheduled = true;
setTimeout(() => {
updateHeavyVisuals();
uiUpdateScheduled = false;
}, UI_UPDATE_INTERVAL);
}
});
// --- EVENT HANDLERS ---
$("#packet-table tbody").on("click", "tr", function(){
const pkt = $(this).data('packet');
if (!pkt) return;
let html = '<ul class="list-group">';
Object.entries(pkt).forEach(([k,v])=> {
html += `<li class="list-group-item"><strong>${k}:</strong> ${v}</li>`;
});
html += '</ul>';
$("#packet-detail-body").html(html);
new bootstrap.Modal($("#packetDetailModal")).show();
});
$("#download-btn").on("click", () => {
if (!allPackets.length) {
return alert("No packet data to download.");
}
const ws = XLSX.utils.json_to_sheet(allPackets);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "NetworkData");
const ts = new Date().toISOString().replace(/[:.]/g,"-");
const name = `network_data_${ts}.xlsx`;
XLSX.writeFile(wb, name);
});
}); |