WIP: fetch historic data for ping-chart

pull/4264/head
Nelson Chan 5 months ago
parent 822ce5384b
commit e78eb5e626
No known key found for this signature in database

@ -149,6 +149,7 @@ const apicache = require("./modules/apicache");
const { resetChrome } = require("./monitor-types/real-browser-monitor-type"); const { resetChrome } = require("./monitor-types/real-browser-monitor-type");
const { EmbeddedMariaDB } = require("./embedded-mariadb"); const { EmbeddedMariaDB } = require("./embedded-mariadb");
const { SetupDatabase } = require("./setup-database"); const { SetupDatabase } = require("./setup-database");
const { chartSocketHandler } = require("./socket-handlers/chart-socket-handler");
app.use(express.json()); app.use(express.json());
@ -1522,6 +1523,7 @@ let needSetup = false;
apiKeySocketHandler(socket); apiKeySocketHandler(socket);
remoteBrowserSocketHandler(socket); remoteBrowserSocketHandler(socket);
generalSocketHandler(socket, server); generalSocketHandler(socket, server);
chartSocketHandler(socket);
log.debug("server", "added all socket handlers"); log.debug("server", "added all socket handlers");

@ -0,0 +1,39 @@
const { checkLogin } = require("../util-server");
const { UptimeCalculator } = require("../uptime-calculator");
const { log } = require("../../src/util");
module.exports.chartSocketHandler = (socket) => {
socket.on("getMonitorChartData", async (monitorID, period, callback) => {
try {
checkLogin(socket);
log.info("monitor", `Get Monitor Chart Data: ${monitorID} User ID: ${socket.userID}`);
if (period == null) {
throw new Error("Invalid period.");
}
let uptimeCalculator = await UptimeCalculator.getUptimeCalculator(monitorID);
let data;
if (period <= 24) {
data = uptimeCalculator.getDataArray(period * 60, "minute");
} else {
data = uptimeCalculator.getDataArray(period / 24, "day");
}
console.log(data);
callback({
ok: true,
data,
});
} catch (e) {
callback({
ok: false,
msg: e.message,
});
}
});
};

