Router-tools/static/js/pages/akvorado.js
2025-06-22 16:33:28 +02:00

315 lines
No EOL
9.4 KiB
JavaScript

const chartInstances = {};
async function loadPieChart(endpoint, canvasId, legendId) {
try {
const res = await fetch(endpoint);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const json = await res.json();
const canvas = document.getElementById(canvasId);
const legend = document.getElementById(legendId);
if (!json.top || json.top.length === 0) {
legend.innerHTML = "<p>No data received.</p>";
return;
}
const totalPercent = json.top.reduce((sum, item) => sum + item.percent, 0);
let topData = [...json.top];
if (totalPercent < 100) {
topData.push({ name: "Other", percent: 100 - totalPercent });
}
const labels = topData.map(item => item.name);
const data = topData.map(item => item.percent);
const baseColors = labels.map((label, i) =>
label === "Other" ? "#888888" : `hsl(${(i * 57) % 360}, 70%, 60%)`
);
if (!chartInstances[canvasId]) {
const ctx = canvas.getContext("2d");
chartInstances[canvasId] = new Chart(ctx, {
type: "pie",
data: {
labels,
datasets: [{
data,
backgroundColor: baseColors,
borderColor: "#fff",
borderWidth: 2,
hoverOffset: 15
}]
},
options: {
responsive: true,
plugins: {
legend: { display: false },
tooltip: {
enabled: true,
callbacks: {
label: ctx => `${ctx.label}: ${ctx.parsed.toFixed(2)}%`
}
}
}
}
});
chartInstances[canvasId].hiddenSlices = new Set();
} else {
const chart = chartInstances[canvasId];
chart.data.labels = labels;
chart.data.datasets[0].data = data;
chart.data.datasets[0].backgroundColor = baseColors;
chart.update();
}
const chart = chartInstances[canvasId];
const hiddenSlices = chart.hiddenSlices;
legend.innerHTML = "";
const ul = document.createElement("ul");
ul.style.listStyle = "none";
ul.style.padding = "0";
ul.style.margin = "0";
topData.forEach((item, i) => {
const li = document.createElement("li");
li.style.cursor = "pointer";
li.style.display = "flex";
li.style.alignItems = "center";
li.style.marginBottom = "6px";
li.style.userSelect = "none";
const colorBox = document.createElement("span");
colorBox.style.display = "inline-block";
colorBox.style.width = "16px";
colorBox.style.height = "16px";
colorBox.style.marginRight = "8px";
colorBox.style.borderRadius = "3px";
const labelText = item.name === "Other" ? "Other" : item.name.split(":")[0].trim();
li.appendChild(colorBox);
li.appendChild(document.createTextNode(labelText));
if (hiddenSlices.has(i)) {
colorBox.style.backgroundColor = "#bbb";
li.style.opacity = "0.5";
} else {
colorBox.style.backgroundColor = baseColors[i];
li.style.opacity = "1";
}
li.addEventListener("mouseenter", () => {
if (hiddenSlices.has(i)) return;
chart.setActiveElements([{ datasetIndex: 0, index: i }]);
chart.update();
});
li.addEventListener("mouseleave", () => {
chart.setActiveElements([]);
chart.update();
});
li.addEventListener("click", () => {
if (hiddenSlices.has(i)) {
hiddenSlices.delete(i);
} else {
hiddenSlices.add(i);
}
chart.getDatasetMeta(0).data[i].hidden = hiddenSlices.has(i);
if (hiddenSlices.has(i)) {
colorBox.style.backgroundColor = "#bbb";
li.style.opacity = "0.5";
} else {
colorBox.style.backgroundColor = baseColors[i];
li.style.opacity = "1";
}
chart.update();
});
ul.appendChild(li);
});
legend.appendChild(ul);
} catch (err) {
console.error(err);
document.getElementById(legendId).innerHTML = `<p style="color:red">Error retrieving data: ${err.message}</p>`;
}
}
function loadAllCharts() {
loadPieChart("/stats/src-as", "pieChart1", "legend1");
loadPieChart("/stats/src-ports", "pieChart2", "legend2");
loadPieChart("/stats/protocol", "pieChart3", "legend3");
loadPieChart("/stats/src-country", "pieChart4", "legend4");
loadPieChart("/stats/etype", "pieChart5", "legend5");
}
window.addEventListener("DOMContentLoaded", () => {
loadAllCharts();
setInterval(loadAllCharts, 60000);
});
let chart;
async function fetchData() {
const response = await fetch('/stats/graph');
const jsonData = await response.json();
const rawData = jsonData.data.map(item => {
const date = new Date(item.t);
return {
x: date,
y: item.gbps
};
});
const maxVal = Math.max(...rawData.map(p => p.y));
const scaleFactor = maxVal < 1 ? 1000 : 1;
const unitLabel = maxVal < 1 ? 'Mbit/s' : 'Gbit/s';
const data = rawData.map(p => ({
x: p.x,
y: p.y * scaleFactor
}));
return { data, unitLabel };
}
async function initChart() {
const canvas = document.getElementById('liveChart');
const ctx = canvas.getContext('2d');
const dpr = window.devicePixelRatio || 1;
const rect = canvas.getBoundingClientRect();
canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
ctx.scale(dpr, dpr);
const { data, unitLabel } = await fetchData();
chart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: `Network traffic (${unitLabel})`,
data,
borderColor: 'blue',
backgroundColor: 'rgba(0, 0, 255, 0.1)',
fill: true,
pointRadius: 0,
pointHoverRadius: 5
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
interaction: {
mode: 'index',
intersect: false
},
scales: {
x: {
type: 'time',
time: {
unit: 'hour',
displayFormats: {
hour: 'HH:mm'
}
},
ticks: {
autoSkip: false,
callback: function(value, index, ticks) {
const current = new Date(value);
const prev = index > 0 ? new Date(ticks[index - 1].value) : null;
const hours = current.getHours().toString().padStart(2, '0');
const minutes = current.getMinutes().toString().padStart(2, '0');
const day = current.getDate().toString().padStart(2, '0');
const isEvery4Hours = current.getHours() % 4 === 0 && minutes === '00';
const isNewDay = !prev || current.getDate() !== prev.getDate();
if (isNewDay) return `${day}`;
if (isEvery4Hours) return `${hours}:${minutes}`;
return '';
}
},
title: {
display: true,
text: 'Time'
}
},
y: {
beginAtZero: true,
title: {
display: true,
text: unitLabel
}
}
},
plugins: {
tooltip: {
enabled: true,
callbacks: {
label: function(context) {
const label = context.dataset.label || '';
const value = context.parsed.y;
return `${label}: ${value.toFixed(2)}`;
}
}
},
legend: {
display: true
}
}
}
});
}
async function updateChart() {
const { data, unitLabel } = await fetchData();
chart.data.datasets[0].data = data;
chart.data.datasets[0].label = `Network traffic (${unitLabel})`;
chart.options.scales.y.title.text = unitLabel;
chart.update();
}
window.addEventListener("DOMContentLoaded", () => {
loadAllCharts?.();
initChart();
setInterval(() => {
loadAllCharts?.();
updateChart();
}, 60000);
});
document.addEventListener('DOMContentLoaded', () => {
async function fetchData() {
try {
const flowResp = await fetch('/stats/flow-rate');
const flowData = await flowResp.json();
const exportersResp = await fetch('/stats/exporters');
const exportersData = await exportersResp.json();
const flowRateRounded = Math.round(flowData.rate);
const exportersCount = exportersData.exporters.length;
document.getElementById('flowRate').textContent = flowRateRounded;
document.getElementById('exporterCount').textContent = exportersCount;
} catch (error) {
console.error('Error fetching stats:', error);
document.getElementById('flowRate').textContent = 'Error';
document.getElementById('exporterCount').textContent = 'Error';
}
}
fetchData();
setInterval(fetchData, 10000);
});