| document.addEventListener("DOMContentLoaded", () => { |
| let table = null; |
| const allPackets = []; |
| const protoCounts = { TCP: 0, UDP: 0, DNS: 0, Other: 0 }; |
| const srcCounts = {}; |
| let protocolChart = null; |
| |
| |
| let uiUpdateScheduled = false; |
| const UI_UPDATE_INTERVAL = 1000; |
|
|
| 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"; |
| } |
|
|
| |
| |
| function updateHeavyVisuals() { |
| |
| $('#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'); |
| } |
|
|
| 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); |
| }); |
| } |
| |
| |
| 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(); |
| } |
|
|
| |
| initChart(); |
| initDataTable(); |
|
|
| fetch("/api/packets") |
| .then(r => r.ok ? r.json() : []) |
| .then(processHistoricalData) |
| .catch(err => console.error("Error loading history:", err)); |
|
|
| |
| const socket = io(); |
| socket.on('new_packet', pkt => { |
| |
| allPackets.push(pkt); |
| const proto = getProtocol(pkt); |
| if (protoCounts.hasOwnProperty(proto)) protoCounts[proto]++; |
| srcCounts[pkt.src] = (srcCounts[pkt.src] || 0) + 1; |
|
|
| |
| $('#count-All').text(allPackets.length); |
| const time = new Date(pkt.timestamp * 1000).toLocaleTimeString(); |
| |
| |
| |
| const rowData = [time, pkt.src, pkt.dst, getProtocol(pkt), pkt.length || '-']; |
| table.row.add(rowData).draw(false); |
|
|
| |
| if (!uiUpdateScheduled) { |
| uiUpdateScheduled = true; |
| setTimeout(() => { |
| updateHeavyVisuals(); |
| uiUpdateScheduled = false; |
| }, UI_UPDATE_INTERVAL); |
| } |
| }); |
|
|
| |
| $("#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); |
| }); |
| }); |