summaryrefslogtreecommitdiffstats
path: root/server/routers/status-page-router.js
diff options
context:
space:
mode:
Diffstat (limited to 'server/routers/status-page-router.js')
-rw-r--r--server/routers/status-page-router.js241
1 files changed, 241 insertions, 0 deletions
diff --git a/server/routers/status-page-router.js b/server/routers/status-page-router.js
new file mode 100644
index 0000000..b209d33
--- /dev/null
+++ b/server/routers/status-page-router.js
@@ -0,0 +1,241 @@
+let express = require("express");
+const apicache = require("../modules/apicache");
+const { UptimeKumaServer } = require("../uptime-kuma-server");
+const StatusPage = require("../model/status_page");
+const { allowDevAllOrigin, sendHttpError } = require("../util-server");
+const { R } = require("redbean-node");
+const { badgeConstants } = require("../../src/util");
+const { makeBadge } = require("badge-maker");
+const { UptimeCalculator } = require("../uptime-calculator");
+
+let router = express.Router();
+
+let cache = apicache.middleware;
+const server = UptimeKumaServer.getInstance();
+
+router.get("/status/:slug", cache("5 minutes"), async (request, response) => {
+ let slug = request.params.slug;
+ await StatusPage.handleStatusPageResponse(response, server.indexHTML, slug);
+});
+
+router.get("/status/:slug/rss", cache("5 minutes"), async (request, response) => {
+ let slug = request.params.slug;
+ await StatusPage.handleStatusPageRSSResponse(response, slug);
+});
+
+router.get("/status", cache("5 minutes"), async (request, response) => {
+ let slug = "default";
+ await StatusPage.handleStatusPageResponse(response, server.indexHTML, slug);
+});
+
+router.get("/status-page", cache("5 minutes"), async (request, response) => {
+ let slug = "default";
+ await StatusPage.handleStatusPageResponse(response, server.indexHTML, slug);
+});
+
+// Status page config, incident, monitor list
+router.get("/api/status-page/:slug", cache("5 minutes"), async (request, response) => {
+ allowDevAllOrigin(response);
+ let slug = request.params.slug;
+
+ try {
+ // Get Status Page
+ let statusPage = await R.findOne("status_page", " slug = ? ", [
+ slug
+ ]);
+
+ if (!statusPage) {
+ sendHttpError(response, "Status Page Not Found");
+ return null;
+ }
+
+ let statusPageData = await StatusPage.getStatusPageData(statusPage);
+
+ // Response
+ response.json(statusPageData);
+
+ } catch (error) {
+ sendHttpError(response, error.message);
+ }
+});
+
+// Status Page Polling Data
+// Can fetch only if published
+router.get("/api/status-page/heartbeat/:slug", cache("1 minutes"), async (request, response) => {
+ allowDevAllOrigin(response);
+
+ try {
+ let heartbeatList = {};
+ let uptimeList = {};
+
+ let slug = request.params.slug;
+ let statusPageID = await StatusPage.slugToID(slug);
+
+ let monitorIDList = await R.getCol(`
+ SELECT monitor_group.monitor_id FROM monitor_group, \`group\`
+ WHERE monitor_group.group_id = \`group\`.id
+ AND public = 1
+ AND \`group\`.status_page_id = ?
+ `, [
+ statusPageID
+ ]);
+
+ for (let monitorID of monitorIDList) {
+ let list = await R.getAll(`
+ SELECT * FROM heartbeat
+ WHERE monitor_id = ?
+ ORDER BY time DESC
+ LIMIT 50
+ `, [
+ monitorID,
+ ]);
+
+ list = R.convertToBeans("heartbeat", list);
+ heartbeatList[monitorID] = list.reverse().map(row => row.toPublicJSON());
+
+ const uptimeCalculator = await UptimeCalculator.getUptimeCalculator(monitorID);
+ uptimeList[`${monitorID}_24`] = uptimeCalculator.get24Hour().uptime;
+ }
+
+ response.json({
+ heartbeatList,
+ uptimeList
+ });
+
+ } catch (error) {
+ sendHttpError(response, error.message);
+ }
+});
+
+// Status page's manifest.json
+router.get("/api/status-page/:slug/manifest.json", cache("1440 minutes"), async (request, response) => {
+ allowDevAllOrigin(response);
+ let slug = request.params.slug;
+
+ try {
+ // Get Status Page
+ let statusPage = await R.findOne("status_page", " slug = ? ", [
+ slug
+ ]);
+
+ if (!statusPage) {
+ sendHttpError(response, "Not Found");
+ return;
+ }
+
+ // Response
+ response.json({
+ "name": statusPage.title,
+ "start_url": "/status/" + statusPage.slug,
+ "display": "standalone",
+ "icons": [
+ {
+ "src": statusPage.icon,
+ "sizes": "128x128",
+ "type": "image/png"
+ }
+ ]
+ });
+
+ } catch (error) {
+ sendHttpError(response, error.message);
+ }
+});
+
+// overall status-page status badge
+router.get("/api/status-page/:slug/badge", cache("5 minutes"), async (request, response) => {
+ allowDevAllOrigin(response);
+ const slug = request.params.slug;
+ const statusPageID = await StatusPage.slugToID(slug);
+ const {
+ label,
+ upColor = badgeConstants.defaultUpColor,
+ downColor = badgeConstants.defaultDownColor,
+ partialColor = "#F6BE00",
+ maintenanceColor = "#808080",
+ style = badgeConstants.defaultStyle
+ } = request.query;
+
+ try {
+ let monitorIDList = await R.getCol(`
+ SELECT monitor_group.monitor_id FROM monitor_group, \`group\`
+ WHERE monitor_group.group_id = \`group\`.id
+ AND public = 1
+ AND \`group\`.status_page_id = ?
+ `, [
+ statusPageID
+ ]);
+
+ let hasUp = false;
+ let hasDown = false;
+ let hasMaintenance = false;
+
+ for (let monitorID of monitorIDList) {
+ // retrieve the latest heartbeat
+ let beat = await R.getAll(`
+ SELECT * FROM heartbeat
+ WHERE monitor_id = ?
+ ORDER BY time DESC
+ LIMIT 1
+ `, [
+ monitorID,
+ ]);
+
+ // to be sure, when corresponding monitor not found
+ if (beat.length === 0) {
+ continue;
+ }
+ // handle status of beat
+ if (beat[0].status === 3) {
+ hasMaintenance = true;
+ } else if (beat[0].status === 2) {
+ // ignored
+ } else if (beat[0].status === 1) {
+ hasUp = true;
+ } else {
+ hasDown = true;
+ }
+
+ }
+
+ const badgeValues = { style };
+
+ if (!hasUp && !hasDown && !hasMaintenance) {
+ // return a "N/A" badge in naColor (grey), if monitor is not public / not available / non exsitant
+
+ badgeValues.message = "N/A";
+ badgeValues.color = badgeConstants.naColor;
+
+ } else {
+ if (hasMaintenance) {
+ badgeValues.label = label ? label : "";
+ badgeValues.color = maintenanceColor;
+ badgeValues.message = "Maintenance";
+ } else if (hasUp && !hasDown) {
+ badgeValues.label = label ? label : "";
+ badgeValues.color = upColor;
+ badgeValues.message = "Up";
+ } else if (hasUp && hasDown) {
+ badgeValues.label = label ? label : "";
+ badgeValues.color = partialColor;
+ badgeValues.message = "Degraded";
+ } else {
+ badgeValues.label = label ? label : "";
+ badgeValues.color = downColor;
+ badgeValues.message = "Down";
+ }
+
+ }
+
+ // build the svg based on given values
+ const svg = makeBadge(badgeValues);
+
+ response.type("image/svg+xml");
+ response.send(svg);
+
+ } catch (error) {
+ sendHttpError(response, error.message);
+ }
+});
+
+module.exports = router;