From d893ebe65052bc5f249679f3a2d518e0dbef4ebe Mon Sep 17 00:00:00 2001 From: Claudius Orsmond Date: Wed, 15 Dec 2021 22:14:10 +0200 Subject: [PATCH] Added optional health query parameter to push events. When nothing is specified then UP is used. If anything but UP is used, then it is considered DOWN --- server/routers/api-router.js | 320 +++++++++++++++++------------------ 1 file changed, 159 insertions(+), 161 deletions(-) diff --git a/server/routers/api-router.js b/server/routers/api-router.js index 1920cef71..d27c84c5d 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -1,223 +1,221 @@ -let express = require("express"); -const { allowDevAllOrigin, getSettings, setting } = require("../util-server"); -const { R } = require("redbean-node"); -const server = require("../server"); -const apicache = require("../modules/apicache"); -const Monitor = require("../model/monitor"); -const dayjs = require("dayjs"); -const { UP, flipStatus, debug } = require("../../src/util"); +let express = require('express'); +const { allowDevAllOrigin, getSettings, setting } = require('../util-server'); +const { R } = require('redbean-node'); +const server = require('../server'); +const apicache = require('../modules/apicache'); +const Monitor = require('../model/monitor'); +const dayjs = require('dayjs'); +const { UP, flipStatus, debug, DOWN } = require('../../src/util'); let router = express.Router(); let cache = apicache.middleware; let io = server.io; -router.get("/api/entry-page", async (_, response) => { - allowDevAllOrigin(response); - response.json(server.entryPage); +router.get('/api/entry-page', async (_, response) => { + allowDevAllOrigin(response); + response.json(server.entryPage); }); -router.get("/api/push/:pushToken", async (request, response) => { - try { +router.get('/api/push/:pushToken', async (request, response) => { + try { + let pushToken = request.params.pushToken; + let msg = request.query.msg || 'OK'; + let ping = request.query.ping || null; + let health = request.query.health || 'UP'; // Backwards compatible, so if nothing was specified it will work as always - let pushToken = request.params.pushToken; - let msg = request.query.msg || "OK"; - let ping = request.query.ping || null; + let monitor = await R.findOne('monitor', ' push_token = ? AND active = 1 ', [pushToken]); - let monitor = await R.findOne("monitor", " push_token = ? AND active = 1 ", [ - pushToken - ]); + if (!monitor) { + throw new Error('Monitor not found or not active.'); + } - if (! monitor) { - throw new Error("Monitor not found or not active."); - } + const previousHeartbeat = await Monitor.getPreviousHeartbeat(monitor.id); - const previousHeartbeat = await Monitor.getPreviousHeartbeat(monitor.id); + let status = health === 'UP' ? UP : DOWN; + if (monitor.isUpsideDown()) { + status = flipStatus(status); + } - let status = UP; - if (monitor.isUpsideDown()) { - status = flipStatus(status); - } + let isFirstBeat = true; + let previousStatus = status; + let duration = 0; - let isFirstBeat = true; - let previousStatus = status; - let duration = 0; + let bean = R.dispense('heartbeat'); + bean.time = R.isoDateTime(dayjs.utc()); - let bean = R.dispense("heartbeat"); - bean.time = R.isoDateTime(dayjs.utc()); + if (previousHeartbeat) { + isFirstBeat = false; + previousStatus = previousHeartbeat.status; + duration = dayjs(bean.time).diff(dayjs(previousHeartbeat.time), 'second'); + } - if (previousHeartbeat) { - isFirstBeat = false; - previousStatus = previousHeartbeat.status; - duration = dayjs(bean.time).diff(dayjs(previousHeartbeat.time), "second"); - } + debug('PreviousStatus: ' + previousStatus); + debug('Current Status: ' + status); - 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 = duration; - bean.important = Monitor.isImportantBeat(isFirstBeat, previousStatus, status); - bean.monitor_id = monitor.id; - bean.status = status; - bean.msg = msg; - bean.ping = ping; - bean.duration = duration; + await R.store(bean); - await R.store(bean); + io.to(monitor.user_id).emit('heartbeat', bean.toJSON()); + Monitor.sendStats(io, monitor.id, monitor.user_id); - io.to(monitor.user_id).emit("heartbeat", bean.toJSON()); - Monitor.sendStats(io, monitor.id, monitor.user_id); + response.json({ + ok: true, + }); - response.json({ - ok: true, - }); - - if (bean.important) { - await Monitor.sendNotification(isFirstBeat, monitor, bean); - } - - } catch (e) { - response.json({ - ok: false, - msg: e.message - }); - } + if (bean.important) { + await Monitor.sendNotification(isFirstBeat, monitor, bean); + } + } catch (e) { + response.json({ + ok: false, + msg: e.message, + }); + } }); // Status Page Config -router.get("/api/status-page/config", async (_request, response) => { - allowDevAllOrigin(response); +router.get('/api/status-page/config', async (_request, response) => { + allowDevAllOrigin(response); - let config = await getSettings("statusPage"); + let config = await getSettings('statusPage'); - if (! config.statusPageTheme) { - config.statusPageTheme = "light"; - } + if (!config.statusPageTheme) { + config.statusPageTheme = 'light'; + } - if (! config.statusPagePublished) { - config.statusPagePublished = true; - } + if (!config.statusPagePublished) { + config.statusPagePublished = true; + } - if (! config.statusPageTags) { - config.statusPageTags = false; - } + if (!config.statusPageTags) { + config.statusPageTags = false; + } - if (! config.title) { - config.title = "Uptime Kuma"; - } + if (!config.title) { + config.title = 'Uptime Kuma'; + } - response.json(config); + response.json(config); }); // Status Page - Get the current Incident // Can fetch only if published -router.get("/api/status-page/incident", async (_, response) => { - allowDevAllOrigin(response); +router.get('/api/status-page/incident', async (_, response) => { + allowDevAllOrigin(response); - try { - await checkPublished(); + try { + await checkPublished(); - let incident = await R.findOne("incident", " pin = 1 AND active = 1"); + let incident = await R.findOne('incident', ' pin = 1 AND active = 1'); - if (incident) { - incident = incident.toPublicJSON(); - } + if (incident) { + incident = incident.toPublicJSON(); + } - response.json({ - ok: true, - incident, - }); - - } catch (error) { - send403(response, error.message); - } + response.json({ + ok: true, + incident, + }); + } catch (error) { + send403(response, error.message); + } }); // Status Page - Monitor List // Can fetch only if published -router.get("/api/status-page/monitor-list", cache("5 minutes"), async (_request, response) => { - allowDevAllOrigin(response); +router.get('/api/status-page/monitor-list', cache('5 minutes'), async (_request, response) => { + allowDevAllOrigin(response); - try { - await checkPublished(); - const publicGroupList = []; - const tagsVisible = (await getSettings("statusPage")).statusPageTags; - const list = await R.find("group", " public = 1 ORDER BY weight "); - for (let groupBean of list) { - let monitorGroup = await groupBean.toPublicJSON(); - if (tagsVisible) { - monitorGroup.monitorList = await Promise.all(monitorGroup.monitorList.map(async (monitor) => { - // Includes tags as an array in response, allows for tags to be displayed on public status page - const tags = await R.getAll( - `SELECT monitor_tag.monitor_id, monitor_tag.value, tag.name, tag.color + try { + await checkPublished(); + const publicGroupList = []; + const tagsVisible = (await getSettings('statusPage')).statusPageTags; + const list = await R.find('group', ' public = 1 ORDER BY weight '); + for (let groupBean of list) { + let monitorGroup = await groupBean.toPublicJSON(); + if (tagsVisible) { + monitorGroup.monitorList = await Promise.all( + monitorGroup.monitorList.map(async (monitor) => { + // Includes tags as an array in response, allows for tags to be displayed on public status page + const tags = await R.getAll( + `SELECT monitor_tag.monitor_id, monitor_tag.value, tag.name, tag.color FROM monitor_tag JOIN tag ON monitor_tag.tag_id = tag.id - WHERE monitor_tag.monitor_id = ?`, [monitor.id] - ); - return { - ...monitor, - tags: tags - }; - })); - } + WHERE monitor_tag.monitor_id = ?`, + [monitor.id] + ); + return { + ...monitor, + tags: tags, + }; + }) + ); + } - publicGroupList.push(monitorGroup); - } + publicGroupList.push(monitorGroup); + } - response.json(publicGroupList); - - } catch (error) { - send403(response, error.message); - } + response.json(publicGroupList); + } catch (error) { + send403(response, error.message); + } }); // Status Page Polling Data // Can fetch only if published -router.get("/api/status-page/heartbeat", cache("5 minutes"), async (_request, response) => { - allowDevAllOrigin(response); +router.get('/api/status-page/heartbeat', cache('5 minutes'), async (_request, response) => { + allowDevAllOrigin(response); - try { - await checkPublished(); + try { + await checkPublished(); - let heartbeatList = {}; - let uptimeList = {}; + let heartbeatList = {}; + let uptimeList = {}; - let monitorIDList = await R.getCol(` + let monitorIDList = await R.getCol(` SELECT monitor_group.monitor_id FROM monitor_group, \`group\` WHERE monitor_group.group_id = \`group\`.id AND public = 1 `); - for (let monitorID of monitorIDList) { - let list = await R.getAll(` + for (let monitorID of monitorIDList) { + let list = await R.getAll( + ` SELECT * FROM heartbeat WHERE monitor_id = ? ORDER BY time DESC LIMIT 50 - `, [ - monitorID, - ]); + `, + [monitorID] + ); - list = R.convertToBeans("heartbeat", list); - heartbeatList[monitorID] = list.reverse().map(row => row.toPublicJSON()); + list = R.convertToBeans('heartbeat', list); + heartbeatList[monitorID] = list.reverse().map((row) => row.toPublicJSON()); - const type = 24; - uptimeList[`${monitorID}_${type}`] = await Monitor.calcUptime(type, monitorID); - } + const type = 24; + uptimeList[`${monitorID}_${type}`] = await Monitor.calcUptime(type, monitorID); + } - response.json({ - heartbeatList, - uptimeList - }); - - } catch (error) { - send403(response, error.message); - } + response.json({ + heartbeatList, + uptimeList, + }); + } catch (error) { + send403(response, error.message); + } }); async function checkPublished() { - if (! await isPublished()) { - throw new Error("The status page is not published"); - } + if (!(await isPublished())) { + throw new Error('The status page is not published'); + } } /** @@ -225,18 +223,18 @@ async function checkPublished() { * @returns {Promise} */ async function isPublished() { - const value = await setting("statusPagePublished"); - if (value === null) { - return true; - } - return value; + const value = await setting('statusPagePublished'); + if (value === null) { + return true; + } + return value; } -function send403(res, msg = "") { - res.status(403).json({ - "status": "fail", - "msg": msg, - }); +function send403(res, msg = '') { + res.status(403).json({ + status: 'fail', + msg: msg, + }); } module.exports = router;