mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-06-19 18:56:48 +02:00
rethought beat aggregation system fully server-side
This commit is contained in:
parent
1518fd528b
commit
adc362a2a8
3 changed files with 49 additions and 6 deletions
|
@ -89,6 +89,9 @@ router.get("/api/status-page/heartbeat/:slug", cache("1 minutes"), async (reques
|
||||||
let statusPage = await R.findOne("status_page", " id = ? ", [ statusPageID ]);
|
let statusPage = await R.findOne("status_page", " id = ? ", [ statusPageID ]);
|
||||||
let heartbeatBarDays = statusPage ? (statusPage.heartbeat_bar_days || 0) : 0;
|
let heartbeatBarDays = statusPage ? (statusPage.heartbeat_bar_days || 0) : 0;
|
||||||
|
|
||||||
|
// Get max beats parameter from query string (for client-side screen width constraints)
|
||||||
|
const maxBeats = parseInt(request.query.maxBeats) || 100;
|
||||||
|
|
||||||
// Process all monitors in parallel using Promise.all
|
// Process all monitors in parallel using Promise.all
|
||||||
const monitorPromises = monitorIDList.map(async (monitorID) => {
|
const monitorPromises = monitorIDList.map(async (monitorID) => {
|
||||||
const uptimeCalculator = await UptimeCalculator.getUptimeCalculator(monitorID);
|
const uptimeCalculator = await UptimeCalculator.getUptimeCalculator(monitorID);
|
||||||
|
@ -112,7 +115,7 @@ router.get("/api/status-page/heartbeat/:slug", cache("1 minutes"), async (reques
|
||||||
uptime = uptimeCalculator.get24Hour().uptime;
|
uptime = uptimeCalculator.get24Hour().uptime;
|
||||||
} else {
|
} else {
|
||||||
// For configured day ranges, use aggregated data from UptimeCalculator
|
// For configured day ranges, use aggregated data from UptimeCalculator
|
||||||
heartbeats = await getAggregatedHeartbeats(uptimeCalculator, heartbeatBarDays);
|
heartbeats = await getAggregatedHeartbeats(uptimeCalculator, heartbeatBarDays, maxBeats);
|
||||||
// Calculate uptime for the configured range instead of just 24h
|
// Calculate uptime for the configured range instead of just 24h
|
||||||
uptime = uptimeCalculator.get24Hour().uptime; // TODO: Calculate range-specific uptime
|
uptime = uptimeCalculator.get24Hour().uptime; // TODO: Calculate range-specific uptime
|
||||||
}
|
}
|
||||||
|
@ -280,16 +283,16 @@ router.get("/api/status-page/:slug/badge", cache("5 minutes"), async (request, r
|
||||||
* Get aggregated heartbeats for status page display
|
* Get aggregated heartbeats for status page display
|
||||||
* @param {UptimeCalculator} uptimeCalculator The uptime calculator instance
|
* @param {UptimeCalculator} uptimeCalculator The uptime calculator instance
|
||||||
* @param {number} days Number of days to show
|
* @param {number} days Number of days to show
|
||||||
|
* @param {number} targetBuckets Number of buckets to aggregate into (default 100)
|
||||||
* @returns {Promise<Array>} Array of aggregated heartbeat data
|
* @returns {Promise<Array>} Array of aggregated heartbeat data
|
||||||
*/
|
*/
|
||||||
async function getAggregatedHeartbeats(uptimeCalculator, days) {
|
async function getAggregatedHeartbeats(uptimeCalculator, days, targetBuckets = 100) {
|
||||||
const now = dayjs.utc();
|
const now = dayjs.utc();
|
||||||
const result = [];
|
const result = [];
|
||||||
|
|
||||||
// Force exact time range: exactly N days ago to exactly now
|
// Force exact time range: exactly N days ago to exactly now
|
||||||
const startTime = now.subtract(days, "day");
|
const startTime = now.subtract(days, "day");
|
||||||
const totalMinutes = days * 60 * 24;
|
const totalMinutes = days * 60 * 24;
|
||||||
const targetBuckets = 100;
|
|
||||||
const bucketSizeMinutes = totalMinutes / targetBuckets;
|
const bucketSizeMinutes = totalMinutes / targetBuckets;
|
||||||
|
|
||||||
// Get available data from UptimeCalculator for lookup
|
// Get available data from UptimeCalculator for lookup
|
||||||
|
|
|
@ -292,7 +292,24 @@ export default {
|
||||||
*/
|
*/
|
||||||
resize() {
|
resize() {
|
||||||
if (this.$refs.wrap) {
|
if (this.$refs.wrap) {
|
||||||
this.maxBeat = Math.floor(this.$refs.wrap.clientWidth / (this.beatWidth + this.beatHoverAreaPadding * 2));
|
const newMaxBeat = Math.floor(this.$refs.wrap.clientWidth / (this.beatWidth + this.beatHoverAreaPadding * 2));
|
||||||
|
|
||||||
|
// If maxBeat changed and we're in configured days mode, notify parent to reload data
|
||||||
|
if (newMaxBeat !== this.maxBeat && this.normalizedHeartbeatBarDays > 0) {
|
||||||
|
this.maxBeat = newMaxBeat;
|
||||||
|
console.log(`HeartBeat Debug: Container width changed, maxBeat=${newMaxBeat}, notifying parent`);
|
||||||
|
|
||||||
|
// Find the closest parent with reloadHeartbeatData method (StatusPage)
|
||||||
|
let parent = this.$parent;
|
||||||
|
while (parent && !parent.reloadHeartbeatData) {
|
||||||
|
parent = parent.$parent;
|
||||||
|
}
|
||||||
|
if (parent && parent.reloadHeartbeatData) {
|
||||||
|
parent.reloadHeartbeatData(newMaxBeat);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.maxBeat = newMaxBeat;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -787,10 +787,19 @@ export default {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load heartbeat data from API
|
* Load heartbeat data from API
|
||||||
|
* @param {number|null} maxBeats Maximum number of beats to request from server
|
||||||
* @returns {Promise} Promise that resolves when data is loaded
|
* @returns {Promise} Promise that resolves when data is loaded
|
||||||
*/
|
*/
|
||||||
loadHeartbeatData() {
|
loadHeartbeatData(maxBeats = null) {
|
||||||
return axios.get("/api/status-page/heartbeat/" + this.slug).then((res) => {
|
// If maxBeats is provided (from HeartbeatBar resize), use it
|
||||||
|
// Otherwise, use a default that will be updated when components mount
|
||||||
|
const targetMaxBeats = maxBeats || 50; // Default, will be updated by actual container measurement
|
||||||
|
|
||||||
|
console.log(`HeartBeat Debug: Using maxBeats=${targetMaxBeats}, provided=${maxBeats !== null}`);
|
||||||
|
|
||||||
|
return axios.get("/api/status-page/heartbeat/" + this.slug, {
|
||||||
|
params: { maxBeats: targetMaxBeats }
|
||||||
|
}).then((res) => {
|
||||||
const { heartbeatList, uptimeList } = res.data;
|
const { heartbeatList, uptimeList } = res.data;
|
||||||
|
|
||||||
this.$root.heartbeatList = heartbeatList;
|
this.$root.heartbeatList = heartbeatList;
|
||||||
|
@ -844,6 +853,20 @@ export default {
|
||||||
}, 1000);
|
}, 1000);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reload heartbeat data with specific maxBeats count
|
||||||
|
* Called by child components when they determine optimal beat count
|
||||||
|
* @param {number} maxBeats Maximum number of beats that fit in container
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
reloadHeartbeatData(maxBeats) {
|
||||||
|
// Only reload if we have configured days (not auto mode)
|
||||||
|
if (this.config && this.config.heartbeatBarDays > 0) {
|
||||||
|
console.log(`HeartBeat Debug: Reloading with maxBeats=${maxBeats} for ${this.config.heartbeatBarDays} days`);
|
||||||
|
this.loadHeartbeatData(maxBeats);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable editing mode
|
* Enable editing mode
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
|
|
Loading…
Add table
Reference in a new issue