mirror of
https://github.com/karl0ss/homepage.git
synced 2025-05-05 15:03:39 +01:00
Feature: Add ArgoCD widget (#4305)
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
This commit is contained in:
parent
adde687331
commit
4a3a4c846e
33
docs/widgets/services/argocd.md
Normal file
33
docs/widgets/services/argocd.md
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
title: ArgoCD
|
||||||
|
description: ArgoCD Widget Configuration
|
||||||
|
---
|
||||||
|
|
||||||
|
Learn more about [ArgoCD](https://argo-cd.readthedocs.io/en/stable/).
|
||||||
|
|
||||||
|
Allowed fields (limited to a max of 4): `["apps", "synced", "outOfSync", "healthy", "progressing", "degraded", "suspended", "missing"]`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
widget:
|
||||||
|
type: argocd
|
||||||
|
url: http://argocd.host.or.ip:port
|
||||||
|
key: argocdapikey
|
||||||
|
```
|
||||||
|
|
||||||
|
You can generate an API key either by creating a bearer token for an existing account, see [Authorization](https://argo-cd.readthedocs.io/en/latest/developer-guide/api-docs/#authorization) (not recommended) or create a new local user account with limited privileges and generate an authentication token for this account. To do this the steps are:
|
||||||
|
|
||||||
|
- [Create a new local user](https://argo-cd.readthedocs.io/en/stable/operator-manual/user-management/#create-new-user) and give it the `apiKey` capability
|
||||||
|
- Setup [RBAC configuration](https://argo-cd.readthedocs.io/en/stable/operator-manual/rbac/#rbac-configuration) for your the user and give it readonly access to your ArgoCD resources, e.g. by giving it the `role:readonly` role.
|
||||||
|
- In your ArgoCD project under _Settings / Accounts_ open the newly created account and in the _Tokens_ section click on _Generate New_ to generate an access token, optionally specifying an expiry date.
|
||||||
|
|
||||||
|
If you installed ArgoCD via the official Helm chart, the account creation and rbac config can be achived by overriding these helm values:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
configs:
|
||||||
|
cm:
|
||||||
|
accounts.readonly: apiKey
|
||||||
|
rbac:
|
||||||
|
policy.csv: "g, readonly, role:readonly"
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates a new account called `readonly` and attaches the `role:readonly` role to it.
|
@ -8,6 +8,7 @@ search:
|
|||||||
You can also find a list of all available service widgets in the sidebar navigation.
|
You can also find a list of all available service widgets in the sidebar navigation.
|
||||||
|
|
||||||
- [Adguard Home](adguard-home.md)
|
- [Adguard Home](adguard-home.md)
|
||||||
|
- [ArgoCD](argocd.md)
|
||||||
- [Atsumeru](atsumeru.md)
|
- [Atsumeru](atsumeru.md)
|
||||||
- [Audiobookshelf](audiobookshelf.md)
|
- [Audiobookshelf](audiobookshelf.md)
|
||||||
- [Authentik](authentik.md)
|
- [Authentik](authentik.md)
|
||||||
|
@ -31,6 +31,7 @@ nav:
|
|||||||
- "Service Widgets":
|
- "Service Widgets":
|
||||||
- widgets/services/index.md
|
- widgets/services/index.md
|
||||||
- widgets/services/adguard-home.md
|
- widgets/services/adguard-home.md
|
||||||
|
- widgets/services/argocd.md
|
||||||
- widgets/services/atsumeru.md
|
- widgets/services/atsumeru.md
|
||||||
- widgets/services/audiobookshelf.md
|
- widgets/services/audiobookshelf.md
|
||||||
- widgets/services/authentik.md
|
- widgets/services/authentik.md
|
||||||
|
@ -988,5 +988,15 @@
|
|||||||
"memory": "MEM",
|
"memory": "MEM",
|
||||||
"disk": "Disk",
|
"disk": "Disk",
|
||||||
"network": "NET"
|
"network": "NET"
|
||||||
|
},
|
||||||
|
"argocd": {
|
||||||
|
"apps": "Apps",
|
||||||
|
"synced": "Synced",
|
||||||
|
"outOfSync": "Out Of Sync",
|
||||||
|
"healthy": "Healthy",
|
||||||
|
"degraded": "Degraded",
|
||||||
|
"progressing": "Progressing",
|
||||||
|
"missing": "Missing",
|
||||||
|
"suspended": "Suspended"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ export default async function credentialedProxyHandler(req, res, map) {
|
|||||||
headers["X-gotify-Key"] = `${widget.key}`;
|
headers["X-gotify-Key"] = `${widget.key}`;
|
||||||
} else if (
|
} else if (
|
||||||
[
|
[
|
||||||
|
"argocd",
|
||||||
"authentik",
|
"authentik",
|
||||||
"cloudflared",
|
"cloudflared",
|
||||||
"ghostfolio",
|
"ghostfolio",
|
||||||
|
52
src/widgets/argocd/component.jsx
Normal file
52
src/widgets/argocd/component.jsx
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import Container from "components/services/widget/container";
|
||||||
|
import Block from "components/services/widget/block";
|
||||||
|
import useWidgetAPI from "utils/proxy/use-widget-api";
|
||||||
|
|
||||||
|
export default function Component({ service }) {
|
||||||
|
const { widget } = service;
|
||||||
|
|
||||||
|
if (!widget.fields) {
|
||||||
|
widget.fields = ["apps", "synced", "outOfSync", "healthy"];
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAX_ALLOWED_FIELDS = 4;
|
||||||
|
if (widget.fields.length > MAX_ALLOWED_FIELDS) {
|
||||||
|
widget.fields = widget.fields.slice(0, MAX_ALLOWED_FIELDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data: appsData, error: appsError } = useWidgetAPI(widget, "applications");
|
||||||
|
|
||||||
|
const appCounts = widget.fields.map((status) => {
|
||||||
|
if (status === "apps") {
|
||||||
|
return { status, count: appsData?.items?.length };
|
||||||
|
}
|
||||||
|
const count = appsData?.items?.filter(
|
||||||
|
(item) =>
|
||||||
|
item.status?.sync?.status.toLowerCase() === status.toLowerCase() ||
|
||||||
|
item.status?.health?.status.toLowerCase() === status.toLowerCase(),
|
||||||
|
).length;
|
||||||
|
return { status, count };
|
||||||
|
});
|
||||||
|
|
||||||
|
if (appsError) {
|
||||||
|
return <Container service={service} error={appsError} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!appsData) {
|
||||||
|
return (
|
||||||
|
<Container service={service}>
|
||||||
|
{appCounts.map((a) => (
|
||||||
|
<Block label={`argocd.${a.status}`} key={a.status} />
|
||||||
|
))}
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container service={service}>
|
||||||
|
{appCounts.map((a) => (
|
||||||
|
<Block label={`argocd.${a.status}`} key={a.status} value={a.count} />
|
||||||
|
))}
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
14
src/widgets/argocd/widget.js
Normal file
14
src/widgets/argocd/widget.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
|
||||||
|
|
||||||
|
const widget = {
|
||||||
|
api: "{url}/api/v1/{endpoint}",
|
||||||
|
proxyHandler: credentialedProxyHandler,
|
||||||
|
|
||||||
|
mappings: {
|
||||||
|
applications: {
|
||||||
|
endpoint: "applications",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default widget;
|
@ -2,6 +2,7 @@ import dynamic from "next/dynamic";
|
|||||||
|
|
||||||
const components = {
|
const components = {
|
||||||
adguard: dynamic(() => import("./adguard/component")),
|
adguard: dynamic(() => import("./adguard/component")),
|
||||||
|
argocd: dynamic(() => import("./argocd/component")),
|
||||||
atsumeru: dynamic(() => import("./atsumeru/component")),
|
atsumeru: dynamic(() => import("./atsumeru/component")),
|
||||||
audiobookshelf: dynamic(() => import("./audiobookshelf/component")),
|
audiobookshelf: dynamic(() => import("./audiobookshelf/component")),
|
||||||
authentik: dynamic(() => import("./authentik/component")),
|
authentik: dynamic(() => import("./authentik/component")),
|
||||||
|
@ -5,6 +5,7 @@ import Block from "components/services/widget/block";
|
|||||||
import useWidgetAPI from "utils/proxy/use-widget-api";
|
import useWidgetAPI from "utils/proxy/use-widget-api";
|
||||||
|
|
||||||
function formatValue(t, metric, rawValue) {
|
function formatValue(t, metric, rawValue) {
|
||||||
|
if (!metric?.format) return rawValue;
|
||||||
if (!rawValue) return "-";
|
if (!rawValue) return "-";
|
||||||
|
|
||||||
let value = rawValue;
|
let value = rawValue;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import adguard from "./adguard/widget";
|
import adguard from "./adguard/widget";
|
||||||
|
import argocd from "./argocd/widget";
|
||||||
import atsumeru from "./atsumeru/widget";
|
import atsumeru from "./atsumeru/widget";
|
||||||
import audiobookshelf from "./audiobookshelf/widget";
|
import audiobookshelf from "./audiobookshelf/widget";
|
||||||
import authentik from "./authentik/widget";
|
import authentik from "./authentik/widget";
|
||||||
@ -130,6 +131,7 @@ import zabbix from "./zabbix/widget";
|
|||||||
|
|
||||||
const widgets = {
|
const widgets = {
|
||||||
adguard,
|
adguard,
|
||||||
|
argocd,
|
||||||
atsumeru,
|
atsumeru,
|
||||||
audiobookshelf,
|
audiobookshelf,
|
||||||
authentik,
|
authentik,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user