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
This commit is contained in:
Claudius Orsmond 2021-12-15 22:14:10 +02:00
parent 5a80157e12
commit d893ebe650

View file

@ -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<boolean>}
*/
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;