mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-06-14 16:42:35 +02:00
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:
parent
5a80157e12
commit
d893ebe650
1 changed files with 159 additions and 161 deletions
|
@ -1,223 +1,221 @@
|
||||||
let express = require("express");
|
let express = require('express');
|
||||||
const { allowDevAllOrigin, getSettings, setting } = require("../util-server");
|
const { allowDevAllOrigin, getSettings, setting } = require('../util-server');
|
||||||
const { R } = require("redbean-node");
|
const { R } = require('redbean-node');
|
||||||
const server = require("../server");
|
const server = require('../server');
|
||||||
const apicache = require("../modules/apicache");
|
const apicache = require('../modules/apicache');
|
||||||
const Monitor = require("../model/monitor");
|
const Monitor = require('../model/monitor');
|
||||||
const dayjs = require("dayjs");
|
const dayjs = require('dayjs');
|
||||||
const { UP, flipStatus, debug } = require("../../src/util");
|
const { UP, flipStatus, debug, DOWN } = require('../../src/util');
|
||||||
let router = express.Router();
|
let router = express.Router();
|
||||||
|
|
||||||
let cache = apicache.middleware;
|
let cache = apicache.middleware;
|
||||||
let io = server.io;
|
let io = server.io;
|
||||||
|
|
||||||
router.get("/api/entry-page", async (_, response) => {
|
router.get('/api/entry-page', async (_, response) => {
|
||||||
allowDevAllOrigin(response);
|
allowDevAllOrigin(response);
|
||||||
response.json(server.entryPage);
|
response.json(server.entryPage);
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/api/push/:pushToken", async (request, response) => {
|
router.get('/api/push/:pushToken', async (request, response) => {
|
||||||
try {
|
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 monitor = await R.findOne('monitor', ' push_token = ? AND active = 1 ', [pushToken]);
|
||||||
let msg = request.query.msg || "OK";
|
|
||||||
let ping = request.query.ping || null;
|
|
||||||
|
|
||||||
let monitor = await R.findOne("monitor", " push_token = ? AND active = 1 ", [
|
if (!monitor) {
|
||||||
pushToken
|
throw new Error('Monitor not found or not active.');
|
||||||
]);
|
}
|
||||||
|
|
||||||
if (! monitor) {
|
const previousHeartbeat = await Monitor.getPreviousHeartbeat(monitor.id);
|
||||||
throw new Error("Monitor not found or not active.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const previousHeartbeat = await Monitor.getPreviousHeartbeat(monitor.id);
|
let status = health === 'UP' ? UP : DOWN;
|
||||||
|
if (monitor.isUpsideDown()) {
|
||||||
|
status = flipStatus(status);
|
||||||
|
}
|
||||||
|
|
||||||
let status = UP;
|
let isFirstBeat = true;
|
||||||
if (monitor.isUpsideDown()) {
|
let previousStatus = status;
|
||||||
status = flipStatus(status);
|
let duration = 0;
|
||||||
}
|
|
||||||
|
|
||||||
let isFirstBeat = true;
|
let bean = R.dispense('heartbeat');
|
||||||
let previousStatus = status;
|
bean.time = R.isoDateTime(dayjs.utc());
|
||||||
let duration = 0;
|
|
||||||
|
|
||||||
let bean = R.dispense("heartbeat");
|
if (previousHeartbeat) {
|
||||||
bean.time = R.isoDateTime(dayjs.utc());
|
isFirstBeat = false;
|
||||||
|
previousStatus = previousHeartbeat.status;
|
||||||
|
duration = dayjs(bean.time).diff(dayjs(previousHeartbeat.time), 'second');
|
||||||
|
}
|
||||||
|
|
||||||
if (previousHeartbeat) {
|
debug('PreviousStatus: ' + previousStatus);
|
||||||
isFirstBeat = false;
|
debug('Current Status: ' + status);
|
||||||
previousStatus = previousHeartbeat.status;
|
|
||||||
duration = dayjs(bean.time).diff(dayjs(previousHeartbeat.time), "second");
|
|
||||||
}
|
|
||||||
|
|
||||||
debug("PreviousStatus: " + previousStatus);
|
bean.important = Monitor.isImportantBeat(isFirstBeat, previousStatus, status);
|
||||||
debug("Current Status: " + 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);
|
await R.store(bean);
|
||||||
bean.monitor_id = monitor.id;
|
|
||||||
bean.status = status;
|
|
||||||
bean.msg = msg;
|
|
||||||
bean.ping = ping;
|
|
||||||
bean.duration = duration;
|
|
||||||
|
|
||||||
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());
|
response.json({
|
||||||
Monitor.sendStats(io, monitor.id, monitor.user_id);
|
ok: true,
|
||||||
|
});
|
||||||
|
|
||||||
response.json({
|
if (bean.important) {
|
||||||
ok: true,
|
await Monitor.sendNotification(isFirstBeat, monitor, bean);
|
||||||
});
|
}
|
||||||
|
} catch (e) {
|
||||||
if (bean.important) {
|
response.json({
|
||||||
await Monitor.sendNotification(isFirstBeat, monitor, bean);
|
ok: false,
|
||||||
}
|
msg: e.message,
|
||||||
|
});
|
||||||
} catch (e) {
|
}
|
||||||
response.json({
|
|
||||||
ok: false,
|
|
||||||
msg: e.message
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Status Page Config
|
// Status Page Config
|
||||||
router.get("/api/status-page/config", async (_request, response) => {
|
router.get('/api/status-page/config', async (_request, response) => {
|
||||||
allowDevAllOrigin(response);
|
allowDevAllOrigin(response);
|
||||||
|
|
||||||
let config = await getSettings("statusPage");
|
let config = await getSettings('statusPage');
|
||||||
|
|
||||||
if (! config.statusPageTheme) {
|
if (!config.statusPageTheme) {
|
||||||
config.statusPageTheme = "light";
|
config.statusPageTheme = 'light';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! config.statusPagePublished) {
|
if (!config.statusPagePublished) {
|
||||||
config.statusPagePublished = true;
|
config.statusPagePublished = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! config.statusPageTags) {
|
if (!config.statusPageTags) {
|
||||||
config.statusPageTags = false;
|
config.statusPageTags = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! config.title) {
|
if (!config.title) {
|
||||||
config.title = "Uptime Kuma";
|
config.title = 'Uptime Kuma';
|
||||||
}
|
}
|
||||||
|
|
||||||
response.json(config);
|
response.json(config);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Status Page - Get the current Incident
|
// Status Page - Get the current Incident
|
||||||
// Can fetch only if published
|
// Can fetch only if published
|
||||||
router.get("/api/status-page/incident", async (_, response) => {
|
router.get('/api/status-page/incident', async (_, response) => {
|
||||||
allowDevAllOrigin(response);
|
allowDevAllOrigin(response);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await checkPublished();
|
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) {
|
if (incident) {
|
||||||
incident = incident.toPublicJSON();
|
incident = incident.toPublicJSON();
|
||||||
}
|
}
|
||||||
|
|
||||||
response.json({
|
response.json({
|
||||||
ok: true,
|
ok: true,
|
||||||
incident,
|
incident,
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
} catch (error) {
|
send403(response, error.message);
|
||||||
send403(response, error.message);
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Status Page - Monitor List
|
// Status Page - Monitor List
|
||||||
// Can fetch only if published
|
// Can fetch only if published
|
||||||
router.get("/api/status-page/monitor-list", cache("5 minutes"), async (_request, response) => {
|
router.get('/api/status-page/monitor-list', cache('5 minutes'), async (_request, response) => {
|
||||||
allowDevAllOrigin(response);
|
allowDevAllOrigin(response);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await checkPublished();
|
await checkPublished();
|
||||||
const publicGroupList = [];
|
const publicGroupList = [];
|
||||||
const tagsVisible = (await getSettings("statusPage")).statusPageTags;
|
const tagsVisible = (await getSettings('statusPage')).statusPageTags;
|
||||||
const list = await R.find("group", " public = 1 ORDER BY weight ");
|
const list = await R.find('group', ' public = 1 ORDER BY weight ');
|
||||||
for (let groupBean of list) {
|
for (let groupBean of list) {
|
||||||
let monitorGroup = await groupBean.toPublicJSON();
|
let monitorGroup = await groupBean.toPublicJSON();
|
||||||
if (tagsVisible) {
|
if (tagsVisible) {
|
||||||
monitorGroup.monitorList = await Promise.all(monitorGroup.monitorList.map(async (monitor) => {
|
monitorGroup.monitorList = await Promise.all(
|
||||||
// Includes tags as an array in response, allows for tags to be displayed on public status page
|
monitorGroup.monitorList.map(async (monitor) => {
|
||||||
const tags = await R.getAll(
|
// Includes tags as an array in response, allows for tags to be displayed on public status page
|
||||||
`SELECT monitor_tag.monitor_id, monitor_tag.value, tag.name, tag.color
|
const tags = await R.getAll(
|
||||||
|
`SELECT monitor_tag.monitor_id, monitor_tag.value, tag.name, tag.color
|
||||||
FROM monitor_tag
|
FROM monitor_tag
|
||||||
JOIN tag
|
JOIN tag
|
||||||
ON monitor_tag.tag_id = tag.id
|
ON monitor_tag.tag_id = tag.id
|
||||||
WHERE monitor_tag.monitor_id = ?`, [monitor.id]
|
WHERE monitor_tag.monitor_id = ?`,
|
||||||
);
|
[monitor.id]
|
||||||
return {
|
);
|
||||||
...monitor,
|
return {
|
||||||
tags: tags
|
...monitor,
|
||||||
};
|
tags: tags,
|
||||||
}));
|
};
|
||||||
}
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
publicGroupList.push(monitorGroup);
|
publicGroupList.push(monitorGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
response.json(publicGroupList);
|
response.json(publicGroupList);
|
||||||
|
} catch (error) {
|
||||||
} catch (error) {
|
send403(response, error.message);
|
||||||
send403(response, error.message);
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Status Page Polling Data
|
// Status Page Polling Data
|
||||||
// Can fetch only if published
|
// Can fetch only if published
|
||||||
router.get("/api/status-page/heartbeat", cache("5 minutes"), async (_request, response) => {
|
router.get('/api/status-page/heartbeat', cache('5 minutes'), async (_request, response) => {
|
||||||
allowDevAllOrigin(response);
|
allowDevAllOrigin(response);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await checkPublished();
|
await checkPublished();
|
||||||
|
|
||||||
let heartbeatList = {};
|
let heartbeatList = {};
|
||||||
let uptimeList = {};
|
let uptimeList = {};
|
||||||
|
|
||||||
let monitorIDList = await R.getCol(`
|
let monitorIDList = await R.getCol(`
|
||||||
SELECT monitor_group.monitor_id FROM monitor_group, \`group\`
|
SELECT monitor_group.monitor_id FROM monitor_group, \`group\`
|
||||||
WHERE monitor_group.group_id = \`group\`.id
|
WHERE monitor_group.group_id = \`group\`.id
|
||||||
AND public = 1
|
AND public = 1
|
||||||
`);
|
`);
|
||||||
|
|
||||||
for (let monitorID of monitorIDList) {
|
for (let monitorID of monitorIDList) {
|
||||||
let list = await R.getAll(`
|
let list = await R.getAll(
|
||||||
|
`
|
||||||
SELECT * FROM heartbeat
|
SELECT * FROM heartbeat
|
||||||
WHERE monitor_id = ?
|
WHERE monitor_id = ?
|
||||||
ORDER BY time DESC
|
ORDER BY time DESC
|
||||||
LIMIT 50
|
LIMIT 50
|
||||||
`, [
|
`,
|
||||||
monitorID,
|
[monitorID]
|
||||||
]);
|
);
|
||||||
|
|
||||||
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());
|
||||||
|
|
||||||
const type = 24;
|
const type = 24;
|
||||||
uptimeList[`${monitorID}_${type}`] = await Monitor.calcUptime(type, monitorID);
|
uptimeList[`${monitorID}_${type}`] = await Monitor.calcUptime(type, monitorID);
|
||||||
}
|
}
|
||||||
|
|
||||||
response.json({
|
response.json({
|
||||||
heartbeatList,
|
heartbeatList,
|
||||||
uptimeList
|
uptimeList,
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
} catch (error) {
|
send403(response, error.message);
|
||||||
send403(response, error.message);
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
async function checkPublished() {
|
async function checkPublished() {
|
||||||
if (! await isPublished()) {
|
if (!(await isPublished())) {
|
||||||
throw new Error("The status page is not published");
|
throw new Error('The status page is not published');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -225,18 +223,18 @@ async function checkPublished() {
|
||||||
* @returns {Promise<boolean>}
|
* @returns {Promise<boolean>}
|
||||||
*/
|
*/
|
||||||
async function isPublished() {
|
async function isPublished() {
|
||||||
const value = await setting("statusPagePublished");
|
const value = await setting('statusPagePublished');
|
||||||
if (value === null) {
|
if (value === null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function send403(res, msg = "") {
|
function send403(res, msg = '') {
|
||||||
res.status(403).json({
|
res.status(403).json({
|
||||||
"status": "fail",
|
status: 'fail',
|
||||||
"msg": msg,
|
msg: msg,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
Loading…
Add table
Reference in a new issue