added validate and other improvements

This commit is contained in:
Doruk 2025-06-14 13:27:34 +02:00
parent ace9ff20b5
commit 0dde42ee36
5 changed files with 59 additions and 49 deletions

View file

@ -433,7 +433,7 @@ class StatusPage extends BeanModel {
showPoweredBy: !!this.show_powered_by, showPoweredBy: !!this.show_powered_by,
googleAnalyticsId: this.google_analytics_tag_id, googleAnalyticsId: this.google_analytics_tag_id,
showCertificateExpiry: !!this.show_certificate_expiry, showCertificateExpiry: !!this.show_certificate_expiry,
heartbeatBarRange: this.heartbeat_bar_range || "auto", heartbeatBarDays: this.heartbeat_bar_days || 0,
}; };
} }

View file

@ -104,40 +104,23 @@ router.get("/api/status-page/heartbeat/:slug", cache("1 minutes"), async (reques
list = R.convertToBeans("heartbeat", list); list = R.convertToBeans("heartbeat", list);
heartbeatList[monitorID] = list.reverse().map(row => row.toPublicJSON()); heartbeatList[monitorID] = list.reverse().map(row => row.toPublicJSON());
} else { } else {
// Use UptimeCalculator for configured day ranges // For configured day ranges, always use raw heartbeat data for client-side aggregation
const uptimeCalculator = await UptimeCalculator.getUptimeCalculator(monitorID); // This ensures consistent behavior between edit mode and published mode
const date = new Date();
date.setDate(date.getDate() - heartbeatBarDays);
const dateFrom = date.toISOString().slice(0, 19).replace("T", " ");
if (heartbeatBarDays <= 1) { let list = await R.getAll(`
// Use 24-hour data SELECT * FROM heartbeat
const data = uptimeCalculator.get24Hour(); WHERE monitor_id = ? AND time >= ?
heartbeatList[monitorID] = Object.entries(data.minutelyUptimeDataList || {}).map(([ timestamp, uptimeData ]) => ({ ORDER BY time DESC
time: dayjs(parseInt(timestamp)).format("YYYY-MM-DD HH:mm:ss"), `, [
status: uptimeData.up > 0 ? 1 : (uptimeData.down > 0 ? 0 : 1), monitorID,
up: uptimeData.up, dateFrom
down: uptimeData.down, ]);
ping: uptimeData.avgPing
})); list = R.convertToBeans("heartbeat", list);
} else if (heartbeatBarDays <= 30) { heartbeatList[monitorID] = list.reverse().map(row => row.toPublicJSON());
// Use 30-day hourly data
const data = uptimeCalculator.get30Day();
heartbeatList[monitorID] = Object.entries(data.hourlyUptimeDataList || {}).map(([ timestamp, uptimeData ]) => ({
time: dayjs(parseInt(timestamp)).format("YYYY-MM-DD HH:mm:ss"),
status: uptimeData.up > 0 ? 1 : (uptimeData.down > 0 ? 0 : 1),
up: uptimeData.up,
down: uptimeData.down,
ping: uptimeData.avgPing
}));
} else {
// Use daily data for longer ranges
const data = uptimeCalculator.getData();
heartbeatList[monitorID] = Object.entries(data.dailyUptimeDataList || {}).map(([ timestamp, uptimeData ]) => ({
time: dayjs(parseInt(timestamp)).format("YYYY-MM-DD HH:mm:ss"),
status: uptimeData.up > 0 ? 1 : (uptimeData.down > 0 ? 0 : 1),
up: uptimeData.up,
down: uptimeData.down,
ping: uptimeData.avgPing
}));
}
} }
const uptimeCalculator = await UptimeCalculator.getUptimeCalculator(monitorID); const uptimeCalculator = await UptimeCalculator.getUptimeCalculator(monitorID);

View file