@ -50,9 +50,7 @@ export default {
168: "1w", 168: "1w",
}, },
// A heartbeatList for 3h, 6h, 24h, 1w chartRawData: null
// Uses the $root.heartbeatList when value is null
heartbeatList: null
}; };
}, },
computed: { computed: {
@ -157,62 +155,143 @@ export default {
}; };
}, },
chartData() { chartData() {
let pingData = []; // Ping Data for Line Chart, y-axis contains ping time
let downData = []; // Down Data for Bar Chart, y-axis is 1 if target is down (red color), under maintenance (blue color) or pending (orange color), 0 if target is up if (this.chartPeriodHrs === 0) {
let colorData = []; // Color Data for Bar Chart // Render chart using heartbeatList
let pingData = []; // Ping Data for Line Chart, y-axis contains ping time
let heartbeatList = this.heartbeatList || let downData = []; // Down Data for Bar Chart, y-axis is 1 if target is down (red color), under maintenance (blue color) or pending (orange color), 0 if target is up
(this.monitorId in this.$root.heartbeatList && this.$root.heartbeatList[this.monitorId]) || let colorData = []; // Color Data for Bar Chart
[];
let heartbeatList = (this.monitorId in this.$root.heartbeatList && this.$root.heartbeatList[this.monitorId]) || [];
heartbeatList
.filter( heartbeatList
// Filtering as data gets appended .map((beat) => {
// not the most efficient, but works for now const x = this.$root.datetime(beat.time);
(beat) => dayjs.utc(beat.time).tz(this.$root.timezone).isAfter( pingData.push({
dayjs().subtract(Math.max(this.chartPeriodHrs, 6), "hours") x,
) y: beat.ping,
) });
.map((beat) => { downData.push({
const x = this.$root.datetime(beat.time); x,
pingData.push({ y: (beat.status === DOWN || beat.status === MAINTENANCE || beat.status === PENDING) ? 1 : 0,
x, });
y: beat.ping, colorData.push((beat.status === MAINTENANCE) ? "rgba(23,71,245,0.41)" : ((beat.status === PENDING) ? "rgba(245,182,23,0.41)" : "#DC354568"));
}); });
return {
datasets: [
{
// Line Chart
data: pingData,
fill: "origin",
tension: 0.2,
borderColor: "#5CDD8B",
backgroundColor: "#5CDD8B38",
yAxisID: "y",
label: "ping",
},
{
// Bar Chart
type: "bar",
data: downData,
borderColor: "#00000000",
backgroundColor: colorData,
yAxisID: "y1",
barThickness: "flex",
barPercentage: 1,
categoryPercentage: 1,
inflateAmount: 0.05,
label: "status",
},
],
};
} else {
// Render chart using UptimeCalculator data
let avgPingData = []; // Ping Data for Line Chart, y-axis contains ping time
let minPingData = []; // Ping Data for Line Chart, y-axis contains ping time
let maxPingData = []; // Ping Data for Line Chart, y-axis contains ping time
let downData = []; // Down Data for Bar Chart, y-axis is 1 if target is down (red color), under maintenance (blue color) or pending (orange color), 0 if target is up
let colorData = []; // Color Data for Bar Chart
this.chartRawData?.map((datapoint) => {
// Empty datapoints are ignored
if (datapoint.up === 0 && datapoint.down === 0) {
return;
}
const x = this.$root.unixToDateTime(datapoint.timestamp);
// Show ping values if it was up in this period
if (datapoint.up > 0) {
avgPingData.push({
x,
y: datapoint.avgPing,
});
minPingData.push({
x,
y: datapoint.minPing,
});
maxPingData.push({
x,
y: datapoint.maxPing,
});
}
downData.push({ downData.push({
x, x,
y: (beat.status === DOWN || beat.status === MAINTENANCE || beat.status === PENDING) ? 1 : 0, y: datapoint.down,
}); });
colorData.push((beat.status === MAINTENANCE) ? "rgba(23,71,245,0.41)" : ((beat.status === PENDING) ? "rgba(245,182,23,0.41)" : "#DC354568")); colorData.push(this.getBarColorForDatapoint(datapoint));
}); });
return { return {
datasets: [ datasets: [
{ {
// Line Chart // average ping chart
data: pingData, data: avgPingData,
fill: "origin", fill: "origin",
tension: 0.2, tension: 0.2,
borderColor: "#5CDD8B", borderColor: "#5CDD8B",
backgroundColor: "#5CDD8B38", backgroundColor: "#5CDD8B08",
yAxisID: "y", yAxisID: "y",
label: "ping", label: "avg-ping",
}, },
{ {
// minimum ping chart
data: minPingData,
fill: "origin",
tension: 0.2,
borderColor: "#3CBD6B55",
backgroundColor: "#5CDD8B08",
yAxisID: "y",
label: "min-ping",
},
{
// maximum ping chart
data: maxPingData,
fill: "origin",
tension: 0.2,
borderColor: "#7CBD6B55",
backgroundColor: "#5CDD8B08",
yAxisID: "y",
label: "max-ping",
},
{
// Bar Chart // Bar Chart
type: "bar", type: "bar",
data: downData, data: downData,
borderColor: "#00000000", borderColor: "#00000000",
backgroundColor: colorData, backgroundColor: colorData,
yAxisID: "y1", yAxisID: "y1",
barThickness: "flex", barThickness: "flex",
barPercentage: 1, barPercentage: 1,
categoryPercentage: 1, categoryPercentage: 1,
inflateAmount: 0.05, inflateAmount: 0.05,
label: "status", label: "status",
}, },
], ],
}; };
}
}, },
}, },
watch: { watch: {
@ -226,11 +305,19 @@ export default {
} else { } else {
this.loading = true; this.loading = true;
this.$root.getMonitorBeats(this.monitorId, newPeriod, (res) => { let period;
try {
period = parseInt(newPeriod);
} catch (e) {
// Invalid period
period = 24;
}
this.$root.getMonitorChartData(this.monitorId, period, (res) => {
if (!res.ok) { if (!res.ok) {
this.$root.toastError(res.msg); this.$root.toastError(res.msg);
} else { } else {
this.heartbeatList = res.data; this.chartRawData = res.data;
this.$root.storage()[`chart-period-${this.monitorId}`] = newPeriod; this.$root.storage()[`chart-period-${this.monitorId}`] = newPeriod;
} }
this.loading = false; this.loading = false;
@ -239,29 +326,26 @@ export default {
} }
}, },
created() { created() {
// Setup Watcher on the root heartbeatList,
// And mirror latest change to this.heartbeatList
this.$watch(() => this.$root.heartbeatList[this.monitorId],
(heartbeatList) => {
log.debug("ping_chart", `this.chartPeriodHrs type ${typeof this.chartPeriodHrs}, value: ${this.chartPeriodHrs}`);
// eslint-disable-next-line eqeqeq
if (this.chartPeriodHrs != "0") {
const newBeat = heartbeatList.at(-1);
if (newBeat && dayjs.utc(newBeat.time) > dayjs.utc(this.heartbeatList.at(-1)?.time)) {
this.heartbeatList.push(heartbeatList.at(-1));
}
}
},
{ deep: true }
);
// Load chart period from storage if saved // Load chart period from storage if saved
let period = this.$root.storage()[`chart-period-${this.monitorId}`]; let period = this.$root.storage()[`chart-period-${this.monitorId}`];
if (period != null) { if (period != null) {
this.chartPeriodHrs = Math.min(period, 6); this.chartPeriodHrs = Math.min(period, 6);
} }
},
methods: {
getBarColorForDatapoint(datapoint) {
if (datapoint.down === 0) {
// Target is up
return "#FFFFFFFF";
} else if (datapoint.up === 0) {
return "#DC354568";
} else {
return "rgba(245,182,23,0.41)";
}
// TODO: handle maintenance status
// return "rgba(23,71,245,0.41)"
}
} }
}; };
</script> </script>

@ -41,6 +41,15 @@ export default {
return this.datetimeFormat(value, "YYYY-MM-DD HH:mm:ss"); return this.datetimeFormat(value, "YYYY-MM-DD HH:mm:ss");
}, },
/**
* Converts a Unix timestamp to a formatted date and time string.
* @param {number} value - The Unix timestamp to convert.
* @returns {string} The formatted date and time string.
*/
unixToDateTime(value) {
return dayjs.unix(value).tz(this.timezone).format("YYYY-MM-DD HH:mm:ss");
},
/** /**
* Get time for maintenance * Get time for maintenance
* @param {string | number | Date | dayjs.Dayjs} value Time to * @param {string | number | Date | dayjs.Dayjs} value Time to

@ -681,6 +681,17 @@ export default {
getMonitorBeats(monitorID, period, callback) { getMonitorBeats(monitorID, period, callback) {
socket.emit("getMonitorBeats", monitorID, period, callback); socket.emit("getMonitorBeats", monitorID, period, callback);
}, },
/**
* Retrieves monitor chart data.
* @param {string} monitorID - The ID of the monitor.
* @param {string} period - The time period for the chart data.
* @param {socketCB} callback - The callback function to handle the chart data.
* @returns {void}
*/
getMonitorChartData(monitorID, period, callback) {
socket.emit("getMonitorChartData", monitorID, period, callback);
}
}, },
computed: { computed: {

Loading…
Cancel
Save