mirror of
https://github.com/gethomepage/homepage.git
synced 2025-12-24 05:48:08 +08:00
Feature: Pangolin service widget (#6065)
Some checks failed
Crowdin Action / Crowdin Sync (push) Has been cancelled
Docker CI / Linting Checks (push) Has been cancelled
Docker CI / Docker Build & Push (push) Has been cancelled
Repository Maintenance / Stale (push) Has been cancelled
Repository Maintenance / Lock Old Threads (push) Has been cancelled
Repository Maintenance / Close Answered Discussions (push) Has been cancelled
Repository Maintenance / Close Outdated Discussions (push) Has been cancelled
Repository Maintenance / Close Unsupported Feature Requests (push) Has been cancelled
Some checks failed
Crowdin Action / Crowdin Sync (push) Has been cancelled
Docker CI / Linting Checks (push) Has been cancelled
Docker CI / Docker Build & Push (push) Has been cancelled
Repository Maintenance / Stale (push) Has been cancelled
Repository Maintenance / Lock Old Threads (push) Has been cancelled
Repository Maintenance / Close Answered Discussions (push) Has been cancelled
Repository Maintenance / Close Outdated Discussions (push) Has been cancelled
Repository Maintenance / Close Unsupported Feature Requests (push) Has been cancelled
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
This commit is contained in:
29
docs/widgets/services/pangolin.md
Normal file
29
docs/widgets/services/pangolin.md
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
title: Pangolin
|
||||
description: Pangolin Widget Configuration
|
||||
---
|
||||
|
||||
Learn more about [Pangolin](https://github.com/fosrl/pangolin).
|
||||
|
||||
This widget shows sites (online/total), resources (healthy/total), targets (healthy/total), and traffic statistics for a Pangolin organization. A resource is considered healthy if at least one of its targets is healthy, or if it has no targets.
|
||||
|
||||
Allowed fields: `["sites", "resources", "targets", "traffic", "in", "out"]` (maximum of 4).
|
||||
|
||||
```yaml
|
||||
widget:
|
||||
type: pangolin
|
||||
url: https://api.pangolin.net
|
||||
key: your-api-key
|
||||
org: your-org-id
|
||||
```
|
||||
|
||||
Find your organization ID in the URL when logged in (e.g., `https://app.pangolin.net/{org-id}/...`).
|
||||
|
||||
## API Key Setup
|
||||
|
||||
Create an API key with the following permissions:
|
||||
|
||||
- **List Sites**
|
||||
- **List Resources**
|
||||
|
||||
**Self-Hosted:** Enable the [Integration API](https://docs.pangolin.net/self-host/advanced/integration-api) in your Pangolin configuration before creating the key.
|
||||
@@ -122,6 +122,7 @@ nav:
|
||||
- widgets/services/opnsense.md
|
||||
- widgets/services/openwrt.md
|
||||
- widgets/services/overseerr.md
|
||||
- widgets/services/pangolin.md
|
||||
- widgets/services/paperlessngx.md
|
||||
- widgets/services/peanut.md
|
||||
- widgets/services/pfsense.md
|
||||
|
||||
@@ -599,6 +599,15 @@
|
||||
"inbox": "Inbox",
|
||||
"total": "Total"
|
||||
},
|
||||
"pangolin": {
|
||||
"orgs": "Orgs",
|
||||
"sites": "Sites",
|
||||
"resources": "Resources",
|
||||
"targets": "Targets",
|
||||
"traffic": "Traffic",
|
||||
"in": "In",
|
||||
"out": "Out"
|
||||
},
|
||||
"peanut": {
|
||||
"battery_charge": "Battery Charge",
|
||||
"ups_load": "UPS Load",
|
||||
|
||||
@@ -53,6 +53,7 @@ export default async function credentialedProxyHandler(req, res, map) {
|
||||
"linkwarden",
|
||||
"mealie",
|
||||
"netalertx",
|
||||
"pangolin",
|
||||
"tailscale",
|
||||
"tandoor",
|
||||
"pterodactyl",
|
||||
|
||||
@@ -97,6 +97,7 @@ const components = {
|
||||
openmediavault: dynamic(() => import("./openmediavault/component")),
|
||||
openwrt: dynamic(() => import("./openwrt/component")),
|
||||
paperlessngx: dynamic(() => import("./paperlessngx/component")),
|
||||
pangolin: dynamic(() => import("./pangolin/component")),
|
||||
pfsense: dynamic(() => import("./pfsense/component")),
|
||||
photoprism: dynamic(() => import("./photoprism/component")),
|
||||
proxmoxbackupserver: dynamic(() => import("./proxmoxbackupserver/component")),
|
||||
|
||||
70
src/widgets/pangolin/component.jsx
Normal file
70
src/widgets/pangolin/component.jsx
Normal file
@@ -0,0 +1,70 @@
|
||||
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;
|
||||
|
||||
if (!widget.fields) {
|
||||
widget.fields = ["sites", "resources", "targets", "traffic"];
|
||||
} else if (widget.fields?.length > MAX_ALLOWED_FIELDS) {
|
||||
widget.fields = widget.fields.slice(0, MAX_ALLOWED_FIELDS);
|
||||
}
|
||||
|
||||
const { data: sitesData, error: sitesError } = useWidgetAPI(widget, "sites");
|
||||
const { data: resourcesData, error: resourcesError } = useWidgetAPI(widget, "resources");
|
||||
|
||||
if (sitesError || resourcesError) {
|
||||
return <Container service={service} error={sitesError || resourcesError} />;
|
||||
}
|
||||
|
||||
if (!sitesData || !resourcesData) {
|
||||
return (
|
||||
<Container service={service}>
|
||||
<Block label="pangolin.sites" />
|
||||
<Block label="pangolin.resources" />
|
||||
<Block label="pangolin.targets" />
|
||||
<Block label="pangolin.traffic" />
|
||||
<Block label="pangolin.in" />
|
||||
<Block label="pangolin.out" />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
const sites = sitesData.data?.sites || [];
|
||||
const resources = resourcesData.data?.resources || [];
|
||||
|
||||
const sitesTotal = sites.length;
|
||||
const sitesOnline = sites.filter((s) => s.online).length;
|
||||
|
||||
const resourcesTotal = resources.length;
|
||||
const resourcesHealthy = resources.filter(
|
||||
(r) => r.targets?.some((t) => t.healthStatus !== "unhealthy") || !r.targets?.length,
|
||||
).length;
|
||||
|
||||
const targetsTotal = resources.reduce((sum, r) => sum + (r.targets?.length || 0), 0);
|
||||
const targetsHealthy = resources.reduce(
|
||||
(sum, r) => sum + (r.targets?.filter((t) => t.healthStatus !== "unhealthy").length || 0),
|
||||
0,
|
||||
);
|
||||
|
||||
const trafficIn = sites.reduce((sum, s) => sum + (s.megabytesIn || 0), 0) * 1_000_000;
|
||||
const trafficOut = sites.reduce((sum, s) => sum + (s.megabytesOut || 0), 0) * 1_000_000;
|
||||
const trafficTotal = trafficIn + trafficOut;
|
||||
|
||||
return (
|
||||
<Container service={service}>
|
||||
<Block label="pangolin.sites" value={`${sitesOnline} / ${sitesTotal}`} />
|
||||
<Block label="pangolin.resources" value={`${resourcesHealthy} / ${resourcesTotal}`} />
|
||||
<Block label="pangolin.targets" value={`${targetsHealthy} / ${targetsTotal}`} />
|
||||
<Block label="pangolin.traffic" value={t("common.bytes", { value: trafficTotal })} />
|
||||
<Block label="pangolin.in" value={t("common.bytes", { value: trafficIn })} />
|
||||
<Block label="pangolin.out" value={t("common.bytes", { value: trafficOut })} />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
17
src/widgets/pangolin/widget.js
Normal file
17
src/widgets/pangolin/widget.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
|
||||
|
||||
const widget = {
|
||||
api: "{url}/v1/{endpoint}",
|
||||
proxyHandler: credentialedProxyHandler,
|
||||
|
||||
mappings: {
|
||||
sites: {
|
||||
endpoint: "org/{org}/sites",
|
||||
},
|
||||
resources: {
|
||||
endpoint: "org/{org}/resources",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default widget;
|
||||
@@ -87,6 +87,7 @@ import openmediavault from "./openmediavault/widget";
|
||||
import openwrt from "./openwrt/widget";
|
||||
import opnsense from "./opnsense/widget";
|
||||
import overseerr from "./overseerr/widget";
|
||||
import pangolin from "./pangolin/widget";
|
||||
import paperlessngx from "./paperlessngx/widget";
|
||||
import peanut from "./peanut/widget";
|
||||
import pfsense from "./pfsense/widget";
|
||||
@@ -237,6 +238,7 @@ const widgets = {
|
||||
openmediavault,
|
||||
openwrt,
|
||||
paperlessngx,
|
||||
pangolin,
|
||||
peanut,
|
||||
pfsense,
|
||||
photoprism,
|
||||
|
||||
Reference in New Issue
Block a user