Merge remote-tracking branch 'upstream/dev' into integration

This commit is contained in:
djeinstine 2024-11-20 14:42:15 +00:00
commit f5a7fada2f
14 changed files with 138 additions and 32 deletions

View File

@ -26,8 +26,6 @@ on:
merge_group:
env:
# Use docker.io for Docker Hub if empty
REGISTRY: ghcr.io
# github.repository as <account>/<repo>
IMAGE_NAME: ${{ github.repository }}
@ -66,14 +64,6 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
# Install the cosign tool except on PR
# https://github.com/sigstore/cosign-installer
- name: Install cosign
if: github.event_name != 'pull_request'
uses: sigstore/cosign-installer@main
with:
cosign-release: 'v1.13.1' # optional
# Setup QEMU
# https://github.com/marketplace/actions/docker-setup-buildx#with-qemu
- name: Setup QEMU
@ -99,9 +89,15 @@ jobs:
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# Extract metadata (tags, labels) for Docker
# https://github.com/docker/metadata-action
@ -109,7 +105,9 @@ jobs:
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
images: |
${{ env.IMAGE_NAME }}
ghcr.io/${{ env.IMAGE_NAME }}
flavor: |
latest=auto
@ -133,19 +131,6 @@ jobs:
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
# Sign the resulting Docker image digest except on PRs.
# This will only write to the public Rekor transparency log when the Docker
# repository is public to avoid leaking data. If you would like to publish
# transparency data even for private images, pass --force to cosign below.
# https://github.com/sigstore/cosign
# - name: Sign the published Docker image
# if: ${{ github.event_name != 'pull_request' }}
# env:
# COSIGN_EXPERIMENTAL: "true"
# # This step uses the identity token to provision an ephemeral certificate
# # against the sigstore community Fulcio instance.
# run: echo "${{ steps.meta.outputs.tags }}" | xargs -I {} cosign sign {}@${{ steps.build-and-push.outputs.digest }}
# Temp fix
# https://github.com/docker/build-push-action/issues/252
# https://github.com/moby/buildkit/issues/1896

View File

@ -5,7 +5,11 @@ description: Unifi Controller Information Widget Configuration
_(Find the Unifi Controller service widget [here](../services/unifi-controller.md))_
You can display general connectivity status from your Unifi (Network) Controller. When authenticating you will want to use a local account that has at least read privileges.
You can display general connectivity status from your Unifi (Network) Controller.
!!!
When authenticating you will want to use a local account that has at least read privileges.
An optional 'site' parameter can be supplied, if it is not the widget will use the default site for the controller.

View 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.

View File

@ -8,6 +8,7 @@ search:
You can also find a list of all available service widgets in the sidebar navigation.
- [Adguard Home](adguard-home.md)
- [ArgoCD](argocd.md)
- [Atsumeru](atsumeru.md)
- [Audiobookshelf](audiobookshelf.md)
- [Authentik](authentik.md)

View File

@ -7,7 +7,11 @@ Learn more about [Unifi Controller](https://ui.com/).
_(Find the Unifi Controller information widget [here](../info/unifi_controller.md))_
You can display general connectivity status from your Unifi (Network) Controller. When authenticating you will want to use a local account that has at least read privileges.
You can display general connectivity status from your Unifi (Network) Controller.
!!!
When authenticating you will want to use a local account that has at least read privileges.
An optional 'site' parameter can be supplied, if it is not the widget will use the default site for the controller.

View File

@ -31,6 +31,7 @@ nav:
- "Service Widgets":
- widgets/services/index.md
- widgets/services/adguard-home.md
- widgets/services/argocd.md
- widgets/services/atsumeru.md
- widgets/services/audiobookshelf.md
- widgets/services/authentik.md

View File

@ -988,5 +988,15 @@
"memory": "MEM",
"disk": "Disk",
"network": "NET"
},
"argocd": {
"apps": "Apps",
"synced": "Synced",
"outOfSync": "Out Of Sync",
"healthy": "Healthy",
"degraded": "Degraded",
"progressing": "Progressing",
"missing": "Missing",
"suspended": "Suspended"
}
}

View File

@ -36,6 +36,7 @@ export default async function credentialedProxyHandler(req, res, map) {
headers["X-gotify-Key"] = `${widget.key}`;
} else if (
[
"argocd",
"authentik",
"cloudflared",
"ghostfolio",

View 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>
);
}

View 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;

View File

@ -46,11 +46,8 @@ export default function Component({ service }) {
<Block label="audiobookshelf.books" value={t("common.number", { value: totalBooks })} />
<Block
label="audiobookshelf.booksDuration"
value={t("common.number", {
value: totalBooksDuration / 60,
maximumFractionDigits: 0,
style: "unit",
unit: "minute",
value={t("common.duration", {
value: totalBooksDuration,
})}
/>
</Container>

View File

@ -2,6 +2,7 @@ import dynamic from "next/dynamic";
const components = {
adguard: dynamic(() => import("./adguard/component")),
argocd: dynamic(() => import("./argocd/component")),
atsumeru: dynamic(() => import("./atsumeru/component")),
audiobookshelf: dynamic(() => import("./audiobookshelf/component")),
authentik: dynamic(() => import("./authentik/component")),

View File

@ -5,6 +5,7 @@ import Block from "components/services/widget/block";
import useWidgetAPI from "utils/proxy/use-widget-api";
function formatValue(t, metric, rawValue) {
if (!metric?.format) return rawValue;
if (!rawValue) return "-";
let value = rawValue;

View File

@ -1,4 +1,5 @@
import adguard from "./adguard/widget";
import argocd from "./argocd/widget";
import atsumeru from "./atsumeru/widget";
import audiobookshelf from "./audiobookshelf/widget";
import authentik from "./authentik/widget";
@ -130,6 +131,7 @@ import zabbix from "./zabbix/widget";
const widgets = {
adguard,
argocd,
atsumeru,
audiobookshelf,
authentik,