mirror of
				https://github.com/karl0ss/homepage.git
				synced 2025-11-04 08:20:58 +00:00 
			
		
		
		
	Rework uptime kuma remove proxy display more info
This commit is contained in:
		
							parent
							
								
									c3d15a61c3
								
							
						
					
					
						commit
						015d7dac52
					
				@ -449,11 +449,10 @@
 | 
				
			|||||||
        "storage": "Storage"
 | 
					        "storage": "Storage"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "uptimekuma": {
 | 
					    "uptimekuma": {
 | 
				
			||||||
        "status": "status",
 | 
					        "up": "Sites Up",
 | 
				
			||||||
        "uptime": "uptime",
 | 
					        "down": "Sites Down",
 | 
				
			||||||
        "good": "All Systems Operational",
 | 
					        "uptime": "Uptime",
 | 
				
			||||||
        "warn": "Partially Degraded Service",
 | 
					        "incident": "Incident",
 | 
				
			||||||
        "bad": "Degraded Service",
 | 
					        "m": "m"
 | 
				
			||||||
        "unknown": "Unknown service status"
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -4,42 +4,52 @@ import Container from "components/services/widget/container";
 | 
				
			|||||||
import useWidgetAPI from "utils/proxy/use-widget-api";
 | 
					import useWidgetAPI from "utils/proxy/use-widget-api";
 | 
				
			||||||
import Block from "components/services/widget/block";
 | 
					import Block from "components/services/widget/block";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Status = {
 | 
					 | 
				
			||||||
  good: "uptimekuma.good",
 | 
					 | 
				
			||||||
  warn: "uptimekuma.warn",
 | 
					 | 
				
			||||||
  bad: "uptimekuma.bad",
 | 
					 | 
				
			||||||
  unknown: "uptimekuma.unknown",
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default function Component({ service }) {
 | 
					export default function Component({ service }) {
 | 
				
			||||||
  const { t } = useTranslation();
 | 
					  const { t } = useTranslation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const { widget } = service;
 | 
					  const { widget } = service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const { data: statusData, error: statusError } = useWidgetAPI(widget);
 | 
					  const { data: statusData, error: statusError } = useWidgetAPI(widget, "status_page");
 | 
				
			||||||
 | 
					  const { data: heartbeatData, error: heartbeatError } = useWidgetAPI(widget, "heartbeat");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (statusError) {
 | 
					  if (statusError || heartbeatError) {
 | 
				
			||||||
    return <Container error={statusError} />;
 | 
					    return <Container error={statusError ?? heartbeatError} />;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!statusData) {
 | 
					  if (!statusData || !heartbeatData) {
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <Container service={service}>
 | 
					      <Container service={service}>
 | 
				
			||||||
        <Block label="uptimekuma.status"/>
 | 
					        <Block label="uptimekuma.up"/>
 | 
				
			||||||
 | 
					        <Block label="uptimekuma.down"/>
 | 
				
			||||||
        <Block label="uptimekuma.uptime"/>
 | 
					        <Block label="uptimekuma.uptime"/>
 | 
				
			||||||
 | 
					        <Block label="uptimekuma.incidents"/>
 | 
				
			||||||
      </Container>
 | 
					      </Container>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (statusData.icon) {
 | 
					  let sitesUp = 0;
 | 
				
			||||||
    // eslint-disable-next-line no-param-reassign
 | 
					  let sitesDown = 0;
 | 
				
			||||||
    service.icon = statusData.icon;
 | 
					  Object.values(heartbeatData.heartbeatList).forEach((siteList) => {
 | 
				
			||||||
 | 
					    const lastHeartbeat = siteList[siteList.length - 1];
 | 
				
			||||||
 | 
					    if (lastHeartbeat?.status === 1) {
 | 
				
			||||||
 | 
					      sitesUp += 1;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      sitesDown += 1;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Adapted from https://github.com/bastienwirtz/homer/blob/b7cd8f9482e6836a96b354b11595b03b9c3d67cd/src/components/services/UptimeKuma.vue#L105
 | 
				
			||||||
 | 
					  const uptimeList = Object.values(heartbeatData.uptimeList);
 | 
				
			||||||
 | 
					  const percent = uptimeList.reduce((a, b) => a + b, 0) / uptimeList.length || 0;
 | 
				
			||||||
 | 
					  const uptime = (percent * 100).toFixed(1);
 | 
				
			||||||
 | 
					  const incidentTime = statusData.incident ? (Math.abs(new Date(statusData.incident?.createdDate) - new Date()) / 1000) / (60 * 60) : null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <Container service={service}>
 | 
					    <Container service={service}>
 | 
				
			||||||
      <Block label="uptimekuma.status" value={statusData.incident ? statusData.incident : t(Status[statusData.message])} />
 | 
					      <Block label="uptimekuma.up" value={t("common.number", { value: sitesUp })} />
 | 
				
			||||||
      <Block label="uptimekuma.uptime" value={t("common.number", { value: statusData.uptime, decimals: 1 })} />
 | 
					      <Block label="uptimekuma.down" value={t("common.number", { value: sitesDown })} />
 | 
				
			||||||
 | 
					      <Block label="uptimekuma.uptime" value={t("common.percent", { value: uptime })} />
 | 
				
			||||||
 | 
					      {incidentTime && <Block label="uptimekuma.incident" value={t("common.number", { value: Math.round(incidentTime) }) + t("uptimekuma.m")} />}
 | 
				
			||||||
    </Container>
 | 
					    </Container>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,95 +0,0 @@
 | 
				
			|||||||
import { httpProxy } from "utils/proxy/http";
 | 
					 | 
				
			||||||
import getServiceWidget from "utils/config/service-helpers";
 | 
					 | 
				
			||||||
import createLogger from "utils/logger";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const logger = createLogger("uptimeKumaProxyHandler");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function getStatus(widget) {
 | 
					 | 
				
			||||||
  const url = new URL(`${widget.url}/api/status-page/${widget.slug}`).toString();
 | 
					 | 
				
			||||||
  logger.debug("get status %s", url);
 | 
					 | 
				
			||||||
  const params = { method: "GET", headers: {} };
 | 
					 | 
				
			||||||
  const [status, , data] = await httpProxy(url, params);
 | 
					 | 
				
			||||||
  try {
 | 
					 | 
				
			||||||
    return [status, JSON.parse(data)];
 | 
					 | 
				
			||||||
  } catch (e) {
 | 
					 | 
				
			||||||
    logger.error("Error decoding status data. Data: %s", data.toString());
 | 
					 | 
				
			||||||
    return [status, null];
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function getHeartbeat(widget) {
 | 
					 | 
				
			||||||
  const url = new URL(`${widget.url}/api/status-page/heartbeat/${widget.slug}`).toString();
 | 
					 | 
				
			||||||
  logger.debug("get heartbeat %s", url);
 | 
					 | 
				
			||||||
  const params = { method: "GET", headers: {} };
 | 
					 | 
				
			||||||
  const [status, , data] = await httpProxy(url, params);
 | 
					 | 
				
			||||||
  try {
 | 
					 | 
				
			||||||
    return [status, JSON.parse(data)];
 | 
					 | 
				
			||||||
  } catch (e) {
 | 
					 | 
				
			||||||
    logger.error("Error decoding heartbeat data. Data: %s", data.toString());
 | 
					 | 
				
			||||||
    return [status, null];
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function statusMessage(data) {
 | 
					 | 
				
			||||||
  if (!data || Object.keys(data.heartbeatList) === 0) {
 | 
					 | 
				
			||||||
    return "unknown";
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let result = "good";
 | 
					 | 
				
			||||||
  let hasUp = false;
 | 
					 | 
				
			||||||
  Object.values(data.heartbeatList).forEach((el) => {
 | 
					 | 
				
			||||||
    const index = el.length - 1;
 | 
					 | 
				
			||||||
    if (el[index].status === 1) {
 | 
					 | 
				
			||||||
      hasUp = true;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      result = "warn";
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (!hasUp) {
 | 
					 | 
				
			||||||
    result = "bad";
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return result;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function uptime(data) {
 | 
					 | 
				
			||||||
  if (!data) {
 | 
					 | 
				
			||||||
    return 0;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const uptimeList = Object.values(data.uptimeList);
 | 
					 | 
				
			||||||
  const percent = uptimeList.reduce((a, b) => a + b, 0) / uptimeList.length || 0;
 | 
					 | 
				
			||||||
  return (percent * 100).toFixed(1);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default async function uptimeKumaProxyHandler(req, res) {
 | 
					 | 
				
			||||||
  const { group, service } = req.query;
 | 
					 | 
				
			||||||
  const widget = await getServiceWidget(group, service);
 | 
					 | 
				
			||||||
  if (!widget) {
 | 
					 | 
				
			||||||
    logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group);
 | 
					 | 
				
			||||||
    return res.status(400).json({ error: "Invalid proxy service type" });
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const [[statusCode, statusData], [heartbeatCode, heartbeatData]] = await Promise.all([
 | 
					 | 
				
			||||||
    getStatus(widget),
 | 
					 | 
				
			||||||
    getHeartbeat(widget),
 | 
					 | 
				
			||||||
  ]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (statusCode !== 200) {
 | 
					 | 
				
			||||||
    logger.error("HTTP %d getting status data error. Data: %s", statusCode, statusData);
 | 
					 | 
				
			||||||
    return res.status(statusCode).send(statusData);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (heartbeatCode !== 200) {
 | 
					 | 
				
			||||||
    logger.error("HTTP %d getting heartbeat data error. Data: %s", heartbeatCode, heartbeatData);
 | 
					 | 
				
			||||||
    return res.status(heartbeatCode).send(heartbeatData);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const icon = statusData?.config ? statusData.config.icon : null;
 | 
					 | 
				
			||||||
  return res.status(200).send({
 | 
					 | 
				
			||||||
    uptime: uptime(heartbeatData),
 | 
					 | 
				
			||||||
    message: statusMessage(heartbeatData),
 | 
					 | 
				
			||||||
    incident: statusData?.incident ? statusData.incident.title : "",
 | 
					 | 
				
			||||||
    icon: `${widget.url}${icon}`,
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,8 +1,18 @@
 | 
				
			|||||||
// import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
 | 
					// import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
 | 
				
			||||||
import uptimeKumaProxyHandler from "./proxy";
 | 
					import genericProxyHandler from "utils/proxy/handlers/generic";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const widget = {
 | 
					const widget = {
 | 
				
			||||||
  proxyHandler: uptimeKumaProxyHandler,
 | 
					  api: "{url}/api/{endpoint}/{slug}",
 | 
				
			||||||
 | 
					  proxyHandler: genericProxyHandler,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mappings: {
 | 
				
			||||||
 | 
					    status_page: {
 | 
				
			||||||
 | 
					      endpoint: "status-page",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    heartbeat: {
 | 
				
			||||||
 | 
					      endpoint: "status-page/heartbeat",
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default widget;
 | 
					export default widget;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user