mirror of
https://github.com/gethomepage/homepage.git
synced 2025-12-07 09:35:54 -08:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
805f119a25 | ||
|
|
74f76e2656 | ||
|
|
4d38222ba0 | ||
|
|
b9b7c482d4 | ||
|
|
1c5d9ca223 | ||
|
|
8fba3eee1d | ||
|
|
0d6aabc737 | ||
|
|
b124e12509 | ||
|
|
0a23bfe263 | ||
|
|
9fa38c9ba8 | ||
|
|
9484a9ff56 | ||
|
|
ed928bfa25 | ||
|
|
a996d43bbf | ||
|
|
6ce67b5ab8 | ||
|
|
5d531b11e7 |
7
.github/DISCUSSION_TEMPLATE/support.yml
vendored
7
.github/DISCUSSION_TEMPLATE/support.yml
vendored
@@ -1,4 +1,11 @@
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
### ⚠️ Before opening a discussion:
|
||||
|
||||
- [Check the troubleshooting guide](https://gethomepage.dev/latest/troubleshooting/).
|
||||
- [Search existing issues](https://github.com/gethomepage/homepage/search?q=&type=issues) [and discussions](https://github.com/gethomepage/homepage/search?q=&type=discussions).
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
|
||||
@@ -9,6 +9,8 @@ The disk path is the path reported by `df` (Mounted On), or the mount point of t
|
||||
|
||||
The cpu and memory resource information are the container's usage while [glances](glances.md) displays statistics for the host machine on which it is installed.
|
||||
|
||||
The resources widget primarily relies on a popular tool called [systeminformation](https://systeminformation.io). Thus, any limitiations of that software apply, for example, BRTFS RAID is not supported for the disk usage. In this case users may want to use the [glances widget](glances.md) instead.
|
||||
|
||||
_Note: unfortunately, the package used for getting CPU temp ([systeminformation](https://systeminformation.io)) is not compatible with some setups and will not report any value(s) for CPU temp._
|
||||
|
||||
**Any disk you wish to access must be mounted to your container as a volume.**
|
||||
|
||||
@@ -55,6 +55,7 @@ You can also find a list of all available service widgets in the sidebar navigat
|
||||
- [Komga](komga.md)
|
||||
- [Kopia](kopia.md)
|
||||
- [Lidarr](lidarr.md)
|
||||
- [Linkwarden](linkwarden.md)
|
||||
- [Mastodon](mastodon.md)
|
||||
- [Mealie](mealie.md)
|
||||
- [Medusa](medusa.md)
|
||||
|
||||
15
docs/widgets/services/linkwarden.md
Normal file
15
docs/widgets/services/linkwarden.md
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
title: Linkwarden
|
||||
description: Linkwarden Widget Configuration
|
||||
---
|
||||
|
||||
Learn more about [Linkwarden](https://linkwarden.app/).
|
||||
|
||||
Allowed fields: `["links", "collections", "tags"]`.
|
||||
|
||||
```yaml
|
||||
widget:
|
||||
type: linkwarden
|
||||
url: http://linkwarden.host.or.ip
|
||||
key: myApiKeyHere # On your Linkwarden install, go to Settings > Access Tokens. Generate a token.
|
||||
```
|
||||
@@ -9,7 +9,7 @@ This widget requires the installation of the [pfsense-api](https://github.com/ja
|
||||
|
||||
Once pfSense API is installed, you can set the API to be read-only in System > API > Settings.
|
||||
|
||||
There are two currently supported authentication modes: 'Local Database' and 'API Token'. For 'Local Database', use `username` and `password` with the credentials of an admin user. For 'API Token', utilize the `headers` parameter with `client_token` and `client_id` obtained from pfSense as shown below. Do not use both headers and username / password.
|
||||
There are two currently supported authentication modes: 'Local Database' and 'API Key' (v2) / 'API Token' (v1). For 'Local Database', use `username` and `password` with the credentials of an admin user. The specifics of using the API key / token depend on the version of the pfSense API, see the config examples below. Do not use both headers and username / password.
|
||||
|
||||
The interface to monitor is defined by updating the `wan` parameter. It should be referenced as it is shown under Interfaces > Assignments in pfSense.
|
||||
|
||||
@@ -17,15 +17,25 @@ Load is returned instead of cpu utilization. This is a limitation in the pfSense
|
||||
|
||||
Allowed fields: `["load", "memory", "temp", "wanStatus", "wanIP", "disk"]` (maximum of 4)
|
||||
|
||||
For version 2:
|
||||
|
||||
```yaml
|
||||
widget:
|
||||
type: pfsense
|
||||
url: http://pfsense.host.or.ip:port
|
||||
username: user # optional, or API token
|
||||
password: pass # optional, or API token
|
||||
username: user # optional, or API key
|
||||
password: pass # optional, or API key
|
||||
headers: # optional, or username/password
|
||||
Authorization: client_id client_token
|
||||
X-API-Key: key
|
||||
wan: igb0
|
||||
version: 2 # optional, defaults to 1 for api v1
|
||||
fields: ["load", "memory", "temp", "wanStatus"] # optional
|
||||
```
|
||||
|
||||
For version 1:
|
||||
|
||||
```yaml
|
||||
headers: # optional, or username/password
|
||||
Authorization: client_id client_token # obtained from pfSense API
|
||||
version: 1
|
||||
```
|
||||
|
||||
@@ -5,7 +5,7 @@ description: Tube Archivist Widget Configuration
|
||||
|
||||
Learn more about [Tube Archivist](https://github.com/tubearchivist/tubearchivist).
|
||||
|
||||
Requires API key.
|
||||
You must be running at least version 0.4.4
|
||||
|
||||
Allowed fields: `["downloads", "videos", "channels", "playlists"]`.
|
||||
|
||||
@@ -13,5 +13,5 @@ Allowed fields: `["downloads", "videos", "channels", "playlists"]`.
|
||||
widget:
|
||||
type: tubearchivist
|
||||
url: http://tubearchivist.host.or.ip
|
||||
key: apikeyapikeyapikeyapikeyapikey
|
||||
key: tubearchivistapikey
|
||||
```
|
||||
|
||||
@@ -80,6 +80,7 @@ nav:
|
||||
- widgets/services/komga.md
|
||||
- widgets/services/kopia.md
|
||||
- widgets/services/lidarr.md
|
||||
- widgets/services/linkwarden.md
|
||||
- widgets/services/mastodon.md
|
||||
- widgets/services/mealie.md
|
||||
- widgets/services/medusa.md
|
||||
|
||||
114
package-lock.json
generated
114
package-lock.json
generated
@@ -26,7 +26,7 @@
|
||||
"next-i18next": "^12.1.0",
|
||||
"ping": "^0.4.4",
|
||||
"pretty-bytes": "^6.1.1",
|
||||
"raw-body": "^2.5.2",
|
||||
"raw-body": "^3.0.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-i18next": "^11.18.6",
|
||||
@@ -34,9 +34,9 @@
|
||||
"recharts": "^2.12.6",
|
||||
"rrule": "^2.8.1",
|
||||
"swr": "^1.3.0",
|
||||
"systeminformation": "^5.22.7",
|
||||
"systeminformation": "^5.23.2",
|
||||
"tough-cookie": "^4.1.3",
|
||||
"urbackup-server-api": "^0.8.9",
|
||||
"urbackup-server-api": "^0.52.0",
|
||||
"winston": "^3.11.0",
|
||||
"xml-js": "^1.6.11"
|
||||
},
|
||||
@@ -49,7 +49,7 @@
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.8.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"eslint-plugin-react": "^7.34.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"postcss": "^8.4.38",
|
||||
@@ -638,6 +638,18 @@
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgr/core": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz",
|
||||
"integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/unts"
|
||||
}
|
||||
},
|
||||
"node_modules/@rushstack/eslint-patch": {
|
||||
"version": "1.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.7.2.tgz",
|
||||
@@ -1230,17 +1242,17 @@
|
||||
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg=="
|
||||
},
|
||||
"node_modules/async-mutex": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.3.2.tgz",
|
||||
"integrity": "sha512-HuTK7E7MT7jZEh1P9GtRW9+aTWiDWWi9InbZ5hjxrnRa39KS4BW04+xLBhYNS2aXhHUIKZSw3gj4Pn1pj+qGAA==",
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz",
|
||||
"integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.1"
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/async-mutex/node_modules/tslib": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
|
||||
"integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ=="
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
@@ -2855,21 +2867,30 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-prettier": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz",
|
||||
"integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==",
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz",
|
||||
"integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"prettier-linter-helpers": "^1.0.0"
|
||||
"prettier-linter-helpers": "^1.0.0",
|
||||
"synckit": "^0.9.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/eslint-plugin-prettier"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">=7.28.0",
|
||||
"prettier": ">=2.0.0"
|
||||
"@types/eslint": ">=8.0.0",
|
||||
"eslint": ">=8.0.0",
|
||||
"eslint-config-prettier": "*",
|
||||
"prettier": ">=3.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/eslint": {
|
||||
"optional": true
|
||||
},
|
||||
"eslint-config-prettier": {
|
||||
"optional": true
|
||||
}
|
||||
@@ -5695,30 +5716,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
|
||||
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
|
||||
"integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"iconv-lite": "0.6.3",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body/node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||
@@ -6750,10 +6760,32 @@
|
||||
"react": "^16.11.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/synckit": {
|
||||
"version": "0.9.1",
|
||||
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz",
|
||||
"integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@pkgr/core": "^0.1.0",
|
||||
"tslib": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/unts"
|
||||
}
|
||||
},
|
||||
"node_modules/synckit/node_modules/tslib": {
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz",
|
||||
"integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/systeminformation": {
|
||||
"version": "5.22.7",
|
||||
"resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.22.7.tgz",
|
||||
"integrity": "sha512-AWxlP05KeHbpGdgvZkcudJpsmChc2Y5Eo/GvxG/iUA/Aws5LZKHAMSeAo+V+nD+nxWZaxrwpWcnx4SH3oxNL3A==",
|
||||
"version": "5.23.2",
|
||||
"resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.23.2.tgz",
|
||||
"integrity": "sha512-FoipTSwzZR68ZAjXZ8DRH2DFEErMAOi9JvRMsn6i/hTp6Hd4W4nM1W6a+kUyMrp/pd1SIuUzUZvvkQ21yE6Dig==",
|
||||
"os": [
|
||||
"darwin",
|
||||
"linux",
|
||||
@@ -7267,12 +7299,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/urbackup-server-api": {
|
||||
"version": "0.8.9",
|
||||
"resolved": "https://registry.npmjs.org/urbackup-server-api/-/urbackup-server-api-0.8.9.tgz",
|
||||
"integrity": "sha512-Igu6A0xSZeMsiN6PWT7zG4aD+iJR5fXT/j5+xwAvnD/vCNfvVrettIsXv6MftxOajvTmtlgaYu8KDoH1EJQ6DQ==",
|
||||
"version": "0.52.0",
|
||||
"resolved": "https://registry.npmjs.org/urbackup-server-api/-/urbackup-server-api-0.52.0.tgz",
|
||||
"integrity": "sha512-KfroCFZEWCuCkWye1F1JwI2fkO1za/Mf1a8TNGTujzxU0ZGzDqhA1zCOcvV97q7nH1TKFNpw5tMZ06fSCKv2UA==",
|
||||
"dependencies": {
|
||||
"async-mutex": "^0.3.1",
|
||||
"node-fetch": "^2.6.1"
|
||||
"async-mutex": "^0.5.0",
|
||||
"node-fetch": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uri-js": {
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"next-i18next": "^12.1.0",
|
||||
"ping": "^0.4.4",
|
||||
"pretty-bytes": "^6.1.1",
|
||||
"raw-body": "^2.5.2",
|
||||
"raw-body": "^3.0.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-i18next": "^11.18.6",
|
||||
@@ -36,9 +36,9 @@
|
||||
"recharts": "^2.12.6",
|
||||
"rrule": "^2.8.1",
|
||||
"swr": "^1.3.0",
|
||||
"systeminformation": "^5.22.7",
|
||||
"systeminformation": "^5.23.2",
|
||||
"tough-cookie": "^4.1.3",
|
||||
"urbackup-server-api": "^0.8.9",
|
||||
"urbackup-server-api": "^0.52.0",
|
||||
"winston": "^3.11.0",
|
||||
"xml-js": "^1.6.11"
|
||||
},
|
||||
@@ -51,7 +51,7 @@
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.8.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"eslint-plugin-react": "^7.34.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"postcss": "^8.4.38",
|
||||
|
||||
6142
pnpm-lock.yaml
generated
6142
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -905,5 +905,10 @@
|
||||
"cameras": "Cameras",
|
||||
"uptime": "Uptime",
|
||||
"version": "Version"
|
||||
},
|
||||
"linkwarden": {
|
||||
"links": "Links",
|
||||
"collections": "Collections",
|
||||
"tags": "Tags"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export function slugifyAndEncode(tabName) {
|
||||
export default function Tab({ tab }) {
|
||||
const { activeTab, setActiveTab } = useContext(TabContext);
|
||||
|
||||
const matchesTab = decodeURI(activeTab) === slugify(tab);
|
||||
const matchesTab = decodeURIComponent(activeTab) === slugify(tab);
|
||||
|
||||
return (
|
||||
<li
|
||||
|
||||
@@ -48,8 +48,8 @@ export default async function handler(req, res) {
|
||||
logger.error(`no pods found with namespace=${namespace} and labelSelector=${labelSelector}`);
|
||||
return;
|
||||
}
|
||||
const someReady = pods.find((pod) => ["Completed", "Running"].includes(pod.status.phase));
|
||||
const allReady = pods.every((pod) => ["Completed", "Running"].includes(pod.status.phase));
|
||||
const someReady = pods.find((pod) => ["Succeeded", "Running"].includes(pod.status.phase));
|
||||
const allReady = pods.every((pod) => ["Succeeded", "Running"].includes(pod.status.phase));
|
||||
let status = "down";
|
||||
if (allReady) {
|
||||
status = "running";
|
||||
|
||||
@@ -35,9 +35,16 @@ export default async function credentialedProxyHandler(req, res, map) {
|
||||
} else if (widget.type === "gotify") {
|
||||
headers["X-gotify-Key"] = `${widget.key}`;
|
||||
} else if (
|
||||
["authentik", "cloudflared", "ghostfolio", "mealie", "tailscale", "tandoor", "pterodactyl"].includes(
|
||||
widget.type,
|
||||
)
|
||||
[
|
||||
"authentik",
|
||||
"cloudflared",
|
||||
"ghostfolio",
|
||||
"linkwarden",
|
||||
"mealie",
|
||||
"tailscale",
|
||||
"tandoor",
|
||||
"pterodactyl",
|
||||
].includes(widget.type)
|
||||
) {
|
||||
headers.Authorization = `Bearer ${widget.key}`;
|
||||
} else if (widget.type === "truenas") {
|
||||
|
||||
@@ -54,6 +54,7 @@ const components = {
|
||||
komga: dynamic(() => import("./komga/component")),
|
||||
kopia: dynamic(() => import("./kopia/component")),
|
||||
lidarr: dynamic(() => import("./lidarr/component")),
|
||||
linkwarden: dynamic(() => import("./linkwarden/component")),
|
||||
mastodon: dynamic(() => import("./mastodon/component")),
|
||||
mealie: dynamic(() => import("./mealie/component")),
|
||||
medusa: dynamic(() => import("./medusa/component")),
|
||||
|
||||
@@ -10,7 +10,8 @@ export default function Component({ service }) {
|
||||
|
||||
const { data: versionData, error: versionError } = useWidgetAPI(widget, "version");
|
||||
// see https://github.com/gethomepage/homepage/issues/2282
|
||||
const endpoint = versionData?.major >= 1 && versionData?.minor > 84 ? "statistics" : "stats";
|
||||
const endpoint =
|
||||
versionData?.major > 1 || (versionData?.major === 1 && versionData?.minor > 84) ? "statistics" : "stats";
|
||||
const { data: immichData, error: immichError } = useWidgetAPI(widget, endpoint);
|
||||
|
||||
if (immichError || versionError || immichData?.statusCode === 401) {
|
||||
|
||||
55
src/widgets/linkwarden/component.jsx
Normal file
55
src/widgets/linkwarden/component.jsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
|
||||
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;
|
||||
|
||||
const [stats, setStats] = useState({
|
||||
totalLinks: null,
|
||||
collections: { total: null },
|
||||
tags: { total: null },
|
||||
});
|
||||
|
||||
const { data: collectionsStatsData, error: collectionsStatsError } = useWidgetAPI(widget, "collections");
|
||||
const { data: tagsStatsData, error: tagsStatsError } = useWidgetAPI(widget, "tags");
|
||||
|
||||
useEffect(() => {
|
||||
if (collectionsStatsData?.response && tagsStatsData?.response) {
|
||||
setStats({
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
totalLinks: collectionsStatsData.response.reduce((sum, collection) => sum + (collection._count?.links || 0), 0),
|
||||
collections: {
|
||||
total: collectionsStatsData.response.length,
|
||||
},
|
||||
tags: {
|
||||
total: tagsStatsData.response.length,
|
||||
},
|
||||
});
|
||||
}
|
||||
}, [collectionsStatsData, tagsStatsData]);
|
||||
|
||||
if (collectionsStatsError || tagsStatsError) {
|
||||
return <Container service={service} error={collectionsStatsError || tagsStatsError} />;
|
||||
}
|
||||
|
||||
if (!tagsStatsData || !collectionsStatsData) {
|
||||
return (
|
||||
<Container service={service}>
|
||||
<Block label="linkwarden.links" />
|
||||
<Block label="linkwarden.collections" />
|
||||
<Block label="linkwarden.tags" />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Container service={service}>
|
||||
<Block label="linkwarden.links" value={stats.totalLinks} />
|
||||
<Block label="linkwarden.collections" value={stats.collections.total} />
|
||||
<Block label="linkwarden.tags" value={stats.tags.total} />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
17
src/widgets/linkwarden/widget.js
Normal file
17
src/widgets/linkwarden/widget.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
|
||||
|
||||
const widget = {
|
||||
api: "{url}/api/v1/{endpoint}",
|
||||
proxyHandler: credentialedProxyHandler,
|
||||
|
||||
mappings: {
|
||||
collections: {
|
||||
endpoint: "collections",
|
||||
},
|
||||
tags: {
|
||||
endpoint: "tags",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default widget;
|
||||
@@ -25,7 +25,7 @@ const widget = {
|
||||
healthy: entry.healthy,
|
||||
allocated: entry.allocated,
|
||||
free: entry.free,
|
||||
data: entry.topology.data,
|
||||
data: entry.topology?.data ?? [],
|
||||
})),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -32,16 +32,10 @@ export default function Component({ service }) {
|
||||
|
||||
return (
|
||||
<Container service={service}>
|
||||
<Block
|
||||
label="tubearchivist.downloads"
|
||||
value={t("common.number", { value: downloadsData?.paginate?.total_hits })}
|
||||
/>
|
||||
<Block label="tubearchivist.videos" value={t("common.number", { value: videosData?.paginate?.total_hits })} />
|
||||
<Block label="tubearchivist.channels" value={t("common.number", { value: channelsData?.paginate?.total_hits })} />
|
||||
<Block
|
||||
label="tubearchivist.playlists"
|
||||
value={t("common.number", { value: playlistsData?.paginate?.total_hits })}
|
||||
/>
|
||||
<Block label="tubearchivist.downloads" value={t("common.number", { value: downloadsData.pending ?? 0 })} />
|
||||
<Block label="tubearchivist.videos" value={t("common.number", { value: videosData.doc_count ?? 0 })} />
|
||||
<Block label="tubearchivist.channels" value={t("common.number", { value: channelsData.doc_count ?? 0 })} />
|
||||
<Block label="tubearchivist.playlists" value={t("common.number", { value: playlistsData.doc_count ?? 0 })} />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,20 +6,19 @@ const widget = {
|
||||
|
||||
mappings: {
|
||||
downloads: {
|
||||
endpoint: "download",
|
||||
validate: ["paginate"],
|
||||
endpoint: "stats/download",
|
||||
},
|
||||
videos: {
|
||||
endpoint: "video",
|
||||
validate: ["paginate"],
|
||||
endpoint: "stats/video",
|
||||
validate: ["doc_count"],
|
||||
},
|
||||
channels: {
|
||||
endpoint: "channel",
|
||||
validate: ["paginate"],
|
||||
endpoint: "stats/channel",
|
||||
validate: ["doc_count"],
|
||||
},
|
||||
playlists: {
|
||||
endpoint: "playlist",
|
||||
validate: ["paginate"],
|
||||
endpoint: "stats/playlist",
|
||||
validate: ["doc_count"],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ export default function Component({ service }) {
|
||||
return <Container service={service} error={infoError} />;
|
||||
}
|
||||
|
||||
if (!infoData) {
|
||||
if (!infoData || infoData.errorCode) {
|
||||
return (
|
||||
<Container service={service}>
|
||||
<Block label="wgeasy.connected" />
|
||||
|
||||
@@ -46,6 +46,7 @@ import kavita from "./kavita/widget";
|
||||
import komga from "./komga/widget";
|
||||
import kopia from "./kopia/widget";
|
||||
import lidarr from "./lidarr/widget";
|
||||
import linkwarden from "./linkwarden/widget";
|
||||
import mastodon from "./mastodon/widget";
|
||||
import mealie from "./mealie/widget";
|
||||
import medusa from "./medusa/widget";
|
||||
@@ -167,6 +168,7 @@ const widgets = {
|
||||
komga,
|
||||
kopia,
|
||||
lidarr,
|
||||
linkwarden,
|
||||
mastodon,
|
||||
mealie,
|
||||
medusa,
|
||||
|
||||
Reference in New Issue
Block a user