This commit is contained in:
Gero Gerke 2025-01-24 17:49:44 +00:00 committed by GitHub
commit e8d67c560f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 75 additions and 13 deletions

View file

@ -4,19 +4,20 @@ const { R } = require("redbean-node");
class Group extends BeanModel { class Group extends BeanModel {
/** /**
* Return an object that ready to parse to JSON for public Only show * Return an object that ready to parse to JSON for public
* necessary data to public * Only show necessary data to public
* @param {boolean} showTags Should the JSON include monitor tags * @param {boolean} showTags Should the JSON include monitor tags
* @param {boolean} certExpiry Should JSON include info about * @param {boolean} certExpiry Should JSON include info about
* certificate expiry? * certificate expiry?
* @param {boolean} showStatus Should the JSON include the status
* @returns {Promise<object>} Object ready to parse * @returns {Promise<object>} Object ready to parse
*/ */
async toPublicJSON(showTags = false, certExpiry = false) { async toPublicJSON(showTags = false, certExpiry = false, showStatus = false) {
let monitorBeanList = await this.getMonitorList(); let monitorBeanList = await this.getMonitorList();
let monitorList = []; let monitorList = [];
for (let bean of monitorBeanList) { for (let bean of monitorBeanList) {
monitorList.push(await bean.toPublicJSON(showTags, certExpiry)); monitorList.push(await bean.toPublicJSON(showTags, certExpiry, showStatus));
} }
return { return {

View file

@ -36,15 +36,31 @@ const rootCertificates = rootCertificatesFingerprints();
*/ */
class Monitor extends BeanModel { 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 * Return an object that ready to parse to JSON for public Only show
* necessary data to public * necessary data to public
* @param {boolean} showTags Include tags in JSON * @param {boolean} showTags Include tags in JSON
* @param {boolean} certExpiry Include certificate expiry info in * @param {boolean} certExpiry Include certificate expiry info in
* JSON * JSON
* @param {boolean} showStatus Should the JSON show the status
* @returns {Promise<object>} Object ready to parse * @returns {Promise<object>} Object ready to parse
*/ */
async toPublicJSON(showTags = false, certExpiry = false) { async toPublicJSON(showTags = false, certExpiry = false, showStatus = false) {
let obj = { let obj = {
id: this.id, id: this.id,
name: this.name, name: this.name,
@ -66,6 +82,11 @@ class Monitor extends BeanModel {
obj.validCert = validCert; obj.validCert = validCert;
} }
if (showStatus) {
const heartbeat = await Monitor.getPreviousHeartbeat(this.id);
obj.status = this.statusToKey(heartbeat.status);
}
return obj; return obj;
} }

View file

@ -257,12 +257,12 @@ class StatusPage extends BeanModel {
/** /**
* Get all status page data in one call * Get all status page data in one call
* @param {StatusPage} statusPage Status page to get data for * @param {StatusPage} statusPage the status page to return the data for
* @returns {object} Status page data * @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) { static async getStatusPageData(statusPage, includeStatus = false, includeConfig = true) {
const config = await statusPage.toPublicJSON();
// Incident // Incident
let incident = await R.findOne("incident", " pin = 1 AND active = 1 AND status_page_id = ? ", [ let incident = await R.findOne("incident", " pin = 1 AND active = 1 AND status_page_id = ? ", [
statusPage.id, statusPage.id,
@ -283,13 +283,20 @@ class StatusPage extends BeanModel {
]); ]);
for (let groupBean of list) { for (let groupBean of list) {
let monitorGroup = await groupBean.toPublicJSON(showTags, config?.showCertificateExpiry); let monitorGroup = await groupBean.toPublicJSON(showTags, false, includeStatus);
publicGroupList.push(monitorGroup); publicGroupList.push(monitorGroup);
} }
let config = {};
if (includeConfig) {
config = {
config: await statusPage.toPublicJSON()
};
}
// Response // Response
return { return {
config, ...config,
incident, incident,
publicGroupList, publicGroupList,
maintenanceList, maintenanceList,

View file

@ -2,7 +2,7 @@ let express = require("express");
const apicache = require("../modules/apicache"); const apicache = require("../modules/apicache");
const { UptimeKumaServer } = require("../uptime-kuma-server"); const { UptimeKumaServer } = require("../uptime-kuma-server");
const StatusPage = require("../model/status_page"); 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 { R } = require("redbean-node");
const { badgeConstants } = require("../../src/util"); const { badgeConstants } = require("../../src/util");
const { makeBadge } = require("badge-maker"); const { makeBadge } = require("badge-maker");
@ -35,6 +35,39 @@ router.get("/status-page", cache("5 minutes"), async (request, response) => {
await StatusPage.handleStatusPageResponse(response, server.indexHTML, slug); 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 // Status page config, incident, monitor list
router.get("/api/status-page/:slug", cache("5 minutes"), async (request, response) => { router.get("/api/status-page/:slug", cache("5 minutes"), async (request, response) => {
allowDevAllOrigin(response); allowDevAllOrigin(response);