245 lines
No EOL
9.5 KiB
JavaScript
245 lines
No EOL
9.5 KiB
JavaScript
document.addEventListener('DOMContentLoaded', function () {
|
|
const ctx = document.getElementById('peerHistoryChart').getContext('2d');
|
|
let chart;
|
|
let currentRange = '24h';
|
|
|
|
const timeRangeButtons = document.querySelectorAll('.btn-group .btn');
|
|
const customRangeBtn = document.getElementById('customRangeBtn');
|
|
const startDateInput = document.getElementById('startDate');
|
|
const endDateInput = document.getElementById('endDate');
|
|
|
|
function formatDateForInput(date) {
|
|
const pad = (num) => num.toString().padStart(2, '0');
|
|
const year = date.getFullYear();
|
|
const month = pad(date.getMonth() + 1);
|
|
const day = pad(date.getDate());
|
|
const hours = pad(date.getHours());
|
|
const minutes = pad(date.getMinutes());
|
|
return `${year}-${month}-${day}T${hours}:${minutes}`;
|
|
}
|
|
|
|
function updateDateInputs(range) {
|
|
const endDate = new Date();
|
|
let startDate = new Date();
|
|
|
|
switch (range) {
|
|
case '24h':
|
|
startDate.setHours(startDate.getHours() - 24);
|
|
break;
|
|
case '7d':
|
|
startDate.setDate(startDate.getDate() - 7);
|
|
break;
|
|
case '30d':
|
|
startDate.setDate(startDate.getDate() - 30);
|
|
break;
|
|
case '90d':
|
|
startDate.setDate(startDate.getDate() - 90);
|
|
break;
|
|
}
|
|
|
|
startDateInput.value = formatDateForInput(startDate);
|
|
endDateInput.value = formatDateForInput(endDate);
|
|
}
|
|
|
|
timeRangeButtons.forEach(button => {
|
|
button.addEventListener('click', () => {
|
|
currentRange = button.getAttribute('data-range');
|
|
timeRangeButtons.forEach(btn => btn.classList.remove('active'));
|
|
button.classList.add('active');
|
|
|
|
updateDateInputs(currentRange);
|
|
fetchDataAndRenderChart({ range: currentRange });
|
|
});
|
|
});
|
|
|
|
customRangeBtn.addEventListener('click', () => {
|
|
const startDate = startDateInput.value;
|
|
const endDate = endDateInput.value;
|
|
|
|
if (startDate && endDate) {
|
|
timeRangeButtons.forEach(btn => btn.classList.remove('active'));
|
|
fetchDataAndRenderChart({ startDate, endDate });
|
|
} else {
|
|
alert('Please select both a start and end date.');
|
|
}
|
|
});
|
|
|
|
function updateChartScale(chartInstance) {
|
|
const visibleDatasets = chartInstance.data.datasets.filter(dataset => !dataset.hidden);
|
|
|
|
let pointsForScaling = [];
|
|
visibleDatasets.forEach(dataset => {
|
|
const validData = (dataset.data || []).filter(val => typeof val === 'number');
|
|
pointsForScaling.push(...validData);
|
|
});
|
|
|
|
let suggestedMin, suggestedMax;
|
|
|
|
if (pointsForScaling.length > 0) {
|
|
const minValue = Math.min(...pointsForScaling);
|
|
const maxValue = Math.max(...pointsForScaling);
|
|
const padding = 200;
|
|
|
|
suggestedMin = Math.floor(minValue - padding);
|
|
suggestedMax = Math.ceil(maxValue + padding);
|
|
} else {
|
|
const allPoints = chartInstance.data.datasets.flatMap(d => d.data || []).filter(v => typeof v === 'number');
|
|
if (allPoints.length > 0) {
|
|
const maxVal = Math.max(...allPoints);
|
|
suggestedMin = 0;
|
|
suggestedMax = maxVal + 200;
|
|
} else {
|
|
suggestedMin = 0;
|
|
suggestedMax = 1000;
|
|
}
|
|
}
|
|
|
|
chartInstance.options.scales.y.min = suggestedMin;
|
|
chartInstance.options.scales.y.max = suggestedMax;
|
|
chartInstance.update();
|
|
}
|
|
|
|
async function fetchDataAndRenderChart(params) {
|
|
let url = `/bgp/peer/${neighborIp}/history`;
|
|
if (params.range) {
|
|
url += `?range=${params.range}`;
|
|
} else if (params.startDate && params.endDate) {
|
|
url += `?start_date=${params.startDate}&end_date=${params.endDate}`;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(url);
|
|
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
|
const data = await response.json();
|
|
renderChart(data);
|
|
} catch (error) {
|
|
console.error('Error fetching chart data:', error);
|
|
const chartContainer = document.querySelector('.achart-wrapper-chart');
|
|
if (chartContainer) {
|
|
chartContainer.innerHTML = '<p class="text-danger text-center">Could not load chart data.</p>';
|
|
}
|
|
}
|
|
}
|
|
|
|
function renderChart(data) {
|
|
if (chart) {
|
|
chart.destroy();
|
|
}
|
|
|
|
const findLastValue = (arr) => {
|
|
if (!arr) return 'N/A';
|
|
for (let i = arr.length - 1; i >= 0; i--) {
|
|
if (arr[i] !== null && typeof arr[i] !== 'undefined') {
|
|
return arr[i];
|
|
}
|
|
}
|
|
return 'N/A';
|
|
};
|
|
|
|
const receivedLastValue = findLastValue(data.received);
|
|
const sentLastValue = findLastValue(data.sent);
|
|
|
|
const receivedLabel = `Prefixes Received: ${receivedLastValue.toLocaleString()}`;
|
|
const sentLabel = `Prefixes Sent: ${sentLastValue.toLocaleString()}`;
|
|
|
|
const receivedData = (data.received || []).filter(val => typeof val === 'number');
|
|
const sentData = (data.sent || []).filter(val => typeof val === 'number');
|
|
const maxReceived = receivedData.length > 0 ? Math.max(...receivedData) : -Infinity;
|
|
const maxSent = sentData.length > 0 ? Math.max(...sentData) : -Infinity;
|
|
const isReceivedHidden = maxReceived < maxSent;
|
|
|
|
chart = new Chart(ctx, {
|
|
type: 'line',
|
|
data: {
|
|
labels: data.labels || [],
|
|
datasets: [
|
|
{
|
|
label: receivedLabel,
|
|
data: data.received || [],
|
|
borderColor: 'rgb(54, 162, 235)',
|
|
backgroundColor: 'rgba(54, 162, 235, 0.1)',
|
|
borderWidth: 1.5,
|
|
fill: true,
|
|
pointRadius: 2,
|
|
pointHoverRadius: 5,
|
|
hidden: isReceivedHidden
|
|
},
|
|
{
|
|
label: sentLabel,
|
|
data: data.sent || [],
|
|
borderColor: 'rgb(255, 99, 132)',
|
|
backgroundColor: 'rgba(255, 99, 132, 0.1)',
|
|
borderWidth: 1.5,
|
|
fill: true,
|
|
pointRadius: 2,
|
|
pointHoverRadius: 5,
|
|
hidden: !isReceivedHidden
|
|
}
|
|
]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
interaction: {
|
|
intersect: false,
|
|
mode: 'index',
|
|
},
|
|
scales: {
|
|
x: {
|
|
type: 'time',
|
|
time: {
|
|
unit: 'minute',
|
|
stepSize: 10,
|
|
displayFormats: {
|
|
millisecond: 'HH:mm:ss.SSS',
|
|
second: 'HH:mm:ss',
|
|
minute: 'HH:mm',
|
|
hour: 'HH:mm'
|
|
}
|
|
}
|
|
},
|
|
y: { }
|
|
},
|
|
plugins: {
|
|
legend: {
|
|
position: 'top',
|
|
onClick: (e, legendItem, legend) => {
|
|
const chartInstance = legend.chart;
|
|
const index = legendItem.datasetIndex;
|
|
chartInstance.data.datasets[index].hidden = !chartInstance.data.datasets[index].hidden;
|
|
updateChartScale(chartInstance);
|
|
}
|
|
},
|
|
tooltip: {
|
|
callbacks: {
|
|
title: function(context) {
|
|
if (!context[0]) return '';
|
|
const date = new Date(context[0].parsed.x);
|
|
const year = date.getFullYear();
|
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
const day = String(date.getDate()).padStart(2, '0');
|
|
const hour = String(date.getHours()).padStart(2, '0');
|
|
const minute = String(date.getMinutes()).padStart(2, '0');
|
|
|
|
return `${day}-${month}-${year} ${hour}:${minute}`;
|
|
},
|
|
label: function(context) {
|
|
let label = context.dataset.label.split(':')[0] || '';
|
|
if (label) label += ': ';
|
|
if (context.parsed.y !== null) {
|
|
label += context.parsed.y.toLocaleString();
|
|
}
|
|
return label;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
updateChartScale(chart);
|
|
}
|
|
|
|
updateDateInputs(currentRange);
|
|
fetchDataAndRenderChart({ range: currentRange });
|
|
}); |