mirror of
https://github.com/gethomepage/homepage.git
synced 2025-07-09 03:04:18 -04:00
Enhancement: Komodo widget (#5407)
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
This commit is contained in:
parent
6e16adc460
commit
8ce9e57ed8
22
docs/widgets/services/komodo.md
Normal file
22
docs/widgets/services/komodo.md
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
title: Komodo
|
||||
description: Komodo Widget Configuration
|
||||
---
|
||||
|
||||
This widget shows either details about all containers or stacks (if `showStacks` is true) managed by [Komodo](https://komo.do/) or the number of running servers, containers and stacks when `showSummary` is enabled.
|
||||
|
||||
The api key and secret can be found in the Komodo settings.
|
||||
|
||||
Allowed fields (max 4): `["total", "running", "stopped", "unhealthy", "unknown"]`.
|
||||
Allowed fields with `showStacks` (max 4): `["total", "running", "down", "unhealthy", "unknown"]`.
|
||||
Allowed fields with `showSummary`: `["servers", "stacks", "containers"]`.
|
||||
|
||||
```yaml
|
||||
widget:
|
||||
type: komodo
|
||||
url: http://komodo.hostname.or.ip:port
|
||||
key: K-xxxxxx...
|
||||
secret: S-xxxxxx...
|
||||
showSummary: true # optional, default: false
|
||||
showStacks: true # optional, default: false
|
||||
```
|
@ -1060,5 +1060,16 @@
|
||||
"checkmk": {
|
||||
"serviceErrors": "Service issues",
|
||||
"hostErrors": "Host issues"
|
||||
},
|
||||
"komodo": {
|
||||
"total": "Total",
|
||||
"running": "Running",
|
||||
"stopped": "Stopped",
|
||||
"down": "Down",
|
||||
"unhealthy": "Unhealthy",
|
||||
"unknown": "Unknown",
|
||||
"servers": "Servers",
|
||||
"stacks": "Stacks",
|
||||
"containers": "Containers"
|
||||
}
|
||||
}
|
||||
|
@ -338,6 +338,10 @@ export function cleanServiceGroups(groups) {
|
||||
// jellystat
|
||||
days,
|
||||
|
||||
// komodo
|
||||
showSummary,
|
||||
showStacks,
|
||||
|
||||
// kopia
|
||||
snapshotHost,
|
||||
snapshotPath,
|
||||
@ -450,6 +454,10 @@ export function cleanServiceGroups(groups) {
|
||||
if (type === "proxmoxbackupserver") {
|
||||
if (datastore) widget.datastore = datastore;
|
||||
}
|
||||
if (type === "komodo") {
|
||||
if (showSummary !== undefined) widget.showSummary = !!JSON.parse(showSummary);
|
||||
if (showStacks !== undefined) widget.showStacks = !!JSON.parse(showStacks);
|
||||
}
|
||||
if (type === "kubernetes") {
|
||||
if (namespace) widget.namespace = namespace;
|
||||
if (app) widget.app = app;
|
||||
|
@ -63,6 +63,7 @@ const components = {
|
||||
jellystat: dynamic(() => import("./jellystat/component")),
|
||||
kavita: dynamic(() => import("./kavita/component")),
|
||||
komga: dynamic(() => import("./komga/component")),
|
||||
komodo: dynamic(() => import("./komodo/component")),
|
||||
kopia: dynamic(() => import("./kopia/component")),
|
||||
lidarr: dynamic(() => import("./lidarr/component")),
|
||||
linkwarden: dynamic(() => import("./linkwarden/component")),
|
||||
|
84
src/widgets/komodo/component.jsx
Normal file
84
src/widgets/komodo/component.jsx
Normal file
@ -0,0 +1,84 @@
|
||||
import Block from "components/services/widget/block";
|
||||
import Container from "components/services/widget/container";
|
||||
import { useTranslation } from "next-i18next";
|
||||
|
||||
import useWidgetAPI from "utils/proxy/use-widget-api";
|
||||
|
||||
const MAX_ALLOWED_FIELDS = 4;
|
||||
|
||||
export default function Component({ service }) {
|
||||
const { t } = useTranslation();
|
||||
const { widget } = service;
|
||||
const containersEndpoint = !(!widget.showSummary && widget.showStacks) ? "containers" : "";
|
||||
const { data: containersData, error: containersError } = useWidgetAPI(widget, containersEndpoint);
|
||||
const stacksEndpoint = widget.showSummary || widget.showStacks ? "stacks" : "";
|
||||
const { data: stacksData, error: stacksError } = useWidgetAPI(widget, stacksEndpoint);
|
||||
const serversEndpoint = widget.showSummary ? "servers" : "";
|
||||
const { data: serversData, error: serversError } = useWidgetAPI(widget, serversEndpoint);
|
||||
|
||||
if (containersError || stacksError || serversError) {
|
||||
return <Container service={service} error={containersError ?? stacksError ?? serversError} />;
|
||||
}
|
||||
|
||||
if (!widget.fields || widget.fields.length === 0) {
|
||||
widget.fields = widget.showSummary
|
||||
? ["servers", "stacks", "containers"]
|
||||
: widget.showStacks
|
||||
? ["total", "running", "down", "unhealthy"]
|
||||
: ["total", "running", "stopped", "unhealthy"];
|
||||
} else if (widget.fields?.length > MAX_ALLOWED_FIELDS) {
|
||||
widget.fields = widget.fields.slice(0, MAX_ALLOWED_FIELDS);
|
||||
}
|
||||
|
||||
if (
|
||||
(!widget.showStacks && !containersData) ||
|
||||
(widget.showSummary && (!stacksData || !serversData)) ||
|
||||
(widget.showStacks && !stacksData)
|
||||
) {
|
||||
return widget.showSummary ? (
|
||||
<Container service={service}>
|
||||
<Block label="komodo.servers" />
|
||||
<Block label="komodo.stacks" />
|
||||
<Block label="komodo.containers" />
|
||||
</Container>
|
||||
) : widget.showStacks ? (
|
||||
<Container service={service}>
|
||||
<Block label="komodo.total" />
|
||||
<Block label="komodo.running" />
|
||||
<Block label="komodo.down" />
|
||||
<Block label="komodo.unhealthy" />
|
||||
</Container>
|
||||
) : (
|
||||
<Container service={service}>
|
||||
<Block label="komodo.total" />
|
||||
<Block label="komodo.running" />
|
||||
<Block label="komodo.stopped" />
|
||||
<Block label="komodo.unhealthy" />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
return widget.showSummary ? (
|
||||
<Container service={service}>
|
||||
<Block label="komodo.servers" value={`${serversData.healthy} / ${serversData.total}`} />
|
||||
<Block label="komodo.stacks" value={`${stacksData.running} / ${stacksData.total}`} />
|
||||
<Block label="komodo.containers" value={`${containersData.running} / ${containersData.total}`} />
|
||||
</Container>
|
||||
) : widget.showStacks ? (
|
||||
<Container service={service}>
|
||||
<Block label="komodo.total" value={t("common.number", { value: stacksData.total })} />
|
||||
<Block label="komodo.running" value={t("common.number", { value: stacksData.running })} />
|
||||
<Block label="komodo.down" value={t("common.number", { value: stacksData.stopped + stacksData.down })} />
|
||||
<Block label="komodo.unhealthy" value={t("common.number", { value: stacksData.unhealthy })} />
|
||||
<Block label="komodo.unknown" value={t("common.number", { value: stacksData.unknown })} />
|
||||
</Container>
|
||||
) : (
|
||||
<Container service={service}>
|
||||
<Block label="komodo.total" value={t("common.number", { value: containersData.total })} />
|
||||
<Block label="komodo.running" value={t("common.number", { value: containersData.running })} />
|
||||
<Block label="komodo.stopped" value={t("common.number", { value: containersData.stopped })} />
|
||||
<Block label="komodo.unhealthy" value={t("common.number", { value: containersData.unhealthy })} />
|
||||
<Block label="komodo.unknown" value={t("common.number", { value: containersData.unknown })} />
|
||||
</Container>
|
||||
);
|
||||
}
|
55
src/widgets/komodo/proxy.js
Normal file
55
src/widgets/komodo/proxy.js
Normal file
@ -0,0 +1,55 @@
|
||||
import getServiceWidget from "utils/config/service-helpers";
|
||||
import createLogger from "utils/logger";
|
||||
import { formatApiCall, sanitizeErrorURL } from "utils/proxy/api-helpers";
|
||||
import { httpProxy } from "utils/proxy/http";
|
||||
import validateWidgetData from "utils/proxy/validate-widget-data";
|
||||
import widgets from "widgets/widgets";
|
||||
|
||||
const logger = createLogger("komodoProxyHandler");
|
||||
|
||||
export default async function komodoProxyHandler(req, res) {
|
||||
const { group, service, endpoint, index } = req.query;
|
||||
|
||||
if (group && service) {
|
||||
const widget = await getServiceWidget(group, service, index);
|
||||
if (!widgets?.[widget.type]?.api) {
|
||||
return res.status(403).json({ error: "Service does not support API calls" });
|
||||
}
|
||||
|
||||
if (widget) {
|
||||
// api uses unified read endpoint
|
||||
const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint: "read", ...widget })).toString();
|
||||
|
||||
const headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-API-Key": `${widget.key}`,
|
||||
"X-API-Secret": `${widget.secret}`,
|
||||
};
|
||||
const [status, contentType, data] = await httpProxy(url, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(widgets[widget.type].mappings?.[endpoint]?.body || {}),
|
||||
headers,
|
||||
});
|
||||
|
||||
let resultData = data;
|
||||
|
||||
if (status >= 400) {
|
||||
logger.error("HTTP Error %d calling %s", status, sanitizeErrorURL(url));
|
||||
}
|
||||
|
||||
if (status === 200) {
|
||||
if (!validateWidgetData(widget, endpoint, resultData)) {
|
||||
return res
|
||||
.status(500)
|
||||
.json({ error: { message: "Invalid data", url: sanitizeErrorURL(url), data: resultData } });
|
||||
}
|
||||
}
|
||||
|
||||
if (contentType) res.setHeader("Content-Type", contentType);
|
||||
return res.status(status).send(resultData);
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Invalid or missing proxy service type '%s' in group '%s'", service, group);
|
||||
return res.status(400).json({ error: "Invalid proxy service type" });
|
||||
}
|
32
src/widgets/komodo/widget.js
Normal file
32
src/widgets/komodo/widget.js
Normal file
@ -0,0 +1,32 @@
|
||||
import komodoProxyHandler from "./proxy";
|
||||
|
||||
const widget = {
|
||||
api: "{url}/{endpoint}",
|
||||
proxyHandler: komodoProxyHandler,
|
||||
|
||||
mappings: {
|
||||
containers: {
|
||||
endpoint: "containers", // api actually uses unified read endpoint
|
||||
body: {
|
||||
type: "GetDockerContainersSummary",
|
||||
params: {},
|
||||
},
|
||||
},
|
||||
stacks: {
|
||||
endpoint: "stacks", // api actually uses unified read endpoint
|
||||
body: {
|
||||
type: "GetStacksSummary",
|
||||
params: {},
|
||||
},
|
||||
},
|
||||
servers: {
|
||||
endpoint: "servers", // api actually uses unified read endpoint
|
||||
body: {
|
||||
type: "GetServersSummary",
|
||||
params: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default widget;
|
@ -54,6 +54,7 @@ import jellystat from "./jellystat/widget";
|
||||
import karakeep from "./karakeep/widget";
|
||||
import kavita from "./kavita/widget";
|
||||
import komga from "./komga/widget";
|
||||
import komodo from "./komodo/widget";
|
||||
import kopia from "./kopia/widget";
|
||||
import lidarr from "./lidarr/widget";
|
||||
import linkwarden from "./linkwarden/widget";
|
||||
@ -197,6 +198,7 @@ const widgets = {
|
||||
jellystat,
|
||||
kavita,
|
||||
komga,
|
||||
komodo,
|
||||
kopia,
|
||||
lidarr,
|
||||
linkwarden,
|
||||
|
Loading…
x
Reference in New Issue
Block a user