diff --git a/server/model/monitor.js b/server/model/monitor.js index 2ad572e53..f43a9ac04 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -46,6 +46,7 @@ const { const { R } = require("redbean-node"); const { BeanModel } = require("redbean-node/dist/bean-model"); const { Notification } = require("../notification"); +const NotificationProvider = require("../notification-providers/notification-provider"); const { Proxy } = require("../proxy"); const { demoMode } = require("../config"); const version = require("../../package.json").version; @@ -1525,6 +1526,14 @@ class Monitor extends BeanModel { if (lastDownHeartbeat && lastDownHeartbeat.time) { heartbeatJSON["lastDownTime"] = lastDownHeartbeat.time; + + const downTimeSeconds = Math.floor( + (new Date(heartbeatJSON["time"]).getTime() - new Date(lastDownHeartbeat.time).getTime()) / 1000 + ); + if (downTimeSeconds >= 0) { + heartbeatJSON["downtimeDuration"] = NotificationProvider.formatDuration(downTimeSeconds); + msg += ` (Downtime: ${heartbeatJSON["downtimeDuration"]})`; + } } } catch (error) { // If we can't calculate downtime, just continue without it diff --git a/server/notification-providers/discord.js b/server/notification-providers/discord.js index e6e39d5ef..479ae45b6 100644 --- a/server/notification-providers/discord.js +++ b/server/notification-providers/discord.js @@ -174,12 +174,10 @@ class Discord extends NotificationProvider { await axios.post(webhookUrl.toString(), discorddowndata, config); return okMsg; } else if (heartbeatJSON["status"] === UP) { - const backOnlineTimestamp = Math.floor(new Date(heartbeatJSON["time"]).getTime() / 1000); - let downtimeDuration = null; + let downtimeDuration = heartbeatJSON["downtimeDuration"] || null; let wentOfflineTimestamp = null; if (heartbeatJSON["lastDownTime"]) { wentOfflineTimestamp = Math.floor(new Date(heartbeatJSON["lastDownTime"]).getTime() / 1000); - downtimeDuration = this.formatDuration(backOnlineTimestamp - wentOfflineTimestamp); } let discordupdata = { @@ -259,31 +257,6 @@ class Discord extends NotificationProvider { } } - /** - * Format duration as human-readable string (e.g., "1h 23m", "45m 30s") - * TODO: Update below to `Intl.DurationFormat("en", { style: "short" }).format(duration)` once we are on a newer node version - * @param {number} timeInSeconds The time in seconds to format a duration for - * @returns {string} The formatted duration - */ - formatDuration(timeInSeconds) { - const hours = Math.floor(timeInSeconds / 3600); - const minutes = Math.floor((timeInSeconds % 3600) / 60); - const seconds = timeInSeconds % 60; - - const durationParts = []; - if (hours > 0) { - durationParts.push(`${hours}h`); - } - if (minutes > 0) { - durationParts.push(`${minutes}m`); - } - if (seconds > 0 && hours === 0) { - // Only show seconds if less than an hour - durationParts.push(`${seconds}s`); - } - - return durationParts.length > 0 ? durationParts.join(" ") : "0s"; - } } module.exports = Discord; diff --git a/server/notification-providers/fluxer.js b/server/notification-providers/fluxer.js index 421f77465..ec07befac 100644 --- a/server/notification-providers/fluxer.js +++ b/server/notification-providers/fluxer.js @@ -143,12 +143,10 @@ class Fluxer extends NotificationProvider { await axios.post(webhookUrl.toString(), fluxerdowndata, config); return okMsg; } else if (heartbeatJSON["status"] === UP) { - const backOnlineTimestamp = Math.floor(new Date(heartbeatJSON["time"]).getTime() / 1000); - let downtimeDuration = null; + let downtimeDuration = heartbeatJSON["downtimeDuration"] || null; let wentOfflineTimestamp = null; if (heartbeatJSON["lastDownTime"]) { wentOfflineTimestamp = Math.floor(new Date(heartbeatJSON["lastDownTime"]).getTime() / 1000); - downtimeDuration = this.formatDuration(backOnlineTimestamp - wentOfflineTimestamp); } let fluxerupdata = { @@ -219,31 +217,6 @@ class Fluxer extends NotificationProvider { } } - /** - * Format duration as human-readable string (e.g., "1h 23m", "45m 30s") - * TODO: Update below to `Intl.DurationFormat("en", { style: "short" }).format(duration)` once we are on a newer node version - * @param {number} timeInSeconds The time in seconds to format a duration for - * @returns {string} The formatted duration - */ - formatDuration(timeInSeconds) { - const hours = Math.floor(timeInSeconds / 3600); - const minutes = Math.floor((timeInSeconds % 3600) / 60); - const seconds = timeInSeconds % 60; - - const durationParts = []; - if (hours > 0) { - durationParts.push(`${hours}h`); - } - if (minutes > 0) { - durationParts.push(`${minutes}m`); - } - if (seconds > 0 && hours === 0) { - // Only show seconds if less than an hour - durationParts.push(`${seconds}s`); - } - - return durationParts.length > 0 ? durationParts.join(" ") : "0s"; - } } module.exports = Fluxer; diff --git a/server/notification-providers/notification-provider.js b/server/notification-providers/notification-provider.js index 42079176c..08ab6b86f 100644 --- a/server/notification-providers/notification-provider.js +++ b/server/notification-providers/notification-provider.js @@ -194,6 +194,30 @@ class NotificationProvider { } return axiosConfig; } + /** + * Format duration as human-readable string (e.g., "1h 23m", "45m 30s") + * TODO: Update below to `Intl.DurationFormat("en", { style: "short" }).format(duration)` once we are on a newer node version + * @param {number} timeInSeconds The time in seconds to format a duration for + * @returns {string} The formatted duration + */ + static formatDuration(timeInSeconds) { + const hours = Math.floor(timeInSeconds / 3600); + const minutes = Math.floor((timeInSeconds % 3600) / 60); + const seconds = timeInSeconds % 60; + + const durationParts = []; + if (hours > 0) { + durationParts.push(`${hours}h`); + } + if (minutes > 0) { + durationParts.push(`${minutes}m`); + } + if (seconds > 0 && hours === 0) { + durationParts.push(`${seconds}s`); + } + + return durationParts.length > 0 ? durationParts.join(" ") : "0s"; + } } module.exports = NotificationProvider;