2025-03-18 21:43:21 +01:00

240 lines
6.7 KiB
JavaScript

var chartOptions = {
colors:['#008ffb', '#f08681'],
series: [
{
name: 'Ping',
data: [null]
},
{
name: 'Pakke Tap',
type: 'area',
data: [null]
}
],
chart: {
height: '100%',
id: 'realtime',
type: 'area',
animations: {
enabled: false,
},
toolbar: {
show: false
},
zoom: {
enabled: false
},
},
dataLabels: {
enabled: false,
},
stroke: {
curve: 'straight'
},
markers: {
size: 0
},
xaxis: {
type: 'datetime',
labels: {
datetimeUTC: false,
},
},
yaxis: [
{
seriesName: 'Ping',
labels: {
formatter: function (val) {
if (val == null) {
return 0;
}
return val.toFixed(1);
},
}
},
{
seriesName: 'Pakke Tap',
min:0,
max:100,
opposite: true,
labels: {
formatter: function (val) {
if (val == null) {
return 0 + '%';
}
return val.toFixed(0) + "%";
},
}
}
],
legend: {
show: false
},
};
var timeRangeInSeconds = 0;
var chartLatest;
var chartHasFetchedTimeRange = false;
var chartElement;
var chartPingSeries = [];
var chartPacketLossSeries = [];
var scanId;
const queryParam = new Proxy(new URLSearchParams(window.location.search), {
get: (searchParams, prop) => searchParams.get(prop),
});
document.body.onload = async function() {
chartElement = new ApexCharts(document.querySelector("#liveChart"), chartOptions);
chartElement.render();
timeRangeInSeconds = timeRangeSelector[timeRangeSelector.selectedIndex].attributes.value.nodeValue;
await update_summary();
update_chart();
window.setInterval(async function() {
update_summary();
update_chart();
}, 5000)
};
timeRangeSelector.onchange = function(event) {
timeRangeInSeconds = event.srcElement[event.srcElement.selectedIndex].attributes.value.nodeValue;
if (timeRangeInSeconds == 'Details') {
if (queryParam.ref_uuid !== null) {
window.location.href = './?p=scan_details&ref_uuid=' + queryParam.ref_uuid + '&scan_id=' + scanId;
} else {
window.location.href = './?p=scan_details&host=' + queryParam.host + '&scan_id=' + scanId;
}
}
// TODO: a very rare race condition may occur when switching too fast between time ranges can cause the chart to show wrong data
// cancel the update interval and start it again
// reset chart
chartHasFetchedTimeRange = false;
chartPingSeries = [];
chartPacketLossSeries = [];
chartElement.updateSeries([
{
data: chartPingSeries
},
{
data: chartPacketLossSeries
}
]);
update_chart();
update_summary();
};
async function update_summary() {
if (queryParam.ref_uuid !== null) {
var response = await fetch('./?p=api_stats&ref_uuid=' + queryParam.ref_uuid + '&since=' + (Math.floor(Date.now() / 1000) - timeRangeInSeconds));
} else {
var response = await fetch('./?p=api_stats&host=' + queryParam.host + '&since=' + (Math.floor(Date.now() / 1000) - timeRangeInSeconds));
}
if (response.status == 400) {
setTimeout(() => {
window.location.reload();
}, 1000);
return;
}
if (response.status !== 200) {
return;
}
let jsonResponse = await response.json();
scanId = jsonResponse.scan_id;
let resp_ping = jsonResponse.ping_latest;
let resp_packet_loss = jsonResponse.packet_loss_percentage;
let resp_uptime = jsonResponse.uptime_percentage;
let resp_connection_status = jsonResponse.connection_status;
if (resp_ping == null) {
ping.innerHTML = 'Feilet';
} else {
ping.innerHTML = resp_ping + ' ms';
}
packet_loss.innerHTML = resp_packet_loss + ' %';
uptime.innerHTML = resp_uptime + ' %';
if (resp_connection_status == 'ONLINE') {
connection_status.className = 'sign-text alert-success'
connection_status.innerHTML = 'Online';
}
if (resp_connection_status == 'UNSTABLE') {
connection_status.className = 'sign-text alert-warning'
connection_status.innerHTML = 'Ustabilt';
}
if (resp_connection_status == 'OFFLINE') {
connection_status.className = 'sign-text alert-danger'
connection_status.innerHTML = 'Offline';
}
}
async function update_chart()
{
if (!chartHasFetchedTimeRange) {
var response = await fetch('./?p=api_chart&limit_length=1000&scan_id=' + scanId + '&since=' + (Math.floor(Date.now() / 1000) - timeRangeInSeconds));
chartHasFetchedTimeRange = true;
} else {
var response = await fetch('./?p=api_chart&scan_id=' + scanId + '&since=' + chartLatest);
}
if (response.status === 204) {
return;
}
let jsonResponse = await response.json();
chartLatest = jsonResponse.latest;
jsonResponse.pings.forEach(ping => {
let newDate = new Date(ping.date_added + ' UTC');
chartPingSeries.push({ x: newDate.toString(), y: ping.ping_avg});
});
// remove old pings
for (let i = 0; i < chartPingSeries.length; i++) {
let pingEpoch = Math.floor((new Date(chartPingSeries[i].x)).getTime() / 1000);
if (pingEpoch < chartLatest - timeRangeInSeconds) {
chartPingSeries.splice(i, 1);
}
}
jsonResponse.pings.forEach(ping => {
let newDate = new Date(ping.date_added + ' UTC');
if (ping.pkt_transmitted != ping.pkt_received) {
let pktLossPercentage = ping.pkt_received / ping.pkt_transmitted * 100;
pktLossPercentage = 100 - pktLossPercentage;
chartPacketLossSeries.push({ x: newDate.toString(), y: pktLossPercentage});
} else {
chartPacketLossSeries.push({ x: newDate.toString(), y: 0});
}
});
for (let i = 0; i < chartPacketLossSeries.length; i++) {
let pingEpoch = Math.floor((new Date(chartPacketLossSeries[i].x)).getTime() / 1000);
if (pingEpoch < chartLatest - timeRangeInSeconds) {
chartPacketLossSeries.splice(i, 1);
}
}
chartElement.updateSeries([
{
name: 'Ping',
data: chartPingSeries
},
{
name: 'Pakke Tap',
data: chartPacketLossSeries
}
]);
}