time calculation & cleanup

This commit is contained in:
Doruk 2025-06-14 18:40:13 +02:00
parent 9402c0fa00
commit 804bb39f52
2 changed files with 25 additions and 81 deletions

View file

@ -263,50 +263,43 @@ router.get("/api/status-page/:slug/badge", cache("5 minutes"), async (request, r
* @returns {Promise<Array>} Array of aggregated heartbeat data
*/
async function getAggregatedHeartbeats(uptimeCalculator, days) {
const targetBuckets = 100; // Always show ~100 buckets for consistent display
const now = dayjs.utc();
const result = [];
// Calculate the actual time range we have
const startTime = now.subtract(days, "day").startOf("minute");
const endTime = now;
const totalMinutes = endTime.diff(startTime, "minute");
// Calculate how many buckets we can actually show (max 100)
const targetBuckets = Math.min(100, totalMinutes);
const bucketSizeMinutes = Math.max(1, Math.floor(totalMinutes / targetBuckets));
// Determine data granularity based on days
let dataPoints;
let granularity;
let bucketSizeMinutes;
if (days <= 1) {
// For 1 day or less, use minutely data
granularity = "minute";
dataPoints = uptimeCalculator.getDataArray(days * 24 * 60, granularity);
bucketSizeMinutes = Math.max(1, Math.floor((days * 24 * 60) / targetBuckets));
} else if (days <= 30) {
// For 2-30 days, use hourly data
granularity = "hour";
dataPoints = uptimeCalculator.getDataArray(days * 24, granularity);
bucketSizeMinutes = Math.max(60, Math.floor((days * 24 * 60) / targetBuckets));
} else {
// For 31+ days, use daily data
granularity = "day";
dataPoints = uptimeCalculator.getDataArray(days, granularity);
bucketSizeMinutes = Math.max(1440, Math.floor((days * 24 * 60) / targetBuckets));
}
// Create time buckets
const startTime = now.subtract(days, "day").startOf("minute");
const endTime = now;
const buckets = [];
const actualBuckets = Math.floor(totalMinutes / bucketSizeMinutes);
for (let i = 0; i < targetBuckets; i++) {
for (let i = 0; i < actualBuckets; i++) {
const bucketStart = startTime.add(i * bucketSizeMinutes, "minute");
let bucketEnd = bucketStart.add(bucketSizeMinutes, "minute");
// Don't create buckets that start after current time
if (bucketStart.isAfter(endTime)) {
break;
}
// Ensure bucket doesn't extend beyond current time
if (bucketEnd.isAfter(endTime)) {
bucketEnd = endTime;
}
const bucketEnd = bucketStart.add(bucketSizeMinutes, "minute");
buckets.push({
start: bucketStart.unix(),
@ -374,29 +367,6 @@ async function getAggregatedHeartbeats(uptimeCalculator, days) {
});
}
// Ensure we always return targetBuckets number of items by padding at the start
while (result.length < targetBuckets) {
const firstStartTime = result.length > 0 ? dayjs(result[0]._startTime || result[0].time) : now.subtract(days, "day");
const paddedStart = firstStartTime.subtract(bucketSizeMinutes, "minute");
const paddedEnd = firstStartTime;
result.unshift({
status: null,
time: paddedEnd.toISOString(),
msg: "",
ping: null,
_aggregated: true,
_startTime: paddedStart.toISOString(),
_endTime: paddedEnd.toISOString(),
_counts: {
up: 0,
down: 0,
maintenance: 0,
pending: 0
}
});
}
return result;
}

View file

@ -155,30 +155,24 @@ export default {
// Always do client-side aggregation using fixed time buckets
const now = dayjs();
const buckets = [];
// Use same logic as server-side: 100 buckets
const targetBuckets = 100;
const days = this.normalizedHeartbeatBarDays;
const bucketSizeMinutes = Math.max(1, Math.floor((days * 24 * 60) / targetBuckets));
// Create time buckets from oldest to newest
// Calculate the actual time range
const startTime = now.subtract(days, "day").startOf("minute");
const endTime = now;
const totalMinutes = endTime.diff(startTime, "minute");
for (let i = 0; i < targetBuckets; i++) {
let bucketStart = startTime.add(i * bucketSizeMinutes, "minute");
let bucketEnd = bucketStart.add(bucketSizeMinutes, "minute");
// Calculate bucket size to fit the display width
const targetBuckets = Math.min(this.maxBeat > 0 ? this.maxBeat : 100, totalMinutes);
const bucketSizeMinutes = Math.max(1, Math.floor(totalMinutes / targetBuckets));
// Don't create buckets that start after current time
if (bucketStart.isAfter(endTime)) {
break;
}
// Create time buckets
const buckets = [];
const actualBuckets = Math.floor(totalMinutes / bucketSizeMinutes);
// Ensure bucket doesn't extend beyond current time
if (bucketEnd.isAfter(endTime)) {
bucketEnd = endTime;
}
for (let i = 0; i < actualBuckets; i++) {
const bucketStart = startTime.add(i * bucketSizeMinutes, "minute");
const bucketEnd = bucketStart.add(bucketSizeMinutes, "minute");
buckets.push({
start: bucketStart,
@ -225,26 +219,6 @@ export default {
}
});
// Ensure we always return targetBuckets number of items by padding at the start
while (buckets.length < targetBuckets) {
const firstStart = buckets.length > 0 ? buckets[0].start : now.subtract(days, "day");
const paddedStart = firstStart.subtract(bucketSizeMinutes, "minute");
const paddedEnd = firstStart;
buckets.unshift({
start: paddedStart,
end: paddedEnd,
beats: [],
status: null,
time: paddedEnd.toISOString()
});
}
// Limit to maxBeat for display
if (buckets.length > this.maxBeat) {
return buckets.slice(buckets.length - this.maxBeat);
}
return buckets;
},