@ -49,8 +49,12 @@ export default {
}, },
/** Heartbeat bar days */ /** Heartbeat bar days */
heartbeatBarDays: { heartbeatBarDays: {
type: Number, type: [Number, String],
default: 0, default: 0,
validator(value) {
const num = Number(value);
return !isNaN(num) && num >= 0 && num <= 365;
}
} }
}, },
data() { data() {
@ -65,6 +69,15 @@ export default {
}, },
computed: { computed: {
/**
* Normalized heartbeatBarDays as a number
* @returns {number} Number of days for heartbeat bar
*/
normalizedHeartbeatBarDays() {
const num = Number(this.heartbeatBarDays);
return isNaN(num) ? 0 : Math.max(0, Math.min(365, Math.floor(num)));
},
/** /**
* If heartbeatList is null, get it from $root.heartbeatList * If heartbeatList is null, get it from $root.heartbeatList
* @returns {object} Heartbeat list * @returns {object} Heartbeat list
@ -103,12 +116,13 @@ export default {
return []; return [];
} }
// If heartbeat days is configured (not auto), aggregate by time periods // If heartbeat days is configured (not auto), always use client-side aggregation
if (this.heartbeatBarDays > 0) { // This ensures consistent behavior between edit mode and published mode
if (this.normalizedHeartbeatBarDays > 0) {
return this.aggregatedBeatList; return this.aggregatedBeatList;
} }
// Original logic for short time ranges // Original logic for auto mode (heartbeatBarDays = 0)
let placeholders = []; let placeholders = [];
let start = this.beatList.length - this.maxBeat; let start = this.beatList.length - this.maxBeat;
@ -138,7 +152,7 @@ export default {
const buckets = []; const buckets = [];
// Calculate total hours from days // Calculate total hours from days
const totalHours = this.heartbeatBarDays * 24; const totalHours = this.normalizedHeartbeatBarDays * 24;
// Use dynamic maxBeat calculated from screen size // Use dynamic maxBeat calculated from screen size
const totalBuckets = this.maxBeat > 0 ? this.maxBeat : 50; const totalBuckets = this.maxBeat > 0 ? this.maxBeat : 50;
@ -245,7 +259,7 @@ export default {
*/ */
timeStyle() { timeStyle() {
// For aggregated mode, don't use padding-based positioning // For aggregated mode, don't use padding-based positioning
if (this.heartbeatBarDays > 0) { if (this.normalizedHeartbeatBarDays > 0) {
return { return {
"margin-left": "0px", "margin-left": "0px",
}; };
@ -263,11 +277,11 @@ export default {
*/ */
timeSinceFirstBeat() { timeSinceFirstBeat() {
// For aggregated beats, calculate from the configured days // For aggregated beats, calculate from the configured days
if (this.heartbeatBarDays > 0) { if (this.normalizedHeartbeatBarDays > 0) {
if (this.heartbeatBarDays < 2) { if (this.normalizedHeartbeatBarDays < 2) {
return (this.heartbeatBarDays * 24) + "h"; return (this.normalizedHeartbeatBarDays * 24) + "h";
} else { } else {
return this.heartbeatBarDays + "d"; return this.normalizedHeartbeatBarDays + "d";
} }
} }
@ -371,14 +385,15 @@ export default {
return ""; return "";
} }
// For aggregated beats, show time range and status // For aggregated beats (client-side aggregation), show time range and status
if (beat.beats !== undefined && this.heartbeatBarDays > 0) { if (beat.beats !== undefined && this.normalizedHeartbeatBarDays > 0) {
const start = this.$root.datetime(beat.start); const start = this.$root.datetime(beat.start);
const end = this.$root.datetime(beat.end); const end = this.$root.datetime(beat.end);
const statusText = beat.status === 1 ? "Up" : beat.status === 0 ? "Down" : beat.status === 3 ? "Maintenance" : "No Data"; const statusText = beat.status === 1 ? "Up" : beat.status === 0 ? "Down" : beat.status === 3 ? "Maintenance" : "No Data";
return `${start} - ${end}: ${statusText} (${beat.beats.length} checks)`; return `${start} - ${end}: ${statusText} (${beat.beats.length} checks)`;
} }
// For published mode with configured days, show simple timestamp
return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : ""); return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : "");
}, },

View file

@ -117,8 +117,12 @@ export default {
}, },
/** Heartbeat bar days */ /** Heartbeat bar days */
heartbeatBarDays: { heartbeatBarDays: {
type: Number, type: [Number, String],
default: 0, default: 0,
validator(value) {
const num = Number(value);
return !isNaN(num) && num >= 0 && num <= 365;
}
} }
}, },
data() { data() {

View file

@ -627,6 +627,12 @@ export default {
if (res.ok) { if (res.ok) {
this.config = res.config; this.config = res.config;
if (this.config.heartbeatBarDays === undefined || this.config.heartbeatBarDays === null || this.config.heartbeatBarDays === "") {
this.config.heartbeatBarDays = 0;
} else {
this.config.heartbeatBarDays = parseInt(this.config.heartbeatBarDays, 10) || 0;
}
if (!this.config.customCSS) { if (!this.config.customCSS) {
this.config.customCSS = "body {\n" + this.config.customCSS = "body {\n" +
" \n" + " \n" +
@ -718,8 +724,10 @@ export default {
this.config.domainNameList = []; this.config.domainNameList = [];
} }
if (!this.config.heartbeatBarRange) { if (this.config.heartbeatBarDays === undefined || this.config.heartbeatBarDays === null || this.config.heartbeatBarDays === "") {
this.config.heartbeatBarRange = "auto"; this.config.heartbeatBarDays = 0;
} else {
this.config.heartbeatBarDays = parseInt(this.config.heartbeatBarDays, 10) || 0;
} }
if (this.config.icon) { if (this.config.icon) {