diff --git a/server/model/monitor.js b/server/model/monitor.js index 16181527..9fe6ffd4 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -292,54 +292,13 @@ class Monitor extends BeanModel { let beatInterval = this.interval; - // * ? -> ANY STATUS = important [isFirstBeat] - // UP -> PENDING = not important - // * UP -> DOWN = important - // UP -> UP = not important - // PENDING -> PENDING = not important - // * PENDING -> DOWN = important - // PENDING -> UP = not important - // DOWN -> PENDING = this case not exists - // DOWN -> DOWN = not important - // * DOWN -> UP = important - let isImportant = isFirstBeat || - (previousBeat.status === UP && bean.status === DOWN) || - (previousBeat.status === DOWN && bean.status === UP) || - (previousBeat.status === PENDING && bean.status === DOWN); + let isImportant = Monitor.isImportantBeat(isFirstBeat, previousBeat.status, bean.status); // Mark as important if status changed, ignore pending pings, // Don't notify if disrupted changes to up if (isImportant) { bean.important = true; - - // Send only if the first beat is DOWN - if (!isFirstBeat || bean.status === DOWN) { - let notificationList = await R.getAll("SELECT notification.* FROM notification, monitor_notification WHERE monitor_id = ? AND monitor_notification.notification_id = notification.id ", [ - this.id, - ]); - - let text; - if (bean.status === UP) { - text = "✅ Up"; - } else { - text = "🔴 Down"; - } - - let msg = `[${this.name}] [${text}] ${bean.msg}`; - - for (let notification of notificationList) { - try { - await Notification.send(JSON.parse(notification.config), msg, await this.toJSON(), bean.toJSON()); - } catch (e) { - console.error("Cannot send notification to " + notification.name); - console.log(e); - } - } - - // Clear Status Page Cache - apicache.clear(); - } - + await Monitor.sendNotification(isFirstBeat, this, bean); } else { bean.important = false; } @@ -546,6 +505,53 @@ class Monitor extends BeanModel { io.to(userID).emit("uptime", monitorID, duration, uptime); } + static isImportantBeat(isFirstBeat, previousBeatStatus, currentBeatStatus) { + // * ? -> ANY STATUS = important [isFirstBeat] + // UP -> PENDING = not important + // * UP -> DOWN = important + // UP -> UP = not important + // PENDING -> PENDING = not important + // * PENDING -> DOWN = important + // PENDING -> UP = not important + // DOWN -> PENDING = this case not exists + // DOWN -> DOWN = not important + // * DOWN -> UP = important + let isImportant = isFirstBeat || + (previousBeatStatus === UP && currentBeatStatus === DOWN) || + (previousBeatStatus === DOWN && currentBeatStatus === UP) || + (previousBeatStatus === PENDING && currentBeatStatus === DOWN); + return isImportant; + } + + static async sendNotification(isFirstBeat, monitor, bean) { + if (!isFirstBeat || bean.status === DOWN) { + let notificationList = await R.getAll("SELECT notification.* FROM notification, monitor_notification WHERE monitor_id = ? AND monitor_notification.notification_id = notification.id ", [ + monitor.id, + ]); + + let text; + if (bean.status === UP) { + text = "✅ Up"; + } else { + text = "🔴 Down"; + } + + let msg = `[${monitor.name}] [${text}] ${bean.msg}`; + + for (let notification of notificationList) { + try { + await Notification.send(JSON.parse(notification.config), msg, await monitor.toJSON(), bean.toJSON()); + } catch (e) { + console.error("Cannot send notification to " + notification.name); + console.log(e); + } + } + + // Clear Status Page Cache + apicache.clear(); + } + } + } module.exports = Monitor; diff --git a/server/routers/api-router.js b/server/routers/api-router.js index 927a5464..fbe8136e 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -5,7 +5,7 @@ const server = require("../server"); const apicache = require("../modules/apicache"); const Monitor = require("../model/monitor"); const dayjs = require("dayjs"); -const { UP } = require("../../src/util"); +const { UP, flipStatus, debug } = require("../../src/util"); let router = express.Router(); let cache = apicache.middleware; @@ -21,7 +21,7 @@ router.get("/api/push/:pushToken", async (request, response) => { let pushToken = request.params.pushToken; let msg = request.query.msg || "OK"; - let ping = request.query.ping; + let ping = request.query.ping || null; let monitor = await R.findOne("monitor", " push_token = ? AND active = 1 ", [ pushToken @@ -31,20 +31,40 @@ router.get("/api/push/:pushToken", async (request, response) => { throw new Error("Monitor not found or not active."); } - const previousHeartbeatTime = await R.getCell(` - SELECT time FROM heartbeat + const previousHeartbeat = await R.getRow(` + SELECT status, time FROM heartbeat WHERE id = (select MAX(id) from heartbeat where monitor_id = ?) `, [ monitor.id ]); + let status = UP; + if (monitor.isUpsideDown()) { + status = flipStatus(status); + } + + let isFirstBeat = true; + let previousStatus = status; + let duration = 0; + let bean = R.dispense("heartbeat"); - bean.monitor_id = monitor.id; bean.time = R.isoDateTime(dayjs.utc()); - bean.status = UP; + + if (previousHeartbeat) { + isFirstBeat = false; + previousStatus = previousHeartbeat.status; + duration = dayjs(bean.time).diff(dayjs(previousHeartbeat.time), "second"); + } + + debug("PreviousStatus: " + previousStatus); + debug("Current Status: " + status); + + bean.important = Monitor.isImportantBeat(isFirstBeat, previousStatus, status); + bean.monitor_id = monitor.id; + bean.status = status; bean.msg = msg; bean.ping = ping; - bean.duration = dayjs(bean.time).diff(dayjs(previousHeartbeatTime), "second"); + bean.duration = duration; await R.store(bean); @@ -54,6 +74,11 @@ router.get("/api/push/:pushToken", async (request, response) => { response.json({ ok: true, }); + + if (bean.important) { + await Monitor.sendNotification(isFirstBeat, monitor, bean); + } + } catch (e) { response.json({ ok: false, diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index b0b0012f..4118d1cf 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -140,7 +140,7 @@ -
+