| | 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); |
| | }); |
| | }); |