diff --git a/server/model/group.js b/server/model/group.js index bd2c301899..87c3a59b8e 100644 --- a/server/model/group.js +++ b/server/model/group.js @@ -4,19 +4,20 @@ const { R } = require("redbean-node"); class Group extends BeanModel { /** - * Return an object that ready to parse to JSON for public Only show - * necessary data to public + * Return an object that ready to parse to JSON for public + * Only show necessary data to public * @param {boolean} showTags Should the JSON include monitor tags * @param {boolean} certExpiry Should JSON include info about * certificate expiry? + * @param {boolean} showStatus Should the JSON include the status * @returns {Promise} Object ready to parse */ - async toPublicJSON(showTags = false, certExpiry = false) { + async toPublicJSON(showTags = false, certExpiry = false, showStatus = false) { let monitorBeanList = await this.getMonitorList(); let monitorList = []; for (let bean of monitorBeanList) { - monitorList.push(await bean.toPublicJSON(showTags, certExpiry)); + monitorList.push(await bean.toPublicJSON(showTags, certExpiry, showStatus)); } return { diff --git a/server/model/monitor.js b/server/model/monitor.js index 3ad8cfafc1..dd936478c6 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -36,15 +36,31 @@ const rootCertificates = rootCertificatesFingerprints(); */ class Monitor extends BeanModel { + /** + * Formats the status code to a human readable form + * @param {number} status the internal status code of the monitor + * @returns {string} a human readable string that corresponds to the status code + */ + statusToKey(status) { + switch (status) { + case 0: return "down"; + case 1: return "up"; + case 2: return "pending"; + case 4: return "maintenance"; + default: return "unknown"; + } + } + /** * Return an object that ready to parse to JSON for public Only show * necessary data to public * @param {boolean} showTags Include tags in JSON * @param {boolean} certExpiry Include certificate expiry info in * JSON + * @param {boolean} showStatus Should the JSON show the status * @returns {Promise} Object ready to parse */ - async toPublicJSON(showTags = false, certExpiry = false) { + async toPublicJSON(showTags = false, certExpiry = false, showStatus = false) { let obj = { id: this.id, name: this.name, @@ -66,6 +82,11 @@ class Monitor extends BeanModel { obj.validCert = validCert; } + if (showStatus) { + const heartbeat = await Monitor.getPreviousHeartbeat(this.id); + obj.status = this.statusToKey(heartbeat.status); + } + return obj; } diff --git a/server/model/status_page.js b/server/model/status_page.js index 38f548ebba..c987741768 100644 --- a/server/model/status_page.js +++ b/server/model/status_page.js @@ -257,12 +257,12 @@ class StatusPage extends BeanModel { /** * Get all status page data in one call - * @param {StatusPage} statusPage Status page to get data for - * @returns {object} Status page data + * @param {StatusPage} statusPage the status page to return the data for + * @param {boolean} includeStatus whether each monitor should include the status of the monitor ("up" or "down") + * @param {boolean} includeConfig whether the config for the status page should be included in the returned JSON + * @returns {object} the status page data object */ - static async getStatusPageData(statusPage) { - const config = await statusPage.toPublicJSON(); - + static async getStatusPageData(statusPage, includeStatus = false, includeConfig = true) { // Incident let incident = await R.findOne("incident", " pin = 1 AND active = 1 AND status_page_id = ? ", [ statusPage.id, @@ -283,13 +283,20 @@ class StatusPage extends BeanModel { ]); for (let groupBean of list) { - let monitorGroup = await groupBean.toPublicJSON(showTags, config?.showCertificateExpiry); + let monitorGroup = await groupBean.toPublicJSON(showTags, false, includeStatus); publicGroupList.push(monitorGroup); } + let config = {}; + if (includeConfig) { + config = { + config: await statusPage.toPublicJSON() + }; + } + // Response return { - config, + ...config, incident, publicGroupList, maintenanceList, diff --git a/server/routers/status-page-router.js b/server/routers/status-page-router.js index b209d33d1f..60c5af6334 100644 --- a/server/routers/status-page-router.js +++ b/server/routers/status-page-router.js @@ -2,7 +2,7 @@ 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 { allowAllOrigin, allowDevAllOrigin, sendHttpError } = require("../util-server"); const { R } = require("redbean-node"); const { badgeConstants } = require("../../src/util"); const { makeBadge } = require("badge-maker"); @@ -33,6 +33,39 @@ router.get("/status-page", cache("5 minutes"), async (request, response) => { await StatusPage.handleStatusPageResponse(response, server.indexHTML, slug); }); +// Status page config, incident, monitor list with status ("up" or "down") +router.get("/api/status-page/:slug/summary", cache("5 minutes"), async (request, response) => { + allowAllOrigin(response); + let slug = request.params.slug; + + try { + // Get Status Page + let statusPage = await R.findOne("status_page", " slug = ? ", [ + slug + ]); + + if (!statusPage) { + return null; + } + + let statusPageData = await StatusPage.getStatusPageData(statusPage, true, false); + + if (!statusPageData) { + response.statusCode = 404; + response.json({ + msg: "Not Found" + }); + return; + } + + // Response + response.json(statusPageData); + + } catch (error) { + sendHttpError(response, error.message); + } +}); + // Status page config, incident, monitor list router.get("/api/status-page/:slug", cache("5 minutes"), async (request, response) => { allowDevAllOrigin(response);