From 3f30feaefbb512a6e95f3cc327cbbd18cedf1093 Mon Sep 17 00:00:00 2001
From: Sjouke de Vries <info@sdvservices.nl>
Date: Mon, 9 Jan 2023 11:04:32 +0100
Subject: [PATCH] feat(server): add badge for overall status of status-page

---
 server/routers/status-page-router.js | 142 +++++++++++++++++++++++++++
 1 file changed, 142 insertions(+)

diff --git a/server/routers/status-page-router.js b/server/routers/status-page-router.js
index de075db8d..8332b18d7 100644
--- a/server/routers/status-page-router.js
+++ b/server/routers/status-page-router.js
@@ -5,6 +5,8 @@ const StatusPage = require("../model/status_page");
 const { allowDevAllOrigin, send403 } = require("../util-server");
 const { R } = require("redbean-node");
 const Monitor = require("../model/monitor");
+const { badgeConstants } = require("../config");
+const { makeBadge } = require("badge-maker");
 
 let router = express.Router();
 
@@ -145,4 +147,144 @@ router.get("/api/status-page/:slug/manifest.json", cache("1440 minutes"), async
     }
 });
 
+// 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) {
+        send403(response, error.message);
+    }
+});
+
+// overallStatus() {
+
+//     if (Object.keys(this.$root.publicLastHeartbeatList).length === 0) {
+//         return -1;
+//     }
+
+//     let status = STATUS_PAGE_ALL_UP;
+//     let hasUp = false;
+
+//     for (let id in this.$root.publicLastHeartbeatList) {
+//         let beat = this.$root.publicLastHeartbeatList[id];
+
+//         if (beat.status === MAINTENANCE) {
+//             return STATUS_PAGE_MAINTENANCE;
+//         } else if (beat.status === UP) {
+//             hasUp = true;
+//         } else {
+//             status = STATUS_PAGE_PARTIAL_DOWN;
+//         }
+//     }
+
+//     if (! hasUp) {
+//         status = STATUS_PAGE_ALL_DOWN;
+//     }
+
+//     return status;
+// },
+
+// allUp() {
+//     return this.overallStatus === STATUS_PAGE_ALL_UP;
+// },
+
+// partialDown() {
+//     return this.overallStatus === STATUS_PAGE_PARTIAL_DOWN;
+// },
+
+// allDown() {
+//     return this.overallStatus === STATUS_PAGE_ALL_DOWN;
+// },
+
+// isMaintenance() {
+//     return this.overallStatus === STATUS_PAGE_MAINTENANCE;
+// },
+
 module.exports = router;