From 6e8fe458183bb50451a4b7a75322ded6c4684600 Mon Sep 17 00:00:00 2001 From: Marco Hofmann Date: Wed, 23 Oct 2024 15:58:35 +0200 Subject: [PATCH 001/100] Documentation: fix typo in zabbix doc (#4178) --- docs/widgets/services/zabbix.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/widgets/services/zabbix.md b/docs/widgets/services/zabbix.md index 47069075..975f4926 100644 --- a/docs/widgets/services/zabbix.md +++ b/docs/widgets/services/zabbix.md @@ -3,7 +3,7 @@ title: Zabbix description: Zabbix Widget Configuration --- -Learn more about [Zabbix](https://github.com/zabbix/zabbix). The widget supports (at least) Zibbax server version 7.0. +Learn more about [Zabbix](https://github.com/zabbix/zabbix). The widget supports (at least) Zabbix server version 7.0. --- From f473569cb2a340530839327bc8b2b9a0815b03ed Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sun, 27 Oct 2024 20:16:32 -0700 Subject: [PATCH 002/100] Update PR guidelines --- docs/widgets/authoring/getting-started.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/widgets/authoring/getting-started.md b/docs/widgets/authoring/getting-started.md index a01b2eee..4b9126ea 100644 --- a/docs/widgets/authoring/getting-started.md +++ b/docs/widgets/authoring/getting-started.md @@ -48,15 +48,14 @@ self-hosted / open-source alternative, we ask that any widgets, etc. are develop ## New Feature Guidelines -- New features should be linked to an existing feature request with at least 10 'up-votes'. The purpose of this requirement is to avoid the addition (and maintenance) of features that might only benefit a small number of users. -- If you have ideas for a larger feature, please open a discussion first. -- Please note that though it is a requirement, a discussion with 10 'up-votes' in no way guarantees that a PR will be merged. +- New features should usually be linked to an existing feature request. The purpose of this requirement is to avoid the addition (and maintenance) of features that might only benefit a small number of users. +- If you have ideas for a larger feature you may want to open a discussion first. ## Service Widget Guidelines To ensure cohesiveness of various widgets, the following should be used as a guide for developing new widgets: -- Please only submit widgets that have been requested and have at least 10 'up-votes'. The purpose of this requirement is to avoid the addition (and maintenance) of service widgets that might only benefit a small number of users. +- Please only submit widgets that target a feature request discussion with at least 10 'up-votes'. The purpose of this requirement is to avoid the addition (and maintenance) of service widgets that might only benefit a small number of users. - Note that we reserve the right to decline widgets for projects that are very young (eg < ~1y) or those with a small reach (eg low GitHub stars). Again, this is in an effort to keep overall widget maintenance under control. - Widgets should be only one row of blocks - Widgets should be no more than 4 blocks wide and generally conform to the styling / design choices of other widgets From a938f8b9faae455a64922dd4a0f4bfb8f9633b29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konrad=20Jasi=C5=84ski?= Date: Mon, 28 Oct 2024 15:58:24 +0100 Subject: [PATCH 003/100] Documentation: Typo in LubeLogger widget documentation (#4205) --- docs/widgets/services/lubelogger.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/widgets/services/lubelogger.md b/docs/widgets/services/lubelogger.md index baa0e4d5..cf2b6439 100644 --- a/docs/widgets/services/lubelogger.md +++ b/docs/widgets/services/lubelogger.md @@ -8,7 +8,7 @@ Learn more about [LubeLogger](https://github.com/hargata/lubelog) (v1.3.7 or hig The widget comes in two 'flavors', one shows data for all vehicles or for just a specific vehicle with the `vehicleID` parameter. Allowed fields: `["vehicles", "serviceRecords", "reminders"]`. -For the single-vehicle version: `["vehicle", "serviceRecords", "reminders", "nextReminder"] +For the single-vehicle version: `["vehicle", "serviceRecords", "reminders", "nextReminder"]`. ```yaml widget: From 01cb9e8830166eee01f75f6720576c8525d8977b Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Mon, 28 Oct 2024 12:27:11 -0700 Subject: [PATCH 004/100] Documentation: add correct pihole v6 password info --- docs/widgets/services/pihole.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/widgets/services/pihole.md b/docs/widgets/services/pihole.md index c8ab83ae..d77b2dfc 100644 --- a/docs/widgets/services/pihole.md +++ b/docs/widgets/services/pihole.md @@ -14,5 +14,5 @@ widget: type: pihole url: http://pi.hole.or.ip version: 6 # required if running v6 or higher, defaults to 5 - key: yourpiholeapikey # optional + key: yourpiholeapikey # optional, in v6 can be your password or app password ``` From 85637f29792176d3fb3f09b4f1cf4789d3f2e3f7 Mon Sep 17 00:00:00 2001 From: ChainSauz <22598783+ChainSauz@users.noreply.github.com> Date: Sat, 9 Nov 2024 07:15:22 -0500 Subject: [PATCH 005/100] Documentation: Update opnsense.md privileges section for OPNSense 24.7.x (#4264) --- docs/widgets/services/opnsense.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/widgets/services/opnsense.md b/docs/widgets/services/opnsense.md index ddda62ff..faaba6b8 100644 --- a/docs/widgets/services/opnsense.md +++ b/docs/widgets/services/opnsense.md @@ -8,7 +8,7 @@ Learn more about [OPNSense](https://opnsense.org/). The API key & secret can be generated via the webui by creating a new user at _System/Access/Users_. Ensure "Generate a scrambled password to prevent local database logins for this user" is checked and then edit the effective privileges selecting **only**: - Diagnostics: System Activity -- Status: Traffic Graph +- Status: Traffic Graph / Reporting: Traffic (OPNSENSE 24.7.x) Finally, create a new API key which will download an `apikey.txt` file with your key and secret in it. Use the values as the username and password fields, respectively, in your homepage config. From ef9068c5bbd95952d7d7d48a084365d5f46bbc7a Mon Sep 17 00:00:00 2001 From: Carlo <138704619+wan0v@users.noreply.github.com> Date: Mon, 11 Nov 2024 04:26:40 +0100 Subject: [PATCH 006/100] Documentation: change authentik port to generic placeholder (#4272) --- docs/widgets/services/authentik.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/widgets/services/authentik.md b/docs/widgets/services/authentik.md index 8968f4bf..f4dafd40 100644 --- a/docs/widgets/services/authentik.md +++ b/docs/widgets/services/authentik.md @@ -20,6 +20,6 @@ Allowed fields: `["users", "loginsLast24H", "failedLoginsLast24H"]`. ```yaml widget: type: authentik - url: http://authentik.host.or.ip:22070 + url: http://authentik.host.or.ip:port key: api_token ``` From 94bbcbe1fb868f8b60cefe5331d581760fecde32 Mon Sep 17 00:00:00 2001 From: Florian Geckeler <43751896+fgeck@users.noreply.github.com> Date: Fri, 22 Nov 2024 00:32:04 +0100 Subject: [PATCH 007/100] Feature: Spoolman Widget (#3959) Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com> --- docs/widgets/services/spoolman.md | 15 +++++++ mkdocs.yml | 1 + public/locales/en/common.json | 3 ++ src/utils/config/service-helpers.js | 6 +++ src/widgets/components.js | 1 + src/widgets/spoolman/component.jsx | 63 +++++++++++++++++++++++++++++ src/widgets/spoolman/widget.js | 14 +++++++ src/widgets/widgets.js | 2 + 8 files changed, 105 insertions(+) create mode 100644 docs/widgets/services/spoolman.md create mode 100644 src/widgets/spoolman/component.jsx create mode 100644 src/widgets/spoolman/widget.js diff --git a/docs/widgets/services/spoolman.md b/docs/widgets/services/spoolman.md new file mode 100644 index 00000000..5baa9268 --- /dev/null +++ b/docs/widgets/services/spoolman.md @@ -0,0 +1,15 @@ +--- +title: Spoolman +description: Spoolman Widget Configuration +--- + +Learn more about [Spoolman](https://github.com/Donkie/Spoolman). + +4 spools are displayed by default. If more than 4 spools are configured in spoolman you can use the spoolIds configuration option to control which are displayed. + +```yaml +widget: + type: spoolman + url: http://spoolman.host.or.ip + spoolIds: [1, 2, 3, 4] # optional +``` diff --git a/mkdocs.yml b/mkdocs.yml index 1e9d59cc..5b350d71 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -138,6 +138,7 @@ nav: - widgets/services/scrutiny.md - widgets/services/sonarr.md - widgets/services/speedtest-tracker.md + - widgets/services/spoolman.md - widgets/services/stash.md - widgets/services/stocks.md - widgets/services/swagdashboard.md diff --git a/public/locales/en/common.json b/public/locales/en/common.json index ab7dcfc9..5abb9a4b 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -998,5 +998,8 @@ "progressing": "Progressing", "missing": "Missing", "suspended": "Suspended" + }, + "spoolman": { + "loading": "Loading" } } diff --git a/src/utils/config/service-helpers.js b/src/utils/config/service-helpers.js index 1566a135..ea82c735 100644 --- a/src/utils/config/service-helpers.js +++ b/src/utils/config/service-helpers.js @@ -492,6 +492,9 @@ export function cleanServiceGroups(groups) { // technitium range, + + // spoolman + spoolIds, } = cleanedService.widget; let fieldsList = fields; @@ -653,6 +656,9 @@ export function cleanServiceGroups(groups) { if (metrics) cleanedService.widget.metrics = metrics; if (refreshInterval) cleanedService.widget.refreshInterval = refreshInterval; } + if (type === "spoolman") { + if (spoolIds !== undefined) cleanedService.widget.spoolIds = spoolIds; + } } return cleanedService; diff --git a/src/widgets/components.js b/src/widgets/components.js index aa476c46..bea37cf2 100644 --- a/src/widgets/components.js +++ b/src/widgets/components.js @@ -111,6 +111,7 @@ const components = { scrutiny: dynamic(() => import("./scrutiny/component")), sonarr: dynamic(() => import("./sonarr/component")), speedtest: dynamic(() => import("./speedtest/component")), + spoolman: dynamic(() => import("./spoolman/component")), stash: dynamic(() => import("./stash/component")), stocks: dynamic(() => import("./stocks/component")), strelaysrv: dynamic(() => import("./strelaysrv/component")), diff --git a/src/widgets/spoolman/component.jsx b/src/widgets/spoolman/component.jsx new file mode 100644 index 00000000..523ecea7 --- /dev/null +++ b/src/widgets/spoolman/component.jsx @@ -0,0 +1,63 @@ +import { useTranslation } from "next-i18next"; + +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 { t } = useTranslation(); + const { widget } = service; + + // eslint-disable-next-line prefer-const + let { data: spoolData, error: spoolError } = useWidgetAPI(widget, "spools"); + + if (spoolError) { + return ; + } + + if (!spoolData) { + const nBlocksGuess = widget.spoolIds?.length ?? 4; + return ( + + {[...Array(nBlocksGuess)].map((_, i) => ( + // eslint-disable-next-line react/no-array-index-key + + ))} + + ); + } + + if (spoolData.error || spoolData.message) { + return ; + } + + if (spoolData.length === 0) { + return ( + + + + ); + } + + if (widget.spoolIds?.length) { + spoolData = spoolData.filter((spool) => widget.spoolIds.includes(spool.id)); + } + + if (spoolData.length > 4) { + spoolData = spoolData.slice(0, 4); + } + + return ( + + {spoolData.map((spool) => ( + + ))} + + ); +} diff --git a/src/widgets/spoolman/widget.js b/src/widgets/spoolman/widget.js new file mode 100644 index 00000000..2c8a3475 --- /dev/null +++ b/src/widgets/spoolman/widget.js @@ -0,0 +1,14 @@ +import credentialedProxyHandler from "utils/proxy/handlers/credentialed"; + +const widget = { + api: "{url}/api/v1/{endpoint}", + proxyHandler: credentialedProxyHandler, + + mappings: { + spools: { + endpoint: "spool", + }, + }, +}; + +export default widget; diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js index 0cad5346..8eb3f51f 100644 --- a/src/widgets/widgets.js +++ b/src/widgets/widgets.js @@ -102,6 +102,7 @@ import sabnzbd from "./sabnzbd/widget"; import scrutiny from "./scrutiny/widget"; import sonarr from "./sonarr/widget"; import speedtest from "./speedtest/widget"; +import spoolman from "./spoolman/widget"; import stash from "./stash/widget"; import stocks from "./stocks/widget"; import strelaysrv from "./strelaysrv/widget"; @@ -237,6 +238,7 @@ const widgets = { scrutiny, sonarr, speedtest, + spoolman, stash, stocks, strelaysrv, From 2b8647b2ef61d6eab8cfc34a629506131ca9df44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Urs=20Kr=C3=B6ll?= <109229014+UrsKroell@users.noreply.github.com> Date: Fri, 22 Nov 2024 16:07:17 +0100 Subject: [PATCH 008/100] Feature: gitlab service widget (#4317) Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com> --- docs/widgets/services/gitlab.md | 20 +++++++++++++ docs/widgets/services/index.md | 1 + mkdocs.yml | 1 + public/locales/en/common.json | 6 ++++ src/utils/proxy/handlers/credentialed.js | 2 ++ src/widgets/components.js | 1 + src/widgets/gitlab/component.jsx | 36 ++++++++++++++++++++++++ src/widgets/gitlab/widget.js | 13 +++++++++ src/widgets/widgets.js | 2 ++ 9 files changed, 82 insertions(+) create mode 100644 docs/widgets/services/gitlab.md create mode 100644 src/widgets/gitlab/component.jsx create mode 100644 src/widgets/gitlab/widget.js diff --git a/docs/widgets/services/gitlab.md b/docs/widgets/services/gitlab.md new file mode 100644 index 00000000..a92434d8 --- /dev/null +++ b/docs/widgets/services/gitlab.md @@ -0,0 +1,20 @@ +--- +title: Gitlab +description: Gitlab Widget Configuration +--- + +Learn more about [Gitlab](https://gitlab.com). + +API requires a personal access token with either `read_api` or `api` permission. See the [gitlab documentation](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#create-a-personal-access-token) for details on generating one. + +Your Gitlab user ID can be found on [your profile page](https://support.circleci.com/hc/en-us/articles/20761157174043-How-to-find-your-GitLab-User-ID). + +Allowed fields: `["events", "issues", "merges", "projects"]`. + +```yaml +widget: + type: gitlab + url: http://gitlab.host.or.ip:port + key: personal-access-token + user_id: 123456 +``` diff --git a/docs/widgets/services/index.md b/docs/widgets/services/index.md index ae506f08..894a31f6 100644 --- a/docs/widgets/services/index.md +++ b/docs/widgets/services/index.md @@ -41,6 +41,7 @@ You can also find a list of all available service widgets in the sidebar navigat - [Gatus](gatus.md) - [Ghostfolio](ghostfolio.md) - [Gitea](gitea.md) +- [Gitlab](gitlab.md) - [Glances](glances.md) - [Gluetun](gluetun.md) - [Gotify](gotify.md) diff --git a/mkdocs.yml b/mkdocs.yml index 5b350d71..a19d3b83 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -64,6 +64,7 @@ nav: - widgets/services/gatus.md - widgets/services/ghostfolio.md - widgets/services/gitea.md + - widgets/services/gitlab.md - widgets/services/glances.md - widgets/services/gluetun.md - widgets/services/gotify.md diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 5abb9a4b..484f76b5 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -1001,5 +1001,11 @@ }, "spoolman": { "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Issues", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/src/utils/proxy/handlers/credentialed.js b/src/utils/proxy/handlers/credentialed.js index 8d4340c2..cbe0422a 100644 --- a/src/utils/proxy/handlers/credentialed.js +++ b/src/utils/proxy/handlers/credentialed.js @@ -94,6 +94,8 @@ export default async function credentialedProxyHandler(req, res, map) { } } else if (widget.type === "wgeasy") { headers.Authorization = widget.password; + } else if (widget.type === "gitlab") { + headers["PRIVATE-TOKEN"] = widget.key; } else { headers["X-API-Key"] = `${widget.key}`; } diff --git a/src/widgets/components.js b/src/widgets/components.js index bea37cf2..19f41d4a 100644 --- a/src/widgets/components.js +++ b/src/widgets/components.js @@ -38,6 +38,7 @@ const components = { gatus: dynamic(() => import("./gatus/component")), ghostfolio: dynamic(() => import("./ghostfolio/component")), gitea: dynamic(() => import("./gitea/component")), + gitlab: dynamic(() => import("./gitlab/component")), glances: dynamic(() => import("./glances/component")), gluetun: dynamic(() => import("./gluetun/component")), gotify: dynamic(() => import("./gotify/component")), diff --git a/src/widgets/gitlab/component.jsx b/src/widgets/gitlab/component.jsx new file mode 100644 index 00000000..fb6f898f --- /dev/null +++ b/src/widgets/gitlab/component.jsx @@ -0,0 +1,36 @@ +import { useTranslation } from "next-i18next"; + +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 { t } = useTranslation(); + const { widget } = service; + + const { data: gitlabCounts, error: gitlabCountsError } = useWidgetAPI(widget, "counts"); + + if (gitlabCountsError) { + return ; + } + + if (!gitlabCounts) { + return ( + + + + + + + ); + } + + return ( + + + + + + + ); +} diff --git a/src/widgets/gitlab/widget.js b/src/widgets/gitlab/widget.js new file mode 100644 index 00000000..26f77a77 --- /dev/null +++ b/src/widgets/gitlab/widget.js @@ -0,0 +1,13 @@ +import credentialedProxyHandler from "utils/proxy/handlers/credentialed"; + +const widget = { + api: "{url}/api/v4/{endpoint}", + proxyHandler: credentialedProxyHandler, + mappings: { + counts: { + endpoint: "users/{user_id}/associations_count", + }, + }, +}; + +export default widget; diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js index 8eb3f51f..9d4bb935 100644 --- a/src/widgets/widgets.js +++ b/src/widgets/widgets.js @@ -32,6 +32,7 @@ import gamedig from "./gamedig/widget"; import gatus from "./gatus/widget"; import ghostfolio from "./ghostfolio/widget"; import gitea from "./gitea/widget"; +import gitlab from "./gitlab/widget"; import glances from "./glances/widget"; import gluetun from "./gluetun/widget"; import gotify from "./gotify/widget"; @@ -164,6 +165,7 @@ const widgets = { gatus, ghostfolio, gitea, + gitlab, glances, gluetun, gotify, From 56972535c7a38e2e03cca4ae61443ac2e52d9f34 Mon Sep 17 00:00:00 2001 From: Simon Emms Date: Fri, 22 Nov 2024 15:41:48 +0000 Subject: [PATCH 009/100] Documentation: additional explainer for the pod-selector (#4316) --- docs/configs/kubernetes.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/configs/kubernetes.md b/docs/configs/kubernetes.md index 06685f3a..29ab6b38 100644 --- a/docs/configs/kubernetes.md +++ b/docs/configs/kubernetes.md @@ -100,6 +100,8 @@ If you are using multiple instances of homepage, an `instance` annotation can be If you have a single service that needs to be shown on multiple specific instances of homepage (but not on all of them), the service can be annotated by multiple `instance.name` annotations, where `name` can be the names of your specific multiple homepage instances. For example, a service that is annotated with `gethomepage.dev/instance.public: ""` and `gethomepage.dev/instance.internal: ""` will be shown on `public` and `internal` homepage instances. +Use the `gethomepage.dev/pod-selector` selector to specify the pod used for the health check. For example, a service that is annotated with `gethomepage.dev/pod-selector: app.kubernetes.io/name=deployment` would link to a pod with the label `app.kubernetes.io/name: deployment`. + ### Traefik IngressRoute support Homepage can also read ingresses defined using the Traefik IngressRoute custom resource definition. Due to the complex nature of Traefik routing rules, it is required for the `gethomepage.dev/href` annotation to be set: From ba5e6dec077a440e6dec5c17d119da8edcc01cba Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Fri, 22 Nov 2024 13:23:07 -0800 Subject: [PATCH 010/100] Documentation: Add missing wan option for opnsense widget --- docs/widgets/services/opnsense.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/widgets/services/opnsense.md b/docs/widgets/services/opnsense.md index faaba6b8..eb6c7c1c 100644 --- a/docs/widgets/services/opnsense.md +++ b/docs/widgets/services/opnsense.md @@ -20,4 +20,5 @@ widget: url: http://opnsense.host.or.ip username: key password: secret + wan: opt1 # optional, defaults to wan ``` From cb45d89163e8448e3285f9afea5dbb6a9342f7ec Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 24 Nov 2024 09:01:47 -0800 Subject: [PATCH 011/100] New Crowdin translations by GitHub Action (#4053) Co-authored-by: Crowdin Bot --- public/locales/af/common.json | 68 ++++++- public/locales/ar/common.json | 56 +++++- public/locales/bg/common.json | 56 +++++- public/locales/ca/common.json | 112 ++++++++--- public/locales/cs/common.json | 56 +++++- public/locales/da/common.json | 56 +++++- public/locales/de/common.json | 58 +++++- public/locales/el/common.json | 56 +++++- public/locales/eo/common.json | 56 +++++- public/locales/es/common.json | 56 +++++- public/locales/eu/common.json | 56 +++++- public/locales/fi/common.json | 56 +++++- public/locales/fr/common.json | 128 +++++++++---- public/locales/he/common.json | 56 +++++- public/locales/hi/common.json | 56 +++++- public/locales/hr/common.json | 74 +++++++- public/locales/hu/common.json | 56 +++++- public/locales/id/common.json | 56 +++++- public/locales/it/common.json | 56 +++++- public/locales/ja/common.json | 56 +++++- public/locales/ko/common.json | 56 +++++- public/locales/lv/common.json | 56 +++++- public/locales/ms/common.json | 56 +++++- public/locales/nl/common.json | 70 ++++++- public/locales/no/common.json | 56 +++++- public/locales/pl/common.json | 96 +++++++--- public/locales/pt/common.json | 290 +++++++++++++++++------------ public/locales/pt_BR/common.json | 56 +++++- public/locales/ro/common.json | 56 +++++- public/locales/ru/common.json | 140 +++++++++----- public/locales/sk/common.json | 56 +++++- public/locales/sl/common.json | 56 +++++- public/locales/sr/common.json | 56 +++++- public/locales/sv/common.json | 56 +++++- public/locales/te/common.json | 56 +++++- public/locales/th/common.json | 56 +++++- public/locales/tr/common.json | 58 +++++- public/locales/uk/common.json | 254 +++++++++++++++---------- public/locales/vi/common.json | 56 +++++- public/locales/yue/common.json | 56 +++++- public/locales/zh-Hans/common.json | 118 ++++++++---- public/locales/zh-Hant/common.json | 56 +++++- 42 files changed, 2707 insertions(+), 439 deletions(-) diff --git a/public/locales/af/common.json b/public/locales/af/common.json index ef835d72..5f5f1883 100644 --- a/public/locales/af/common.json +++ b/public/locales/af/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "ma", "days": "d", "hours": "h", @@ -309,6 +309,16 @@ "stopped": "Gestop", "total": "Totaal" }, + "suwayomi": { + "download": "Afgelaai", + "nondownload": "Nie-Afgelaai", + "read": "Gelees", + "unread": "Ongelees", + "downloadedread": "Afgelaai & Gelees", + "downloadedunread": "Afgelaai en Ongelees", + "nondownloadedread": "Nie-Afgelaai & Gelees", + "nondownloadedunread": "Nie-Afgelaai & Ongelees" + }, "tailscale": { "address": "Adres", "expires": "Verval", @@ -947,11 +957,55 @@ "disaster": "Ramp" }, "lubelogger": { - "vehicle": "Vehicle", - "vehicles": "Vehicles", - "serviceRecords": "Service Records", - "reminders": "Reminders", - "nextReminder": "Next Reminder", - "none": "None" + "vehicle": "Voertuig", + "vehicles": "Voertuie", + "serviceRecords": "Diensrekords", + "reminders": "Herinneringe", + "nextReminder": "Volgende Herinnering", + "none": "Geen" + }, + "vikunja": { + "projects": "Aktiewe Projekte", + "tasks7d": "Take Hierdie week", + "tasksOverdue": "Agterstallige Take", + "tasksInProgress": "Take Aan Die Gang" + }, + "headscale": { + "name": "Naam", + "address": "Adres", + "last_seen": "Laaste Gesien", + "status": "Status", + "online": "Aanlyn", + "offline": "Vanlyn" + }, + "beszel": { + "name": "Naam", + "systems": "Stelsels", + "up": "Op", + "status": "Status", + "updated": "Opgedateer", + "cpu": "SVE", + "memory": "GEH", + "disk": "Skyf", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Gesond", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Vermis", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Laai" + }, + "gitlab": { + "groups": "Groups", + "issues": "Kwessies", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/ar/common.json b/public/locales/ar/common.json index 305a01f0..558de975 100644 --- a/public/locales/ar/common.json +++ b/public/locales/ar/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "ش", "days": "ي", "hours": "س", @@ -309,6 +309,16 @@ "stopped": "متوقف", "total": "المجموع" }, + "suwayomi": { + "download": "مُنزل", + "nondownload": "Non-Downloaded", + "read": "قراءة", + "unread": "غير مقروءة", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "عنوان", "expires": "تنتهي", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "الاسم", + "address": "عنوان", + "last_seen": "آخر ظهور", + "status": "الحالة", + "online": "مُتّصل", + "offline": "غير متصل" + }, + "beszel": { + "name": "الاسم", + "systems": "Systems", + "up": "يعمل", + "status": "الحالة", + "updated": "محدث", + "cpu": "المعالج", + "memory": "الذاكرة", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "سليم", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "مفقود", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "تحميل" + }, + "gitlab": { + "groups": "Groups", + "issues": "المُشكِلات", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/bg/common.json b/public/locales/bg/common.json index 325e5583..15c38b14 100644 --- a/public/locales/bg/common.json +++ b/public/locales/bg/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mo", "days": "d", "hours": "ч", @@ -309,6 +309,16 @@ "stopped": "Спрян", "total": "Общо" }, + "suwayomi": { + "download": "Изтеглени", + "nondownload": "Non-Downloaded", + "read": "Read", + "unread": "Unread", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Address", "expires": "Expires", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Name", + "address": "Address", + "last_seen": "Последно видян", + "status": "Статус", + "online": "Online", + "offline": "Изключен" + }, + "beszel": { + "name": "Name", + "systems": "Systems", + "up": "Up", + "status": "Статус", + "updated": "Updated", + "cpu": "Процесор", + "memory": "Памет", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Healthy", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Липсващи", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Issues", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/ca/common.json b/public/locales/ca/common.json index 46340e4a..9eb7e447 100644 --- a/public/locales/ca/common.json +++ b/public/locales/ca/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mes", "days": "d", "hours": "h", @@ -227,8 +227,8 @@ "seed": "Llavors" }, "develancacheui": { - "cachehitbytes": "Cache Hit Bytes", - "cachemissbytes": "Cache Miss Bytes" + "cachehitbytes": "Bytes trobats a la memòria cau", + "cachemissbytes": "Bytes no trobats a la memòria cau" }, "downloadstation": { "download": "Descarregar", @@ -309,6 +309,16 @@ "stopped": "Aturat", "total": "Total" }, + "suwayomi": { + "download": "Descarregat", + "nondownload": "No descarregat", + "read": "Llegit", + "unread": "Sense llegir", + "downloadedread": "Descarregat i llegit", + "downloadedunread": "Descarregat i per llegir", + "nondownloadedread": "No descarregat i llegit", + "nondownloadedunread": "No descarregat i per llegir" + }, "tailscale": { "address": "Adreça", "expires": "Caduca", @@ -325,15 +335,15 @@ }, "technitium": { "totalQueries": "Consultes", - "totalNoError": "Success", - "totalServerFailure": "Failures", - "totalNxDomain": "NX Domains", - "totalRefused": "Refused", - "totalAuthoritative": "Authoritative", - "totalRecursive": "Recursive", - "totalCached": "Cached", + "totalNoError": "Èxits", + "totalServerFailure": "Fallades", + "totalNxDomain": "Dominis NX", + "totalRefused": "Rebutjat", + "totalAuthoritative": "Autoritatiu", + "totalRecursive": "Recursiu", + "totalCached": "A la memòria cau", "totalBlocked": "Bloquejat", - "totalDropped": "Dropped", + "totalDropped": "Abandonat", "totalClients": "Clients" }, "tdarr": { @@ -844,16 +854,16 @@ }, "romm": { "platforms": "Plataformes", - "totalRoms": "Games", - "saves": "Saves", - "states": "States", - "screenshots": "Screenshots", - "totalfilesize": "Total Size" + "totalRoms": "Jocs", + "saves": "Partides desades", + "states": "Estats", + "screenshots": "Captures de pantalla", + "totalfilesize": "Tamany total" }, "mailcow": { "domains": "Dominis", - "mailboxes": "Mailboxes", - "mails": "Mails", + "mailboxes": "Bústies", + "mails": "Correus", "storage": "Emmagatzematge" }, "netdata": { @@ -934,24 +944,68 @@ "version": "Versió" }, "linkwarden": { - "links": "Links", - "collections": "Collections", + "links": "Enllaços", + "collections": "Col·leccions", "tags": "Etiquetes" }, "zabbix": { - "unclassified": "Not classified", + "unclassified": "No classificat", "information": "Informació", - "warning": "Warning", - "average": "Average", - "high": "High", - "disaster": "Disaster" + "warning": "Avís", + "average": "Mitjana", + "high": "Alt", + "disaster": "Desastre" }, "lubelogger": { "vehicle": "Vehicle", "vehicles": "Vehicles", - "serviceRecords": "Service Records", - "reminders": "Reminders", - "nextReminder": "Next Reminder", - "none": "None" + "serviceRecords": "Constàncies de manteniment", + "reminders": "Recordatoris", + "nextReminder": "Proper recordatori", + "none": "Cap" + }, + "vikunja": { + "projects": "Projectes actius", + "tasks7d": "Tasques a completar aquesta setmana", + "tasksOverdue": "Tasques vençudes", + "tasksInProgress": "Tasques en marxa" + }, + "headscale": { + "name": "Nom", + "address": "Adreça", + "last_seen": "Vist per darrer cop", + "status": "Estat", + "online": "En línia", + "offline": "Fora de línia" + }, + "beszel": { + "name": "Nom", + "systems": "Sistemes", + "up": "Actiu", + "status": "Estat", + "updated": "Actualitzat", + "cpu": "CPU", + "memory": "MEM", + "disk": "Disc", + "network": "XARXA" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Saludable", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Falten", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Carregant" + }, + "gitlab": { + "groups": "Groups", + "issues": "Problemes", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/cs/common.json b/public/locales/cs/common.json index 3cf6aa3b..983beafe 100644 --- a/public/locales/cs/common.json +++ b/public/locales/cs/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "měs.", "days": "d", "hours": "h", @@ -309,6 +309,16 @@ "stopped": "Zastaveno", "total": "Celkem" }, + "suwayomi": { + "download": "Staženo", + "nondownload": "Non-Downloaded", + "read": "Přečteno", + "unread": "Nepřečteno", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Adresa", "expires": "Vyprší", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Name", + "address": "Adresa", + "last_seen": "Naposledy viděno", + "status": "Stav", + "online": "Online", + "offline": "Offline" + }, + "beszel": { + "name": "Name", + "systems": "Systems", + "up": "Up", + "status": "Stav", + "updated": "Aktualizováno", + "cpu": "CPU", + "memory": "RAM", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Zdravý", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Chybějící", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Problémy", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/da/common.json b/public/locales/da/common.json index 6c6fd9b2..de447397 100644 --- a/public/locales/da/common.json +++ b/public/locales/da/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mnd", "days": "d", "hours": "t", @@ -309,6 +309,16 @@ "stopped": "Stoppede", "total": "Total" }, + "suwayomi": { + "download": "Hentet", + "nondownload": "Non-Downloaded", + "read": "Læst", + "unread": "Ulæst", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Adresse", "expires": "Udløber", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Navn", + "address": "Adresse", + "last_seen": "Sidst Set", + "status": "Status", + "online": "Online", + "offline": "Offline" + }, + "beszel": { + "name": "Navn", + "systems": "Systems", + "up": "Op", + "status": "Status", + "updated": "Opdateret", + "cpu": "CPU", + "memory": "RAM", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Sund", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Mangler", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Problemer", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/de/common.json b/public/locales/de/common.json index 254d696d..647e7d8a 100644 --- a/public/locales/de/common.json +++ b/public/locales/de/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "Mo.", "days": "d", "hours": "h", @@ -305,10 +305,20 @@ "ping": "Ping" }, "portainer": { - "running": "Werden ausgeführt", + "running": "Wird ausgeführt", "stopped": "Gestoppt", "total": "Gesamt" }, + "suwayomi": { + "download": "Heruntergeladen", + "nondownload": "Nicht heruntergeladen", + "read": "Gelesen", + "unread": "Ungelesen", + "downloadedread": "Heruntergeladen & gelesen", + "downloadedunread": "Heruntergeladen & ungelesen", + "nondownloadedread": "Nicht heruntergeladen & gelesen", + "nondownloadedunread": "Nicht heruntergeladen & ungelesen" + }, "tailscale": { "address": "Adresse", "expires": "Läuft ab", @@ -953,5 +963,49 @@ "reminders": "Erinnerungen", "nextReminder": "Nächste Erinnerung", "none": "Keine" + }, + "vikunja": { + "projects": "Aktive Projekte", + "tasks7d": "Diese Woche fällige Aufgaben", + "tasksOverdue": "Überfällige Aufgaben", + "tasksInProgress": "Aufgaben in Arbeit" + }, + "headscale": { + "name": "Name", + "address": "Adresse", + "last_seen": "Zuletzt gesehen", + "status": "Status", + "online": "Online", + "offline": "Offline" + }, + "beszel": { + "name": "Name", + "systems": "Systeme", + "up": "Senden", + "status": "Status", + "updated": "Aktualisiert", + "cpu": "CPU", + "memory": "RAM", + "disk": "Festplatte", + "network": "NET" + }, + "argocd": { + "apps": "Anwendungen", + "synced": "Synchronisiert", + "outOfSync": "Nicht mehr synchronisiert", + "healthy": "Fehlerfrei", + "degraded": "Beeinträchtigt", + "progressing": "Fortschritt", + "missing": "Fehlend", + "suspended": "Unterbrochen" + }, + "spoolman": { + "loading": "Wird geladen" + }, + "gitlab": { + "groups": "Groups", + "issues": "Probleme", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/el/common.json b/public/locales/el/common.json index 08c91246..8ce8a7e0 100644 --- a/public/locales/el/common.json +++ b/public/locales/el/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mo", "days": "d", "hours": "h", @@ -309,6 +309,16 @@ "stopped": "Σταματημένο", "total": "Σύνολο" }, + "suwayomi": { + "download": "Κατεβασμένο", + "nondownload": "Non-Downloaded", + "read": "Διαβάστηκε", + "unread": "Μη Διαβασμένο", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Διεύθυνση", "expires": "Λήγει", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Όνομα", + "address": "Διεύθυνση", + "last_seen": "Τελευταία Σύνδεση", + "status": "Κατάσταση", + "online": "Συνδεδεμένοι", + "offline": "Εκτός σύνδεσης" + }, + "beszel": { + "name": "Όνομα", + "systems": "Systems", + "up": "Ping up", + "status": "Κατάσταση", + "updated": "Ενημερώθηκε", + "cpu": "Επεξεργαστής", + "memory": "Μνήμη", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Υγειές", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Απουσιάζει", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Issues", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/eo/common.json b/public/locales/eo/common.json index 8deaa62f..fd21b925 100644 --- a/public/locales/eo/common.json +++ b/public/locales/eo/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mo", "days": "d", "hours": "h", @@ -309,6 +309,16 @@ "stopped": "Stopped", "total": "Totalo" }, + "suwayomi": { + "download": "Downloaded", + "nondownload": "Non-Downloaded", + "read": "Read", + "unread": "Unread", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Address", "expires": "Expires", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Name", + "address": "Address", + "last_seen": "Last Seen", + "status": "Stato", + "online": "Online", + "offline": "Malkonekta" + }, + "beszel": { + "name": "Name", + "systems": "Systems", + "up": "Up", + "status": "Stato", + "updated": "Updated", + "cpu": "Ĉefprocesoro", + "memory": "MEM", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Sana", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Missing", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Issues", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/es/common.json b/public/locales/es/common.json index 4f9affdf..299307d0 100644 --- a/public/locales/es/common.json +++ b/public/locales/es/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "me", "days": "d", "hours": "h", @@ -309,6 +309,16 @@ "stopped": "Detenido", "total": "Total" }, + "suwayomi": { + "download": "Descargado", + "nondownload": "Non-Downloaded", + "read": "Leer", + "unread": "Sin leer", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Dirección", "expires": "Caduca en", @@ -953,5 +963,49 @@ "reminders": "Recordatorios", "nextReminder": "Siguiente recordatorio", "none": "Nada" + }, + "vikunja": { + "projects": "Proyectos activos", + "tasks7d": "Tareas que vencen esta semana", + "tasksOverdue": "Tareas vencidas", + "tasksInProgress": "Tareas en progreso" + }, + "headscale": { + "name": "Nombre", + "address": "Dirección", + "last_seen": "Visto por última vez", + "status": "Estado", + "online": "En línea", + "offline": "Desconectado" + }, + "beszel": { + "name": "Nombre", + "systems": "Systems", + "up": "Activo", + "status": "Estado", + "updated": "Actualizado", + "cpu": "CPU", + "memory": "MEM", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Saludable", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Faltantes", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Cargando" + }, + "gitlab": { + "groups": "Groups", + "issues": "Números", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/eu/common.json b/public/locales/eu/common.json index e96ef729..ef3c5262 100644 --- a/public/locales/eu/common.json +++ b/public/locales/eu/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mo", "days": "d", "hours": "h", @@ -309,6 +309,16 @@ "stopped": "Stopped", "total": "Guztira" }, + "suwayomi": { + "download": "Downloaded", + "nondownload": "Non-Downloaded", + "read": "Read", + "unread": "Unread", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Address", "expires": "Expires", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Name", + "address": "Address", + "last_seen": "Last Seen", + "status": "Status", + "online": "Online", + "offline": "Offline" + }, + "beszel": { + "name": "Name", + "systems": "Systems", + "up": "Up", + "status": "Status", + "updated": "Updated", + "cpu": "CPU", + "memory": "MEM", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Osasuntsu", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Missing", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Issues", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/fi/common.json b/public/locales/fi/common.json index f5724790..402aa8d3 100644 --- a/public/locales/fi/common.json +++ b/public/locales/fi/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mo", "days": "d", "hours": "h", @@ -309,6 +309,16 @@ "stopped": "Pysäytetty", "total": "Yhteensä" }, + "suwayomi": { + "download": "Ladattu", + "nondownload": "Non-Downloaded", + "read": "Read", + "unread": "Unread", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Address", "expires": "Expires", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Name", + "address": "Address", + "last_seen": "Last Seen", + "status": "Tila", + "online": "Online", + "offline": "Offline" + }, + "beszel": { + "name": "Name", + "systems": "Systems", + "up": "Up", + "status": "Tila", + "updated": "Updated", + "cpu": "CPU", + "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" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Issues", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/fr/common.json b/public/locales/fr/common.json index 20b76e93..2f27665d 100644 --- a/public/locales/fr/common.json +++ b/public/locales/fr/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mo", "days": "j", "hours": "h", @@ -227,8 +227,8 @@ "seed": "Seed" }, "develancacheui": { - "cachehitbytes": "Cache Hit Bytes", - "cachemissbytes": "Cache Miss Bytes" + "cachehitbytes": "Octets de la mémoire cache", + "cachemissbytes": "Octets manquants du cache" }, "downloadstation": { "download": "Récep.", @@ -309,6 +309,16 @@ "stopped": "Arrêté", "total": "Total" }, + "suwayomi": { + "download": "Téléchargé", + "nondownload": "Non téléchargé", + "read": "Lu", + "unread": "Non lu", + "downloadedread": "Téléchargé et lu", + "downloadedunread": "Téléchargé et non lu", + "nondownloadedread": "Non téléchargé et lu", + "nondownloadedunread": "Non téléchargé et non lu" + }, "tailscale": { "address": "Adresse", "expires": "Expire", @@ -325,15 +335,15 @@ }, "technitium": { "totalQueries": "Requêtes", - "totalNoError": "Success", - "totalServerFailure": "Failures", - "totalNxDomain": "NX Domains", - "totalRefused": "Refused", - "totalAuthoritative": "Authoritative", - "totalRecursive": "Recursive", - "totalCached": "Cached", + "totalNoError": "Effectué avec succès", + "totalServerFailure": "Échecs", + "totalNxDomain": "Domaines NX", + "totalRefused": "Refusés", + "totalAuthoritative": "Autoritaire", + "totalRecursive": "Récursif", + "totalCached": "Mis en cache", "totalBlocked": "Bloqué", - "totalDropped": "Dropped", + "totalDropped": "Abandonné", "totalClients": "Clients" }, "tdarr": { @@ -756,7 +766,7 @@ "books": "Livres", "authors": "Auteurs", "categories": "Catégories", - "series": "Séries" + "series": "Séries TV" }, "jdownloader": { "downloadCount": "En attente", @@ -844,16 +854,16 @@ }, "romm": { "platforms": "Plateformes", - "totalRoms": "Games", - "saves": "Saves", - "states": "States", - "screenshots": "Screenshots", - "totalfilesize": "Total Size" + "totalRoms": "Jeux", + "saves": "Sauvegardes", + "states": "États", + "screenshots": "Captures d'écran", + "totalfilesize": "Taille totale" }, "mailcow": { "domains": "Domaines", - "mailboxes": "Mailboxes", - "mails": "Mails", + "mailboxes": "Boites mail", + "mails": "Courriels", "storage": "Stockage" }, "netdata": { @@ -922,36 +932,80 @@ "upload": "Envoi" }, "stocks": { - "stocks": "Stocks", - "loading": "Loading", - "open": "Open - US Market", - "closed": "Closed - US Market", - "invalidConfiguration": "Invalid Configuration" + "stocks": "Actions", + "loading": "Chargement", + "open": "Ouvert - Marché américain", + "closed": "Fermé - marché américain", + "invalidConfiguration": "Configuration invalide" }, "frigate": { - "cameras": "Cameras", + "cameras": "Caméras", "uptime": "Démarré depuis", "version": "Version" }, "linkwarden": { - "links": "Links", + "links": "Liens", "collections": "Collections", "tags": "Étiquettes" }, "zabbix": { - "unclassified": "Not classified", + "unclassified": "Non classé", "information": "Informations", - "warning": "Warning", - "average": "Average", - "high": "High", - "disaster": "Disaster" + "warning": "Attention", + "average": "Moyenne", + "high": "Élevé", + "disaster": "" }, "lubelogger": { - "vehicle": "Vehicle", - "vehicles": "Vehicles", - "serviceRecords": "Service Records", - "reminders": "Reminders", - "nextReminder": "Next Reminder", - "none": "None" + "vehicle": "Véhicule", + "vehicles": "Véhicules", + "serviceRecords": "Service d'enregistrements", + "reminders": "Rappels", + "nextReminder": "Prochain rappel", + "none": "Aucun" + }, + "vikunja": { + "projects": "Projets actifs", + "tasks7d": "Tâches à faire cette semaine", + "tasksOverdue": "Tâches en retard", + "tasksInProgress": "Tâche en cours" + }, + "headscale": { + "name": "Nom", + "address": "Adresse", + "last_seen": "Vu pour la dernière fois", + "status": "Statut", + "online": "En ligne", + "offline": "Hors ligne" + }, + "beszel": { + "name": "Nom", + "systems": "Systèmes", + "up": "Up", + "status": "Statut", + "updated": "Mis à jour", + "cpu": "CPU", + "memory": "MÉM", + "disk": "Disque", + "network": "Réseau" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Fonctionnel", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Manquant", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Chargement" + }, + "gitlab": { + "groups": "Groups", + "issues": "Anomalies", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/he/common.json b/public/locales/he/common.json index 2f2fdbd8..0b888396 100644 --- a/public/locales/he/common.json +++ b/public/locales/he/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mo", "days": "d", "hours": "h", @@ -309,6 +309,16 @@ "stopped": "נעצר", "total": "סה\"כ" }, + "suwayomi": { + "download": "הורד", + "nondownload": "Non-Downloaded", + "read": "Read", + "unread": "Unread", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Address", "expires": "Expires", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Name", + "address": "Address", + "last_seen": "Last Seen", + "status": "סטטוס", + "online": "Online", + "offline": "כבוי" + }, + "beszel": { + "name": "Name", + "systems": "Systems", + "up": "Up", + "status": "סטטוס", + "updated": "Updated", + "cpu": "CPU", + "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" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Issues", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/hi/common.json b/public/locales/hi/common.json index c2b9bb70..2076bce7 100644 --- a/public/locales/hi/common.json +++ b/public/locales/hi/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{value, date}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "माह", "days": "d", "hours": "घं.", @@ -309,6 +309,16 @@ "stopped": "Stopped", "total": "Total" }, + "suwayomi": { + "download": "Downloaded", + "nondownload": "Non-Downloaded", + "read": "Read", + "unread": "Unread", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Address", "expires": "Expires", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Name", + "address": "Address", + "last_seen": "Last Seen", + "status": "Status", + "online": "Online", + "offline": "Offline" + }, + "beszel": { + "name": "Name", + "systems": "Systems", + "up": "Up", + "status": "Status", + "updated": "Updated", + "cpu": "CPU", + "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" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Issues", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/hr/common.json b/public/locales/hr/common.json index e8963189..35ae8dab 100644 --- a/public/locales/hr/common.json +++ b/public/locales/hr/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mj", "days": "dan(a)", "hours": "h", @@ -309,6 +309,16 @@ "stopped": "Prekinuto", "total": "Ukupno" }, + "suwayomi": { + "download": "Preuzeto", + "nondownload": "Non-Downloaded", + "read": "Pročitano", + "unread": "Nepročitano", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Adresa", "expires": "Isteče", @@ -844,11 +854,11 @@ }, "romm": { "platforms": "Platforme", - "totalRoms": "Games", + "totalRoms": "Igre", "saves": "Saves", "states": "States", - "screenshots": "Screenshots", - "totalfilesize": "Total Size" + "screenshots": "Snimke ekrana", + "totalfilesize": "Ukupna veličina" }, "mailcow": { "domains": "Domene", @@ -929,19 +939,19 @@ "invalidConfiguration": "Invalid Configuration" }, "frigate": { - "cameras": "Cameras", + "cameras": "Kamere", "uptime": "Vrijeme rada", "version": "Verzija" }, "linkwarden": { - "links": "Links", - "collections": "Collections", + "links": "Poveznice", + "collections": "Zbirke", "tags": "Oznake" }, "zabbix": { "unclassified": "Not classified", "information": "Informacije", - "warning": "Warning", + "warning": "Upozorenje", "average": "Average", "high": "High", "disaster": "Disaster" @@ -950,8 +960,52 @@ "vehicle": "Vehicle", "vehicles": "Vehicles", "serviceRecords": "Service Records", - "reminders": "Reminders", - "nextReminder": "Next Reminder", + "reminders": "Podsjetnici", + "nextReminder": "Sljedeći podsjetnik", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Ime", + "address": "Adresa", + "last_seen": "Zadnje viđeno", + "status": "Stanje", + "online": "Online", + "offline": "Offline" + }, + "beszel": { + "name": "Ime", + "systems": "Systems", + "up": "Dostupno", + "status": "Stanje", + "updated": "Aktualizirano", + "cpu": "CPU", + "memory": "MEM", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Funkcionalno", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Nedostaje", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Problemi", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/hu/common.json b/public/locales/hu/common.json index 5dd30479..abe09fde 100644 --- a/public/locales/hu/common.json +++ b/public/locales/hu/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "hó", "days": "n", "hours": "ó", @@ -309,6 +309,16 @@ "stopped": "Megállított", "total": "Összes" }, + "suwayomi": { + "download": "Letöltött", + "nondownload": "Non-Downloaded", + "read": "Olvasott", + "unread": "Olvasatlan", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Cím", "expires": "Lejár", @@ -953,5 +963,49 @@ "reminders": "Emlékeztetők", "nextReminder": "Következő emlékeztető", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Név", + "address": "Cím", + "last_seen": "Utoljára látott", + "status": "Státusz", + "online": "Csatlakozva", + "offline": "Nem elérhető" + }, + "beszel": { + "name": "Név", + "systems": "Systems", + "up": "Fel", + "status": "Státusz", + "updated": "Frissített", + "cpu": "Processzor", + "memory": "RAM", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Egészséges", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Hiányzik", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Problémák", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/id/common.json b/public/locales/id/common.json index 25ff4526..9d65cbdf 100644 --- a/public/locales/id/common.json +++ b/public/locales/id/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "bulan", "days": "h", "hours": "j", @@ -309,6 +309,16 @@ "stopped": "Terhenti", "total": "Total" }, + "suwayomi": { + "download": "Terunduh", + "nondownload": "Non-Downloaded", + "read": "Baca", + "unread": "Belum Dibaca", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Alamat", "expires": "Kadaluarsa", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Nama", + "address": "Alamat", + "last_seen": "Terakhir terlihat", + "status": "Status", + "online": "Online", + "offline": "Offline" + }, + "beszel": { + "name": "Nama", + "systems": "Systems", + "up": "Hidup", + "status": "Status", + "updated": "Terbarui", + "cpu": "CPU", + "memory": "MEM", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Lancar", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Tidak Ditemukan", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Isu", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/it/common.json b/public/locales/it/common.json index 77e09d1e..eca25f3f 100644 --- a/public/locales/it/common.json +++ b/public/locales/it/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mo", "days": "g", "hours": "o", @@ -309,6 +309,16 @@ "stopped": "Fermati", "total": "Totale" }, + "suwayomi": { + "download": "Scaricato", + "nondownload": "Non-Downloaded", + "read": "Letti", + "unread": "Non letto", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Indirizzo", "expires": "Scade", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Nome", + "address": "Indirizzo", + "last_seen": "Ultima visualizzazione", + "status": "Stato", + "online": "Online", + "offline": "Non in linea" + }, + "beszel": { + "name": "Nome", + "systems": "Systems", + "up": "Up", + "status": "Stato", + "updated": "Aggiornato", + "cpu": "CPU", + "memory": "MEM", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Sano", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Mancanti", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Problemi", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/ja/common.json b/public/locales/ja/common.json index 0734f105..e12ccadf 100644 --- a/public/locales/ja/common.json +++ b/public/locales/ja/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "月", "days": "日", "hours": "時間", @@ -309,6 +309,16 @@ "stopped": "停止中", "total": "合計" }, + "suwayomi": { + "download": "ダウンロード", + "nondownload": "Non-Downloaded", + "read": "既読", + "unread": "未読", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "アドレス", "expires": "失効", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "名前", + "address": "アドレス", + "last_seen": "最終日時", + "status": "状態", + "online": "オンライン", + "offline": "オフライン" + }, + "beszel": { + "name": "名前", + "systems": "Systems", + "up": "稼働", + "status": "状態", + "updated": "更新済", + "cpu": "CPU", + "memory": "MEM", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "正常", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "不明", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "読み込み中" + }, + "gitlab": { + "groups": "Groups", + "issues": "課題", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/ko/common.json b/public/locales/ko/common.json index 245510f1..c760fe30 100644 --- a/public/locales/ko/common.json +++ b/public/locales/ko/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mo", "days": "d", "hours": "h", @@ -309,6 +309,16 @@ "stopped": "중지", "total": "총합" }, + "suwayomi": { + "download": "다운로드됨", + "nondownload": "Non-Downloaded", + "read": "읽음", + "unread": "미열람", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "주소", "expires": "만료", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "이름", + "address": "주소", + "last_seen": "마지막 접속", + "status": "상태", + "online": "온라인", + "offline": "중지" + }, + "beszel": { + "name": "이름", + "systems": "Systems", + "up": "Up", + "status": "상태", + "updated": "Updated", + "cpu": "CPU", + "memory": "MEM", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "좋음", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "빠짐", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "로드 중" + }, + "gitlab": { + "groups": "Groups", + "issues": "Issues", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/lv/common.json b/public/locales/lv/common.json index db1fb11b..e15ccc08 100644 --- a/public/locales/lv/common.json +++ b/public/locales/lv/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mo", "days": "d", "hours": "h", @@ -309,6 +309,16 @@ "stopped": "Stopped", "total": "Kopā" }, + "suwayomi": { + "download": "Lejupielādēts", + "nondownload": "Non-Downloaded", + "read": "Read", + "unread": "Unread", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Address", "expires": "Expires", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Name", + "address": "Address", + "last_seen": "Last Seen", + "status": "Statuss", + "online": "Online", + "offline": "Bezsaistē" + }, + "beszel": { + "name": "Name", + "systems": "Systems", + "up": "Up", + "status": "Statuss", + "updated": "Updated", + "cpu": "CPU", + "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" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Issues", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/ms/common.json b/public/locales/ms/common.json index 08f078ca..f1c4407b 100644 --- a/public/locales/ms/common.json +++ b/public/locales/ms/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "bln", "days": "h", "hours": "j", @@ -309,6 +309,16 @@ "stopped": "Terhenti", "total": "Jumlah" }, + "suwayomi": { + "download": "Telah Muat Turun", + "nondownload": "Non-Downloaded", + "read": "Baca", + "unread": "Belum dibaca", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Address", "expires": "Expires", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Nama", + "address": "Address", + "last_seen": "Last Seen", + "status": "Status", + "online": "Dalam Talian", + "offline": "Luar talian" + }, + "beszel": { + "name": "Nama", + "systems": "Systems", + "up": "Hidup", + "status": "Status", + "updated": "Dikemaskini", + "cpu": "CPU", + "memory": "MEM", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Sihat", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Hilang", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Issues", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/nl/common.json b/public/locales/nl/common.json index e80031cd..60face07 100644 --- a/public/locales/nl/common.json +++ b/public/locales/nl/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mnd", "days": "d", "hours": "u", @@ -309,6 +309,16 @@ "stopped": "Gestopt", "total": "Totaal" }, + "suwayomi": { + "download": "Gedownload", + "nondownload": "Non-Downloaded", + "read": "Gelezen", + "unread": "Ongelezen", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Adres", "expires": "Verloopt", @@ -852,7 +862,7 @@ }, "mailcow": { "domains": "Domeinen", - "mailboxes": "Mailboxes", + "mailboxes": "Mailboxen", "mails": "Mails", "storage": "Opslag" }, @@ -947,11 +957,55 @@ "disaster": "Disaster" }, "lubelogger": { - "vehicle": "Vehicle", - "vehicles": "Vehicles", - "serviceRecords": "Service Records", - "reminders": "Reminders", - "nextReminder": "Next Reminder", - "none": "None" + "vehicle": "Voertuig", + "vehicles": "Voertuigen", + "serviceRecords": "Service Historie", + "reminders": "Herinneringen", + "nextReminder": "Volgende Herinnering", + "none": "Geen" + }, + "vikunja": { + "projects": "Actieve Projecten", + "tasks7d": "Taken Die Deze Week Af Moeten Zijn", + "tasksOverdue": "Achterstallige Taken", + "tasksInProgress": "Taken In Uitvoering" + }, + "headscale": { + "name": "Naam", + "address": "Adres", + "last_seen": "Laatst Gezien", + "status": "Status", + "online": "Bereikbaar", + "offline": "Offline" + }, + "beszel": { + "name": "Naam", + "systems": "Systems", + "up": "Online", + "status": "Status", + "updated": "Bijgewerkt", + "cpu": "CPU", + "memory": "GEH", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Gezond", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Ontbreekt", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Laden" + }, + "gitlab": { + "groups": "Groups", + "issues": "Problemen", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/no/common.json b/public/locales/no/common.json index f6091b60..f987aadb 100644 --- a/public/locales/no/common.json +++ b/public/locales/no/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mnd", "days": "d", "hours": "t", @@ -309,6 +309,16 @@ "stopped": "Stoppet", "total": "Totalt" }, + "suwayomi": { + "download": "Nedlastede", + "nondownload": "Non-Downloaded", + "read": "Read", + "unread": "Ulest", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Adresse", "expires": "Utgår", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Navn", + "address": "Adresse", + "last_seen": "Sist sett", + "status": "Status", + "online": "På nett", + "offline": "Frakoblet" + }, + "beszel": { + "name": "Navn", + "systems": "Systems", + "up": "Oppe", + "status": "Status", + "updated": "Oppdatert", + "cpu": "CPU", + "memory": "MEM", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Friskt", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Mangler", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Issues", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/pl/common.json b/public/locales/pl/common.json index d762743f..0d14325c 100644 --- a/public/locales/pl/common.json +++ b/public/locales/pl/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mc", "days": "d", "hours": "g", @@ -309,6 +309,16 @@ "stopped": "Zatrzymane", "total": "Całkowite" }, + "suwayomi": { + "download": "Pobrano", + "nondownload": "Non-Downloaded", + "read": "Przeczytane", + "unread": "Nieprzeczytane", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Adres", "expires": "Wygasa za", @@ -325,15 +335,15 @@ }, "technitium": { "totalQueries": "Zapytania", - "totalNoError": "Success", - "totalServerFailure": "Failures", - "totalNxDomain": "NX Domains", - "totalRefused": "Refused", - "totalAuthoritative": "Authoritative", - "totalRecursive": "Recursive", - "totalCached": "Cached", + "totalNoError": "Sukces", + "totalServerFailure": "Porażki", + "totalNxDomain": "Domeny NX", + "totalRefused": "Odrzucone", + "totalAuthoritative": "Autorytatywne", + "totalRecursive": "Rekursywne", + "totalCached": "Zbuforowane", "totalBlocked": "Zablokowane", - "totalDropped": "Dropped", + "totalDropped": "Upuszczone", "totalClients": "Klienci" }, "tdarr": { @@ -844,16 +854,16 @@ }, "romm": { "platforms": "Platformy", - "totalRoms": "Games", - "saves": "Saves", - "states": "States", - "screenshots": "Screenshots", - "totalfilesize": "Total Size" + "totalRoms": "Gry", + "saves": "Zapisy", + "states": "Stany", + "screenshots": "Screeny", + "totalfilesize": "Rozmiar całkowity" }, "mailcow": { "domains": "Domeny", - "mailboxes": "Mailboxes", - "mails": "Mails", + "mailboxes": "Skrzynki", + "mails": "Poczta", "storage": "Pamięć" }, "netdata": { @@ -939,12 +949,12 @@ "tags": "Tagi" }, "zabbix": { - "unclassified": "Not classified", + "unclassified": "Niezaklasyfikowane", "information": "Informacje", - "warning": "Warning", - "average": "Average", - "high": "High", - "disaster": "Disaster" + "warning": "Ostrzeżenie", + "average": "Średnia", + "high": "Wysokie", + "disaster": "Katastrofa" }, "lubelogger": { "vehicle": "Vehicle", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Nazwa", + "address": "Adres", + "last_seen": "Ostatnio dostępny", + "status": "Stan", + "online": "Dostępny", + "offline": "Nieosiągalny" + }, + "beszel": { + "name": "Nazwa", + "systems": "Systems", + "up": "Dostępny", + "status": "Stan", + "updated": "Zaktualizowane", + "cpu": "Procesor", + "memory": "RAM", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Zdrowy", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Brakujące", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Wczytywanie" + }, + "gitlab": { + "groups": "Groups", + "issues": "Zgłoszenia", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/pt/common.json b/public/locales/pt/common.json index 264cf3d3..3d3de0a8 100644 --- a/public/locales/pt/common.json +++ b/public/locales/pt/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mês", "days": "d", "hours": "h", @@ -30,9 +30,9 @@ "response_data": "Dados da Resposta" }, "weather": { - "current": "Localização atual", + "current": "Localização actual", "allow": "Clique para permitir", - "updating": "Atualizando", + "updating": "A actualizar", "wait": "Por favor aguarde" }, "search": { @@ -79,7 +79,7 @@ "starting": "A iniciar", "unhealthy": "Não-saudável", "not_found": "Não Encontrado", - "exited": "Encerrado", + "exited": "Saiu", "partial": "Parcial" }, "ping": { @@ -117,10 +117,10 @@ "evcc": { "pv_power": "Produção", "battery_soc": "Bateria", - "grid_power": "Grade", + "grid_power": "Grelha", "home_power": "Consumo", "charge_power": "Carregador", - "watt_hour": "Kw" + "watt_hour": "Wh" }, "flood": { "download": "Descarregar", @@ -129,16 +129,16 @@ "seed": "Semente" }, "freshrss": { - "subscriptions": "Assinaturas", + "subscriptions": "Subscrições", "unread": "Não lida" }, "fritzbox": { "connectionStatus": "Estado", - "connectionStatusUnconfigured": "Não configurado", + "connectionStatusUnconfigured": "Desconfigurado", "connectionStatusConnecting": "A conectar", - "connectionStatusAuthenticating": "Autenticando", + "connectionStatusAuthenticating": "A Autenticar", "connectionStatusPendingDisconnect": "Desconexão pendente", - "connectionStatusDisconnecting": "Desconectando", + "connectionStatusDisconnecting": "A Desconectar", "connectionStatusDisconnected": "Desconectado", "connectionStatusConnected": "Conectado", "uptime": "Ligado", @@ -148,19 +148,19 @@ "up": "Up", "received": "Recebido", "sent": "Enviado", - "externalIPAddress": "Endereço IP externo" + "externalIPAddress": "Endereço IP Externo" }, "caddy": { "upstreams": "Upstreams", - "requests": "Solicitações atuais", - "requests_failed": "Solicitações com falha" + "requests": "Solicitações actuais", + "requests_failed": "Solicitações falhadas" }, "changedetectionio": { "totalObserved": "Total Observado", - "diffsDetected": "Diferenças Detetadas" + "diffsDetected": "Diferenças Detectadas" }, "channelsdvrserver": { - "shows": "Shows", + "shows": "Séries", "recordings": "Gravações", "scheduled": "Agendado", "passes": "Passes" @@ -170,7 +170,7 @@ "transcoding": "Transcodificação", "bitrate": "Taxa de bits", "no_active": "Sem streams ativas", - "plex_connection_error": "Verifique a conexão Plex" + "plex_connection_error": "Verifique a conexão do Plex" }, "omada": { "connectedAp": "APs Ligados", @@ -182,10 +182,10 @@ "nzbget": { "rate": "Taxa", "remaining": "Restante", - "downloaded": "Baixado" + "downloaded": "Descarregado" }, "plex": { - "streams": "Streams Ativas", + "streams": "Streams Activas", "albums": "Álbuns", "movies": "Filmes", "tv": "Series de TV" @@ -193,10 +193,10 @@ "sabnzbd": { "rate": "Taxa", "queue": "Fila", - "timeleft": "Tempo restante" + "timeleft": "Tempo Restante" }, "rutorrent": { - "active": "Ativo", + "active": "Activo", "upload": "Carregar", "download": "Descarregar" }, @@ -214,8 +214,8 @@ }, "qnap": { "cpuUsage": "Utilização do CPU", - "memUsage": "Utilização de memória", - "systemTempC": "Temperatura do sistema", + "memUsage": "Utilização de Memória", + "systemTempC": "Temperatura do Sistema", "poolUsage": "Uso de Banco", "volumeUsage": "Uso do Volume", "invalid": "Inválido" @@ -227,8 +227,8 @@ "seed": "Semente" }, "develancacheui": { - "cachehitbytes": "Cache Hit Bytes", - "cachemissbytes": "Cache Miss Bytes" + "cachehitbytes": "‘Bytes’ de Acerto na Memória transitória", + "cachemissbytes": "‘Bytes’ de Falha de Memória transitória" }, "downloadstation": { "download": "Descarregar", @@ -237,54 +237,54 @@ "seed": "Semente" }, "sonarr": { - "wanted": "Desejada", - "queued": "Em fila", + "wanted": "Desejados", + "queued": "Em fila de espera", "series": "Séries", "queue": "Fila", "unknown": "Desconhecido" }, "radarr": { - "wanted": "Desejada", - "missing": "Faltando", - "queued": "Em fila", + "wanted": "Desejados", + "missing": "Em falta", + "queued": "Em fila de espera", "movies": "Filmes", "queue": "Fila", "unknown": "Desconhecido" }, "lidarr": { - "wanted": "Desejada", - "queued": "Em fila", + "wanted": "Desejados", + "queued": "Em fila de espera", "artists": "Artistas" }, "readarr": { - "wanted": "Desejada", - "queued": "Em fila", + "wanted": "Desejados", + "queued": "Em fila de espera", "books": "Livros" }, "bazarr": { - "missingEpisodes": "Episódios Faltantes", - "missingMovies": "Filmes Faltantes" + "missingEpisodes": "Episódios em Falta", + "missingMovies": "Filmes em Falta" }, "ombi": { "pending": "Pendente", - "approved": "Aprovada", + "approved": "Aprovado", "available": "Disponível" }, "jellyseerr": { "pending": "Pendente", - "approved": "Aprovada", + "approved": "Aprovado", "available": "Disponível" }, "overseerr": { "pending": "Pendente", - "processing": "Processando", - "approved": "Aprovada", + "processing": "A Processar", + "approved": "Aprovado", "available": "Disponível" }, "netalertx": { "total": "Total", "connected": "Conectado", - "new_devices": "Novos dispositivos", + "new_devices": "Novos Dispositivos", "down_alerts": "Alertas de Falha" }, "pihole": { @@ -309,11 +309,21 @@ "stopped": "Parado", "total": "Total" }, + "suwayomi": { + "download": "Descarregado", + "nondownload": "Non-Downloaded", + "read": "Lido", + "unread": "Não lida", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Endereço", "expires": "Expira", "never": "Nunca", - "last_seen": "Última vez visto", + "last_seen": "Última Vez Visto", "now": "Agora", "years": "{{number}}y", "weeks": "{{number}}w", @@ -325,15 +335,15 @@ }, "technitium": { "totalQueries": "Consultas", - "totalNoError": "Success", - "totalServerFailure": "Failures", - "totalNxDomain": "NX Domains", - "totalRefused": "Refused", - "totalAuthoritative": "Authoritative", - "totalRecursive": "Recursive", - "totalCached": "Cached", + "totalNoError": "Sucesso", + "totalServerFailure": "Falhas", + "totalNxDomain": "Domínios NX", + "totalRefused": "Recusado", + "totalAuthoritative": "Autoritário", + "totalRecursive": "Recursivo", + "totalCached": "Em Memória transitória", "totalBlocked": "Bloqueado", - "totalDropped": "Dropped", + "totalDropped": "Perdidos", "totalClients": "Clientes" }, "tdarr": { @@ -345,19 +355,19 @@ "traefik": { "routers": "Roteadores", "services": "Serviços", - "middleware": "Software Middleware" + "middleware": "Middleware" }, "navidrome": { "nothing_streaming": "Sem streams ativas", - "please_wait": "Por favor aguarde" + "please_wait": "Por Favor, Aguarde" }, "npm": { - "enabled": "Ativo", + "enabled": "Activo", "disabled": "Desabilitado", "total": "Total" }, "coinmarketcap": { - "configure": "Configurar uma ou mais moedas", + "configure": "Configure uma ou mais criptomoedas para rastrear", "1hour": "1 Hora", "1day": "1 Dia", "7days": "7 Dias", @@ -391,8 +401,8 @@ "domain_count": "Domínios" }, "medusa": { - "wanted": "Desejada", - "queued": "Em fila", + "wanted": "Desejados", + "queued": "Em fila de espera", "series": "Séries" }, "minecraft": { @@ -408,8 +418,8 @@ }, "authentik": { "users": "Utilizadores", - "loginsLast24H": "Inícios de sessão (24h)", - "failedLoginsLast24H": "Inícios de sessão falhados (24h)" + "loginsLast24H": "Inícios de Sessão (24h)", + "failedLoginsLast24H": "Inícios de Sessão Falhados (24h)" }, "proxmox": { "mem": "MEM", @@ -440,7 +450,7 @@ "quicklaunch": { "bookmark": "Marcador", "service": "Serviço", - "search": "Busca", + "search": "Pesquisa", "custom": "Personalizado", "visit": "Visitar", "url": "Endereço URL", @@ -449,7 +459,7 @@ "wmo": { "0-day": "Solarengo", "0-night": "Limpo", - "1-day": "Maioritariamente ensolarado", + "1-day": "Maioritariamente Solarengo", "1-night": "Maioritariamente Limpo", "2-day": "Parcialmente Nublado", "2-night": "Parcialmente Nublado", @@ -487,16 +497,16 @@ "75-night": "Neve forte", "77-day": "Grãos de Neve", "77-night": "Grãos de Neve", - "80-day": "Neve fraca", - "80-night": "Neve fraca", + "80-day": "Chuviscos Leves", + "80-night": "Chuviscos Leves", "81-day": "Chuviscos", "81-night": "Chuviscos", "82-day": "Chuviscos fortes", "82-night": "Chuviscos fortes", - "85-day": "Precipitação de Neve", - "85-night": "Precipitação de Neve", - "86-day": "Precipitação de Neve", - "86-night": "Precipitação de Neve", + "85-day": "Chuva de Neve", + "85-night": "Chuva de Neve", + "86-day": "Chuva de Neve", + "86-night": "Chuva de Neve", "95-day": "Trovoada", "95-night": "Trovoada", "96-day": "Trovoada com granizo", @@ -506,10 +516,10 @@ }, "homebridge": { "available_update": "Sistema", - "updates": "Atualizações", + "updates": "Actualizações", "update_available": "Atualização disponível", "up_to_date": "Atualizado", - "child_bridges": "Pontes Filhas", + "child_bridges": "Child Bridges", "child_bridges_status": "{{ok}}/{{total}}", "up": "Up", "pending": "Pendente", @@ -518,12 +528,12 @@ "healthchecks": { "new": "Novo", "up": "Up", - "grace": "Em Período Gratuito", + "grace": "Em Período de Graça", "down": "Down", - "paused": "Pausado", + "paused": "Pausa", "status": "Estado", "last_ping": "Ultimo Ping", - "never": "Nenhum ping ainda" + "never": "Nenhum Ping ainda" }, "watchtower": { "containers_scanned": "Verificado", @@ -531,7 +541,7 @@ "containers_failed": "Falhou" }, "autobrr": { - "approvedPushes": "Aprovada", + "approvedPushes": "Aprovado", "rejectedPushes": "Rejeitado", "filters": "Filtros", "indexers": "Indexadores" @@ -549,7 +559,7 @@ }, "pyload": { "speed": "Velocidade", - "active": "Ativo", + "active": "Activo", "queue": "Fila", "total": "Total" }, @@ -588,8 +598,8 @@ "low_battery": "Bateria Fraca" }, "nextdns": { - "wait": "Por favor aguarde", - "no_devices": "Nenhum dado do dispositivo recebido" + "wait": "Por Favor, Aguarde", + "no_devices": "Nenhum Dado do Dispositivo Recebido" }, "mikrotik": { "cpuLoad": "Carga do CPU", @@ -599,7 +609,7 @@ }, "xteve": { "streams_all": "Todos os Streams", - "streams_active": "Streams Ativas", + "streams_active": "Streams Activas", "streams_xepg": "Canais XEPG" }, "opendtu": { @@ -637,7 +647,7 @@ "up": "Up", "down": "Down", "temp": "Temp", - "disk": "Utilização", + "disk": "Utilização do Disco", "wanIP": "WAN IP" }, "proxmoxbackupserver": { @@ -678,17 +688,17 @@ "mylar": { "series": "Séries", "issues": "Problemas", - "wanted": "Desejada" + "wanted": "Desejados" }, "photoprism": { "albums": "Álbuns", "photos": "Fotos", "videos": "Vídeos", - "people": "Pessoa" + "people": "Pessoas" }, "fileflows": { "queue": "Fila", - "processing": "Processando", + "processing": "A Processar", "processed": "Processado", "time": "Hora" }, @@ -696,13 +706,13 @@ "dashboards": "Painéis", "datasources": "Origem de Dados", "totalalerts": "Total Alertas", - "alertstriggered": "Alertas Disparados" + "alertstriggered": "Alertas Desencadeados" }, "nextcloud": { "cpuload": "Carga de CPU", "memoryusage": "Memória Utilizada", "freespace": "Espaço Livre", - "activeusers": "Utilizadores Ativos", + "activeusers": "Utilizadores Activos", "numfiles": "Ficheiros", "numshares": "Itens partilhados" }, @@ -724,7 +734,7 @@ }, "prometheus": { "targets_up": "Alvo ativo", - "targets_down": "Alvo inativo", + "targets_down": "Alvo Inactivo", "targets_total": "Total de Alvos" }, "gatus": { @@ -735,7 +745,7 @@ "ghostfolio": { "gross_percent_today": "Hoje", "gross_percent_1y": "Um ano", - "gross_percent_max": "Todo o tempo" + "gross_percent_max": "Desde Sempre" }, "audiobookshelf": { "podcasts": "Podcasts", @@ -750,7 +760,7 @@ }, "whatsupdocker": { "monitoring": "A monitorizar", - "updates": "Atualizações" + "updates": "Actualizações" }, "calibreweb": { "books": "Livros", @@ -772,14 +782,14 @@ "result": "Resultado", "status": "Estado", "buildId": "ID da compilação", - "succeeded": "Com êxito", + "succeeded": "Bem sucedido", "notStarted": "Não Iniciado", "failed": "Falhou", "canceled": "Cancelado", "inProgress": "Em progresso", "totalPrs": "Total de PRs", - "myPrs": "Meus PRs", - "approved": "Aprovada" + "myPrs": "Os Meus PRs", + "approved": "Aprovado" }, "gamedig": { "status": "Estado", @@ -787,7 +797,7 @@ "offline": "Desligado", "name": "Nome", "map": "Mapa", - "currentPlayers": "Jogadores atuais", + "currentPlayers": "Jogadores actuais", "players": "Reprodutores", "maxPlayers": "Máximo de Jogadores", "bots": "Bots", @@ -796,7 +806,7 @@ "urbackup": { "ok": "Ok", "errored": "Erros", - "noRecent": "Desatualizado", + "noRecent": "Desactualizado", "totalUsed": "Espaço utilizado" }, "mealie": { @@ -806,7 +816,7 @@ "tags": "Etiquetas" }, "openmediavault": { - "downloading": "A transferir", + "downloading": "A descarregar", "total": "Total", "running": "A correr", "stopped": "Parado", @@ -824,14 +834,14 @@ "uptimerobot": { "status": "Estado", "uptime": "Ligado", - "lastDown": "Última inatividade", - "downDuration": "Duração de inatividade", + "lastDown": "Última Inactividade", + "downDuration": "Duração de Inactividade", "sitesUp": "Sites no Ar", "sitesDown": "Sites Fora do Ar", - "paused": "Pausado", + "paused": "Pausa", "notyetchecked": "Ainda não verificado", "up": "Up", - "seemsdown": "Parece Baixo", + "seemsdown": "Parece em Baixo", "down": "Down", "unknown": "Desconhecido" }, @@ -844,16 +854,16 @@ }, "romm": { "platforms": "Plataformas", - "totalRoms": "Games", + "totalRoms": "Jogos", "saves": "Saves", - "states": "States", + "states": "Estados", "screenshots": "Screenshots", - "totalfilesize": "Total Size" + "totalfilesize": "Tamanho Total" }, "mailcow": { "domains": "Domínios", - "mailboxes": "Mailboxes", - "mails": "Mails", + "mailboxes": "Caixas de Correio", + "mails": "E-mails", "storage": "Armazenamento" }, "netdata": { @@ -881,7 +891,7 @@ "images": "Imagens", "imageSize": "Tamanho das imagens", "galleries": "Galerias", - "performers": "Artistas", + "performers": "Artistas de palco", "studios": "Estúdios", "movies": "Filmes", "tags": "Etiquetas", @@ -906,14 +916,14 @@ }, "wgeasy": { "connected": "Conectado", - "enabled": "Ativo", + "enabled": "Activo", "disabled": "Desabilitado", "total": "Total" }, "swagdashboard": { "proxied": "Com proxy", "auth": "Com Autorização", - "outdated": "Desatualizado", + "outdated": "Desactualizado", "banned": "Banido" }, "myspeed": { @@ -922,36 +932,80 @@ "upload": "Carregar" }, "stocks": { - "stocks": "Ações", - "loading": "Carregando", + "stocks": "Acções", + "loading": "A carregar", "open": "Aberto - Mercado dos EUA", "closed": "Fechado - Mercado dos EUA", - "invalidConfiguration": "Configuração inválida" + "invalidConfiguration": "Configuração Inválida" }, "frigate": { - "cameras": "Câmaras", + "cameras": "Câmeras", "uptime": "Ligado", "version": "Versão" }, "linkwarden": { "links": "Links", - "collections": "Collections", + "collections": "Colecções", "tags": "Etiquetas" }, "zabbix": { - "unclassified": "Not classified", + "unclassified": "Não Classificados", "information": "Informação", - "warning": "Warning", - "average": "Average", - "high": "High", - "disaster": "Disaster" + "warning": "Avisos", + "average": "Média", + "high": "Elevado", + "disaster": "Desastre" }, "lubelogger": { - "vehicle": "Vehicle", - "vehicles": "Vehicles", - "serviceRecords": "Service Records", - "reminders": "Reminders", - "nextReminder": "Next Reminder", - "none": "None" + "vehicle": "Veículo", + "vehicles": "Veículos", + "serviceRecords": "Registros de Serviço", + "reminders": "Lembretes", + "nextReminder": "Próximo Lembrete", + "none": "Nenhum" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Nome", + "address": "Endereço", + "last_seen": "Última Vez Visto", + "status": "Estado", + "online": "Online", + "offline": "Desligado" + }, + "beszel": { + "name": "Nome", + "systems": "Systems", + "up": "Up", + "status": "Estado", + "updated": "Atualizado", + "cpu": "CPU", + "memory": "MEM", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Saudável", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Em falta", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "A carregar" + }, + "gitlab": { + "groups": "Groups", + "issues": "Problemas", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/pt_BR/common.json b/public/locales/pt_BR/common.json index cf977b45..c2a2330f 100644 --- a/public/locales/pt_BR/common.json +++ b/public/locales/pt_BR/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "M", "days": "d", "hours": "h", @@ -309,6 +309,16 @@ "stopped": "Parado", "total": "Total" }, + "suwayomi": { + "download": "Baixado", + "nondownload": "Non-Downloaded", + "read": "Lido", + "unread": "Não lida", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Endereço", "expires": "Expira em", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Nome", + "address": "Endereço", + "last_seen": "Visto por último", + "status": "Status", + "online": "Disponível", + "offline": "Offline" + }, + "beszel": { + "name": "Nome", + "systems": "Systems", + "up": "Ativo", + "status": "Status", + "updated": "Atualizado", + "cpu": "CPU", + "memory": "MEM", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Saudável", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Faltando", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Carregando" + }, + "gitlab": { + "groups": "Groups", + "issues": "Problemas", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/ro/common.json b/public/locales/ro/common.json index 5be3a551..80b6e542 100644 --- a/public/locales/ro/common.json +++ b/public/locales/ro/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mo", "days": "d", "hours": "h", @@ -309,6 +309,16 @@ "stopped": "Oprit", "total": "Total" }, + "suwayomi": { + "download": "Descărcat", + "nondownload": "Non-Downloaded", + "read": "Read", + "unread": "Necitit", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Address", "expires": "Expires", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Name", + "address": "Address", + "last_seen": "Last Seen", + "status": "Stare", + "online": "Online", + "offline": "Offline" + }, + "beszel": { + "name": "Name", + "systems": "Systems", + "up": "Sus", + "status": "Stare", + "updated": "Updated", + "cpu": "Procesor", + "memory": "MEM", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Sănătos", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Missing", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Issues", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/ru/common.json b/public/locales/ru/common.json index d84fbb73..35894d0c 100644 --- a/public/locales/ru/common.json +++ b/public/locales/ru/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "мес", "days": "дней", "hours": "час", @@ -227,8 +227,8 @@ "seed": "Сид" }, "develancacheui": { - "cachehitbytes": "Cache Hit Bytes", - "cachemissbytes": "Cache Miss Bytes" + "cachehitbytes": "Хит байты кэша", + "cachemissbytes": "Мисс байты кэша" }, "downloadstation": { "download": "Скачивание", @@ -284,7 +284,7 @@ "netalertx": { "total": "Всего", "connected": "Подключено", - "new_devices": "Новое устройство", + "new_devices": "Новые устройства", "down_alerts": "Оповещение о недоступности" }, "pihole": { @@ -309,6 +309,16 @@ "stopped": "Остановлено", "total": "Всего" }, + "suwayomi": { + "download": "Загружено", + "nondownload": "Незагруженные", + "read": "Прочитано", + "unread": "Не прочитано", + "downloadedread": "Загруженные и прочитанные", + "downloadedunread": "Загруженные и непрочитанные", + "nondownloadedread": "Незагруженные и прочитанные", + "nondownloadedunread": "Незагруженные и непрочитанные" + }, "tailscale": { "address": "Адрес", "expires": "Истекает", @@ -325,15 +335,15 @@ }, "technitium": { "totalQueries": "Запросы", - "totalNoError": "Success", - "totalServerFailure": "Failures", - "totalNxDomain": "NX Domains", - "totalRefused": "Refused", - "totalAuthoritative": "Authoritative", - "totalRecursive": "Recursive", - "totalCached": "Cached", + "totalNoError": "Успешные", + "totalServerFailure": "Ошибки", + "totalNxDomain": "NX домены", + "totalRefused": "Отказано", + "totalAuthoritative": "Авторитетные", + "totalRecursive": "Рекурсивные", + "totalCached": "Кэш", "totalBlocked": "Заблокировано", - "totalDropped": "Dropped", + "totalDropped": "Отброшенные", "totalClients": "Клиенты" }, "tdarr": { @@ -844,16 +854,16 @@ }, "romm": { "platforms": "Платформы", - "totalRoms": "Games", - "saves": "Saves", - "states": "States", - "screenshots": "Screenshots", - "totalfilesize": "Total Size" + "totalRoms": "Игры", + "saves": "Сейвы", + "states": "Состояния", + "screenshots": "Скриншоты", + "totalfilesize": "Общий объем" }, "mailcow": { "domains": "Домены", - "mailboxes": "Mailboxes", - "mails": "Mails", + "mailboxes": "Почтовые ящики", + "mails": "Письма", "storage": "Хранилище" }, "netdata": { @@ -902,7 +912,7 @@ }, "crowdsec": { "alerts": "Предупреждения", - "bans": "Запреты" + "bans": "Блокировки" }, "wgeasy": { "connected": "Подключено", @@ -911,10 +921,10 @@ "total": "Всего" }, "swagdashboard": { - "proxied": "Proxied", - "auth": "With Auth", - "outdated": "Outdated", - "banned": "Banned" + "proxied": "Прокси", + "auth": "С Авторизацией", + "outdated": "Устаревшие", + "banned": "Заблокированные" }, "myspeed": { "ping": "Пинг", @@ -922,36 +932,80 @@ "upload": "Загрузка" }, "stocks": { - "stocks": "Stocks", - "loading": "Loading", - "open": "Open - US Market", - "closed": "Closed - US Market", - "invalidConfiguration": "Invalid Configuration" + "stocks": "Акции", + "loading": "Загрузка", + "open": "Открыто - Рынок США", + "closed": "Закрыто - рынок США", + "invalidConfiguration": "Неверная конфигурация" }, "frigate": { - "cameras": "Cameras", + "cameras": "Камеры", "uptime": "Время работы", "version": "Версия" }, "linkwarden": { - "links": "Links", - "collections": "Collections", + "links": "Ссылки", + "collections": "Коллекции", "tags": "Теги" }, "zabbix": { - "unclassified": "Not classified", + "unclassified": "Не классифицировано", "information": "Информация", - "warning": "Warning", - "average": "Average", - "high": "High", - "disaster": "Disaster" + "warning": "Предупреждение", + "average": "Средняя", + "high": "Высокая", + "disaster": "Чрезвычайная" }, "lubelogger": { - "vehicle": "Vehicle", - "vehicles": "Vehicles", - "serviceRecords": "Service Records", - "reminders": "Reminders", - "nextReminder": "Next Reminder", - "none": "None" + "vehicle": "Автомобиль", + "vehicles": "Автомобили", + "serviceRecords": "Сервисные работы", + "reminders": "Напоминания", + "nextReminder": "Следующее напоминание", + "none": "Нет" + }, + "vikunja": { + "projects": "Активные Проекты", + "tasks7d": "Задачи на этой неделе", + "tasksOverdue": "Просроченные задачи", + "tasksInProgress": "Задачи в процессе" + }, + "headscale": { + "name": "Имя", + "address": "Адрес", + "last_seen": "Последнее посещение", + "status": "Статус", + "online": "В сети", + "offline": "Не в сети" + }, + "beszel": { + "name": "Имя", + "systems": "Системы", + "up": "Онлайн", + "status": "Статус", + "updated": "Обновленно", + "cpu": "ЦП", + "memory": "ОЗУ", + "disk": "Диск", + "network": "Сеть" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Здоровый", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Отсутствует", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Загрузка" + }, + "gitlab": { + "groups": "Groups", + "issues": "Вопросы", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/sk/common.json b/public/locales/sk/common.json index 4975958e..e93dd657 100644 --- a/public/locales/sk/common.json +++ b/public/locales/sk/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mes", "days": "d", "hours": "h", @@ -309,6 +309,16 @@ "stopped": "Zastavené", "total": "Celkovo" }, + "suwayomi": { + "download": "Stiahnuté", + "nondownload": "Non-Downloaded", + "read": "Prečítané", + "unread": "Neprečítané", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Adresa", "expires": "Vyprší", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Meno", + "address": "Adresa", + "last_seen": "Naposledy videné", + "status": "Stav", + "online": "Online", + "offline": "Nedostupný" + }, + "beszel": { + "name": "Meno", + "systems": "Systems", + "up": "Nahrávanie", + "status": "Stav", + "updated": "Aktualizované", + "cpu": "CPU", + "memory": "RAM", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Zdravý", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Chýbajúce", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Problémy", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/sl/common.json b/public/locales/sl/common.json index 312f9609..17298acb 100644 --- a/public/locales/sl/common.json +++ b/public/locales/sl/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mes", "days": "d", "hours": "u", @@ -309,6 +309,16 @@ "stopped": "Ustavljen", "total": "Skupaj" }, + "suwayomi": { + "download": "Preneseno", + "nondownload": "Nepreneseno", + "read": "Prebrano", + "unread": "Neprebrano", + "downloadedread": "Preneseno in prebrano", + "downloadedunread": "Preneseno in neprebrano", + "nondownloadedread": "Nepreneseno in prebrano", + "nondownloadedunread": "Nepreneseno in neprebrano" + }, "tailscale": { "address": "Naslov", "expires": "Poteče", @@ -953,5 +963,49 @@ "reminders": "Opomniki", "nextReminder": "Naslednji opomnik", "none": "Brez" + }, + "vikunja": { + "projects": "Aktivni projekti", + "tasks7d": "Potekla opravila tega tedna", + "tasksOverdue": "Potekla opravila", + "tasksInProgress": "Tekoča opravila" + }, + "headscale": { + "name": "Naziv", + "address": "Naslov", + "last_seen": "Viden", + "status": "Stanje", + "online": "Na spletu", + "offline": "Ni povezan" + }, + "beszel": { + "name": "Naziv", + "systems": "Sistemi", + "up": "Povezan", + "status": "Stanje", + "updated": "Posodobljen", + "cpu": "CPU", + "memory": "MEM", + "disk": "Disk", + "network": "Mreža" + }, + "argocd": { + "apps": "Aplikacije", + "synced": "Sinhro", + "outOfSync": "Ni sinhro", + "healthy": "Zdrav", + "degraded": "Degragirano", + "progressing": "V teku", + "missing": "Manjka", + "suspended": "Prekinjeno" + }, + "spoolman": { + "loading": "Nalaganje" + }, + "gitlab": { + "groups": "Groups", + "issues": "Težave", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/sr/common.json b/public/locales/sr/common.json index 4cda2296..135d5040 100644 --- a/public/locales/sr/common.json +++ b/public/locales/sr/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mo", "days": "d", "hours": "h", @@ -309,6 +309,16 @@ "stopped": "Stopped", "total": "Total" }, + "suwayomi": { + "download": "Downloaded", + "nondownload": "Non-Downloaded", + "read": "Read", + "unread": "Unread", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Address", "expires": "Expires", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Name", + "address": "Address", + "last_seen": "Last Seen", + "status": "Status", + "online": "Online", + "offline": "Offline" + }, + "beszel": { + "name": "Name", + "systems": "Systems", + "up": "Up", + "status": "Status", + "updated": "Updated", + "cpu": "CPU", + "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" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Issues", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/sv/common.json b/public/locales/sv/common.json index 5cecb7fe..ea10eadb 100644 --- a/public/locales/sv/common.json +++ b/public/locales/sv/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mån", "days": "d", "hours": "h", @@ -309,6 +309,16 @@ "stopped": "Stoppade", "total": "Total" }, + "suwayomi": { + "download": "Nedladdat", + "nondownload": "Non-Downloaded", + "read": "Read", + "unread": "Unread", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Address", "expires": "Expires", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Name", + "address": "Address", + "last_seen": "Last Seen", + "status": "Status", + "online": "Online", + "offline": "Offline" + }, + "beszel": { + "name": "Name", + "systems": "Systems", + "up": "Up", + "status": "Status", + "updated": "Updated", + "cpu": "CPU", + "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" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Issues", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/te/common.json b/public/locales/te/common.json index d922e8f8..f480451b 100644 --- a/public/locales/te/common.json +++ b/public/locales/te/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mo", "days": "d", "hours": "h", @@ -309,6 +309,16 @@ "stopped": "ఆగిపోయినవి", "total": "మొత్తం" }, + "suwayomi": { + "download": "డౌన్‌లోడ్ చేయబడింది", + "nondownload": "Non-Downloaded", + "read": "Read", + "unread": "Unread", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Address", "expires": "Expires", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Name", + "address": "Address", + "last_seen": "Last Seen", + "status": "హోదా", + "online": "Online", + "offline": "ఆఫ్‌లైన్" + }, + "beszel": { + "name": "Name", + "systems": "Systems", + "up": "Up", + "status": "హోదా", + "updated": "నవీకరించబడింది", + "cpu": "సీపియూ", + "memory": "MEM", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Healthy", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "మిస్సింగ్", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Issues", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/th/common.json b/public/locales/th/common.json index 6a73d2de..67c5804b 100644 --- a/public/locales/th/common.json +++ b/public/locales/th/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mo", "days": "d", "hours": "h", @@ -309,6 +309,16 @@ "stopped": "Stopped", "total": "ทั้งหมด" }, + "suwayomi": { + "download": "Downloaded", + "nondownload": "Non-Downloaded", + "read": "Read", + "unread": "Unread", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Address", "expires": "Expires", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Name", + "address": "Address", + "last_seen": "Last Seen", + "status": "สถานะ", + "online": "Online", + "offline": "ออฟไลน์" + }, + "beszel": { + "name": "Name", + "systems": "Systems", + "up": "Up", + "status": "สถานะ", + "updated": "Updated", + "cpu": "ซีพียู", + "memory": "เมม", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Healthy", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Missing", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Issues", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/tr/common.json b/public/locales/tr/common.json index 6c447f81..e0b25666 100644 --- a/public/locales/tr/common.json +++ b/public/locales/tr/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "ay", "days": "g", "hours": "sa", @@ -309,6 +309,16 @@ "stopped": "Durduruldu", "total": "Toplam" }, + "suwayomi": { + "download": "İndirilen", + "nondownload": "Non-Downloaded", + "read": "Okunan", + "unread": "Okunmamış", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Adres", "expires": "Geciken", @@ -853,7 +863,7 @@ "mailcow": { "domains": "Etki Alanları", "mailboxes": "Mailboxes", - "mails": "Mails", + "mails": "Postalar", "storage": "Depo" }, "netdata": { @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Bitişi Bu Hafta Olan Görevler", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "İsim", + "address": "Adres", + "last_seen": "Son Görülme", + "status": "Durum", + "online": "Çevrimiçi", + "offline": "Çevrimdışı" + }, + "beszel": { + "name": "İsim", + "systems": "Systems", + "up": "Yükleme", + "status": "Durum", + "updated": "Güncellendi", + "cpu": "CPU", + "memory": "MEM", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Sağlıklı", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Eksik", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Yükleniyor" + }, + "gitlab": { + "groups": "Groups", + "issues": "Sorunlar", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/uk/common.json b/public/locales/uk/common.json index 6f3d45d5..6297e570 100644 --- a/public/locales/uk/common.json +++ b/public/locales/uk/common.json @@ -13,10 +13,10 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "міс", - "days": "д", - "hours": "г", + "days": "днів", + "hours": "год", "minutes": "хв", "seconds": "с" }, @@ -47,7 +47,7 @@ "load": "Завантаження", "temp": "Температура", "max": "Макс.", - "uptime": "Відправка" + "uptime": "Онлайн" }, "unifi": { "users": "Користувачі", @@ -61,7 +61,7 @@ "wlan_devices": "WLAN пристрої", "lan_users": "LAN користувачі", "wlan_users": "WLAN користувачі", - "up": "Відправка", + "up": "Онлайн", "down": "Завантаження", "wait": "Будь ласка, зачекайте", "empty_data": "Статус підсистеми невідомий" @@ -75,7 +75,7 @@ "offline": "Офлайн", "error": "Помилка", "unknown": "Невідомий", - "healthy": "Здоров'я", + "healthy": "Здоровий", "starting": "Запуск", "unhealthy": "Нездоровий", "not_found": "Не знайдено", @@ -99,7 +99,7 @@ }, "emby": { "playing": "Відтворення", - "transcoding": "Перекодування", + "transcoding": "Транскодування", "bitrate": "Бітрейт", "no_active": "Немає активних потоків", "movies": "Фільми", @@ -123,8 +123,8 @@ "watt_hour": "Вт/год" }, "flood": { - "download": "Завантаження", - "upload": "Відправлення", + "download": "Завантажено", + "upload": "Відправлено", "leech": "Ліч", "seed": "Сід" }, @@ -142,13 +142,13 @@ "connectionStatusDisconnected": "Відключено", "connectionStatusConnected": "З'єднано", "uptime": "Час роботи", - "maxDown": "Макс. зав", + "maxDown": "Макс. завантаження", "maxUp": "Макс. віддача", "down": "Офлайн", "up": "Онлайн", "received": "Отримано", - "sent": "Надісл.", - "externalIPAddress": "Зовн. IP" + "sent": "Надіслано", + "externalIPAddress": "Зовнішній IP" }, "caddy": { "upstreams": "Потоки", @@ -163,11 +163,11 @@ "shows": "Вистави", "recordings": "Записи", "scheduled": "Заплановано", - "passes": "Перепустки" + "passes": "Пропуски" }, "tautulli": { "playing": "Відтворення", - "transcoding": "Перекодування", + "transcoding": "Транскодування", "bitrate": "Бітрейт", "no_active": "Немає активних потоків", "plex_connection_error": "Перевірте з'єднання Plex" @@ -197,18 +197,18 @@ }, "rutorrent": { "active": "Активний", - "upload": "Відправлення", - "download": "Завантаження" + "upload": "Відправлено", + "download": "Завантажено" }, "transmission": { - "download": "Завантаження", - "upload": "Відправлення", + "download": "Завантажено", + "upload": "Відправлено", "leech": "Ліч", "seed": "Сід" }, "qbittorrent": { - "download": "Завантаження", - "upload": "Відправлення", + "download": "Завантажено", + "upload": "Відправлено", "leech": "Ліч", "seed": "Сід" }, @@ -221,18 +221,18 @@ "invalid": "Недійсний" }, "deluge": { - "download": "Завантаження", - "upload": "Відправлення", + "download": "Завантажено", + "upload": "Відправлено", "leech": "Ліч", "seed": "Сід" }, "develancacheui": { - "cachehitbytes": "Cache Hit Bytes", - "cachemissbytes": "Cache Miss Bytes" + "cachehitbytes": "Кеш-хіт байт", + "cachemissbytes": "Кеш-міс байт" }, "downloadstation": { - "download": "Завантаження", - "upload": "Відправлення", + "download": "Завантажено", + "upload": "Відправлено", "leech": "Ліч", "seed": "Сід" }, @@ -300,8 +300,8 @@ "latency": "Затримка" }, "speedtest": { - "upload": "Відправлення", - "download": "Завантаження", + "upload": "Відправлено", + "download": "Завантажено", "ping": "Пінг" }, "portainer": { @@ -309,6 +309,16 @@ "stopped": "Зупинено", "total": "Усього" }, + "suwayomi": { + "download": "Завантажено", + "nondownload": "Non-Downloaded", + "read": "Прочитано", + "unread": "Не прочитано", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Адреса", "expires": "Дійсний до", @@ -325,15 +335,15 @@ }, "technitium": { "totalQueries": "Запити", - "totalNoError": "Success", - "totalServerFailure": "Failures", - "totalNxDomain": "NX Domains", - "totalRefused": "Refused", - "totalAuthoritative": "Authoritative", - "totalRecursive": "Recursive", - "totalCached": "Cached", + "totalNoError": "Успішно", + "totalServerFailure": "Помилки", + "totalNxDomain": "NX Домени", + "totalRefused": "Відмовлено", + "totalAuthoritative": "Авторитетні", + "totalRecursive": "Рекурсивні", + "totalCached": "Кешовані", "totalBlocked": "Заблоковано", - "totalDropped": "Dropped", + "totalDropped": "Видалені", "totalClients": "Клієнти" }, "tdarr": { @@ -424,12 +434,12 @@ "temp": "Температура", "_temp": "Темп.", "warn": "Увага", - "uptime": "Відправка", + "uptime": "Онлайн", "total": "Усього", "free": "Вільно", "used": "Використано", - "days": "д", - "hours": "г", + "days": "днів", + "hours": "год", "crit": "Крит", "read": "Прочитано", "write": "Написати", @@ -815,77 +825,77 @@ }, "openwrt": { "uptime": "Час роботи", - "cpuLoad": "CPU Load Avg (5m)", + "cpuLoad": "Сер. навантаження ЦП (5 хв)", "up": "Онлайн", "down": "Офлайн", - "bytesTx": "Transmitted", + "bytesTx": "Передано", "bytesRx": "Отримано" }, "uptimerobot": { "status": "Стан", "uptime": "Час роботи", - "lastDown": "Last Downtime", - "downDuration": "Downtime Duration", + "lastDown": "Останній час простою", + "downDuration": "Тривалість простою", "sitesUp": "Активні сайти", "sitesDown": "Неактивні сайти", "paused": "Призупинено", - "notyetchecked": "Not Yet Checked", + "notyetchecked": "Ще не перевірено", "up": "Онлайн", - "seemsdown": "Seems Down", + "seemsdown": "Вірогідно в простої", "down": "Офлайн", "unknown": "Невідомий" }, "calendar": { - "inCinemas": "In cinemas", - "physicalRelease": "Physical release", - "digitalRelease": "Digital release", - "noEventsToday": "No events for today!", - "noEventsFound": "No events found" + "inCinemas": "У кінотеатрах", + "physicalRelease": "Фізичний реліз", + "digitalRelease": "Цифровий реліз", + "noEventsToday": "Події на сьогодні відсутні", + "noEventsFound": "Події не знайдено" }, "romm": { - "platforms": "Platforms", - "totalRoms": "Games", - "saves": "Saves", - "states": "States", - "screenshots": "Screenshots", - "totalfilesize": "Total Size" + "platforms": "Платформи", + "totalRoms": "Ігри", + "saves": "Збереження", + "states": "Штати", + "screenshots": "Знімки екрану", + "totalfilesize": "Загальний обсяг" }, "mailcow": { "domains": "Домени", - "mailboxes": "Mailboxes", - "mails": "Mails", + "mailboxes": "Пошта", + "mails": "Листи", "storage": "Сховище" }, "netdata": { "warnings": "Попередження", - "criticals": "Criticals" + "criticals": "Критичні" }, "plantit": { - "events": "Events", - "plants": "Plants", + "events": "Події", + "plants": "Рослини", "photos": "Фотографії", - "species": "Species" + "species": "Види" }, "gitea": { "notifications": "Сповіщення", "issues": "Питання", - "pulls": "Pull Requests" + "pulls": "Pull-запити" }, "stash": { - "scenes": "Scenes", - "scenesPlayed": "Scenes Played", - "playCount": "Total Plays", - "playDuration": "Time Watched", - "sceneSize": "Scenes Size", - "sceneDuration": "Scenes Duration", - "images": "Images", - "imageSize": "Images Size", - "galleries": "Galleries", + "scenes": "Сцени", + "scenesPlayed": "Зіграні сцени", + "playCount": "Всього п'єс", + "playDuration": "Переглянуто", + "sceneSize": "Розміри сцен", + "sceneDuration": "Тривалість сцен", + "images": "Зображення", + "imageSize": "Розміри зображень", + "galleries": "Галереї", "performers": "Виконавці", - "studios": "Studios", + "studios": "Студії", "movies": "Фільми", "tags": "Теги", - "oCount": "O Count" + "oCount": "Кількість O" }, "tandoor": { "users": "Користувачі", @@ -893,16 +903,16 @@ "keywords": "Ключові слова" }, "homebox": { - "items": "Items", - "totalWithWarranty": "With Warranty", + "items": "Речі", + "totalWithWarranty": "З гарантією", "locations": "Місцезнаходження", "labels": "Мітки", "users": "Користувачі", - "totalValue": "Total Value" + "totalValue": "Загальне значення" }, "crowdsec": { "alerts": "Оповіщення", - "bans": "Bans" + "bans": "Блокування" }, "wgeasy": { "connected": "З'єднано", @@ -911,21 +921,21 @@ "total": "Усього" }, "swagdashboard": { - "proxied": "Proxied", - "auth": "With Auth", - "outdated": "Outdated", - "banned": "Banned" + "proxied": "Пропущено через проксі", + "auth": "З аутентифікацією", + "outdated": "Застаріле", + "banned": "Заблоковано" }, "myspeed": { "ping": "Пінг", - "download": "Завантаження", - "upload": "Відправлення" + "download": "Завантажено", + "upload": "Відправлено" }, "stocks": { - "stocks": "Stocks", + "stocks": "Акції", "loading": "Завантажую", - "open": "Open - US Market", - "closed": "Closed - US Market", + "open": "Відкрито - ринок США", + "closed": "Закрито - ринок США", "invalidConfiguration": "Неприпустима конфігурація" }, "frigate": { @@ -934,24 +944,68 @@ "version": "Версія" }, "linkwarden": { - "links": "Links", - "collections": "Collections", + "links": "Посилання", + "collections": "Колекції", "tags": "Теги" }, "zabbix": { - "unclassified": "Not classified", + "unclassified": "Не визначено", "information": "Інформація", - "warning": "Warning", - "average": "Average", - "high": "High", - "disaster": "Disaster" + "warning": "Попередження", + "average": "Середнє", + "high": "Високе", + "disaster": "Катастрофа" }, "lubelogger": { - "vehicle": "Vehicle", - "vehicles": "Vehicles", - "serviceRecords": "Service Records", - "reminders": "Reminders", - "nextReminder": "Next Reminder", - "none": "None" + "vehicle": "Транспортний засіб", + "vehicles": "Транспортні засоби", + "serviceRecords": "Записи служб", + "reminders": "Нагадування", + "nextReminder": "Наступне нагадування", + "none": "Жодного" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Назва", + "address": "Адреса", + "last_seen": "Востаннє у мережі", + "status": "Стан", + "online": "Онлайн", + "offline": "Офлайн" + }, + "beszel": { + "name": "Назва", + "systems": "Systems", + "up": "Онлайн", + "status": "Стан", + "updated": "Оновлено", + "cpu": "ЦП", + "memory": "ОЗП", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "Здоровий", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "Відсутній", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Завантажую" + }, + "gitlab": { + "groups": "Groups", + "issues": "Питання", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/vi/common.json b/public/locales/vi/common.json index d81903c4..8abcdad5 100644 --- a/public/locales/vi/common.json +++ b/public/locales/vi/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "mo", "days": "d", "hours": "h", @@ -309,6 +309,16 @@ "stopped": "Stopped", "total": "Tổng" }, + "suwayomi": { + "download": "Đã tải", + "nondownload": "Non-Downloaded", + "read": "Read", + "unread": "Unread", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "Address", "expires": "Expires", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "Name", + "address": "Address", + "last_seen": "Last Seen", + "status": "Trạng thái", + "online": "Online", + "offline": "Ngoại tuyến" + }, + "beszel": { + "name": "Name", + "systems": "Systems", + "up": "Up", + "status": "Trạng thái", + "updated": "Updated", + "cpu": "CPU", + "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" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "Issues", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/yue/common.json b/public/locales/yue/common.json index 2578eb8f..f10269f6 100644 --- a/public/locales/yue/common.json +++ b/public/locales/yue/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "月", "days": "天", "hours": "小時", @@ -309,6 +309,16 @@ "stopped": "暫停", "total": "全部" }, + "suwayomi": { + "download": "下載咗", + "nondownload": "Non-Downloaded", + "read": "已讀", + "unread": "未讀", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "位址", "expires": "已失效", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "名稱", + "address": "位址", + "last_seen": "上次連線", + "status": "狀況", + "online": "在線", + "offline": "離線" + }, + "beszel": { + "name": "名稱", + "systems": "Systems", + "up": "在線", + "status": "狀況", + "updated": "已更新", + "cpu": "CPU", + "memory": "記憶體", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "健康", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "缺少", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "出版", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/zh-Hans/common.json b/public/locales/zh-Hans/common.json index 6665ed6a..f0d445c2 100644 --- a/public/locales/zh-Hans/common.json +++ b/public/locales/zh-Hans/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "月", "days": "日", "hours": "时", @@ -61,7 +61,7 @@ "wlan_devices": "无线局域网设备", "lan_users": "局域网用户", "wlan_users": "无线局域网用户", - "up": "在线", + "up": "运行时间", "down": "离线", "wait": "请稍候", "empty_data": "子系统状态未知" @@ -227,8 +227,8 @@ "seed": "做种" }, "develancacheui": { - "cachehitbytes": "Cache Hit Bytes", - "cachemissbytes": "Cache Miss Bytes" + "cachehitbytes": "缓存命中字节", + "cachemissbytes": "缓存Bytes失败" }, "downloadstation": { "download": "下载", @@ -309,6 +309,16 @@ "stopped": "停止", "total": "总计" }, + "suwayomi": { + "download": "下载", + "nondownload": "Non-Downloaded", + "read": "已读", + "unread": "未读", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "地址", "expires": "失效", @@ -327,7 +337,7 @@ "totalQueries": "查询", "totalNoError": "成功", "totalServerFailure": "失败", - "totalNxDomain": "NX Domains", + "totalNxDomain": "域", "totalRefused": "已拒绝", "totalAuthoritative": "权威", "totalRecursive": "递归", @@ -724,7 +734,7 @@ }, "prometheus": { "targets_up": "目标上线", - "targets_down": "离线目标", + "targets_down": "目标离线", "targets_total": "总目标" }, "gatus": { @@ -844,16 +854,16 @@ }, "romm": { "platforms": "平台", - "totalRoms": "Games", - "saves": "Saves", - "states": "States", - "screenshots": "Screenshots", - "totalfilesize": "Total Size" + "totalRoms": "游戏数", + "saves": "已保存", + "states": "状态", + "screenshots": "屏幕截图", + "totalfilesize": "总大小" }, "mailcow": { "domains": "域", - "mailboxes": "Mailboxes", - "mails": "Mails", + "mailboxes": "邮箱", + "mails": "邮件", "storage": "储存空间" }, "netdata": { @@ -922,36 +932,80 @@ "upload": "上传速率" }, "stocks": { - "stocks": "Stocks", - "loading": "Loading", - "open": "Open - US Market", - "closed": "Closed - US Market", - "invalidConfiguration": "Invalid Configuration" + "stocks": "库存", + "loading": "正在加载", + "open": "打開-美国商店", + "closed": "关闭-美国市场", + "invalidConfiguration": "无效配置" }, "frigate": { - "cameras": "Cameras", + "cameras": "摄像头", "uptime": "运行时间", "version": "版本" }, "linkwarden": { - "links": "Links", - "collections": "Collections", + "links": "链接", + "collections": "收藏", "tags": "Tags" }, "zabbix": { - "unclassified": "Not classified", + "unclassified": "未分类", "information": "信息", - "warning": "Warning", - "average": "Average", - "high": "High", - "disaster": "Disaster" + "warning": "警告", + "average": "平均红包", + "high": "高", + "disaster": "灾难" }, "lubelogger": { - "vehicle": "Vehicle", - "vehicles": "Vehicles", - "serviceRecords": "Service Records", - "reminders": "Reminders", - "nextReminder": "Next Reminder", - "none": "None" + "vehicle": "载具", + "vehicles": "交通工具", + "serviceRecords": "保养记录", + "reminders": "提示", + "nextReminder": "下次提醒", + "none": "空" + }, + "vikunja": { + "projects": "积极的项目", + "tasks7d": "本周到期的任务", + "tasksOverdue": "过期的任务", + "tasksInProgress": "正在处理的任务" + }, + "headscale": { + "name": "Name", + "address": "地址", + "last_seen": "最后上线", + "status": "状态", + "online": "在线的", + "offline": "离线" + }, + "beszel": { + "name": "Name", + "systems": "Systems", + "up": "Up", + "status": "状态", + "updated": "已升级", + "cpu": "CPU", + "memory": "内存", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "应用程序", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "健康", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "丢失", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "正在加载" + }, + "gitlab": { + "groups": "Groups", + "issues": "问题", + "merges": "Merge Requests", + "projects": "Projects" } } diff --git a/public/locales/zh-Hant/common.json b/public/locales/zh-Hant/common.json index 4eb68d91..1a497aab 100644 --- a/public/locales/zh-Hant/common.json +++ b/public/locales/zh-Hant/common.json @@ -13,7 +13,7 @@ "ms": "{{value, number}}", "date": "{{value, date}}", "relativeDate": "{{value, relativeDate}}", - "uptime": "{{value, uptime}}", + "duration": "{{value, duration}}", "months": "月", "days": "天", "hours": "小時", @@ -309,6 +309,16 @@ "stopped": "已停止", "total": "全部" }, + "suwayomi": { + "download": "已下載", + "nondownload": "Non-Downloaded", + "read": "已讀", + "unread": "未讀", + "downloadedread": "Downloaded & Read", + "downloadedunread": "Downloaded & Unread", + "nondownloadedread": "Non-Downloaded & Read", + "nondownloadedunread": "Non-Downloaded & Unread" + }, "tailscale": { "address": "位址", "expires": "已失效", @@ -953,5 +963,49 @@ "reminders": "Reminders", "nextReminder": "Next Reminder", "none": "None" + }, + "vikunja": { + "projects": "Active Projects", + "tasks7d": "Tasks Due This Week", + "tasksOverdue": "Overdue Tasks", + "tasksInProgress": "Tasks In Progress" + }, + "headscale": { + "name": "名稱", + "address": "位址", + "last_seen": "上次連線", + "status": "狀態", + "online": "在線", + "offline": "離線" + }, + "beszel": { + "name": "名稱", + "systems": "Systems", + "up": "在線", + "status": "狀態", + "updated": "已更新", + "cpu": "CPU", + "memory": "記憶體", + "disk": "Disk", + "network": "NET" + }, + "argocd": { + "apps": "Apps", + "synced": "Synced", + "outOfSync": "Out Of Sync", + "healthy": "健康", + "degraded": "Degraded", + "progressing": "Progressing", + "missing": "缺少", + "suspended": "Suspended" + }, + "spoolman": { + "loading": "Loading" + }, + "gitlab": { + "groups": "Groups", + "issues": "出版", + "merges": "Merge Requests", + "projects": "Projects" } } From 897309a47cb94ff9d2d45eb875e7a09ff291b5dc Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sun, 24 Nov 2024 22:43:13 -0800 Subject: [PATCH 012/100] Enhancement: resources network widget (#4327) --- docs/installation/k8s.md | 3 +- docs/widgets/info/resources.md | 3 +- src/components/widgets/resources/network.jsx | 47 +++++++++++++++++++ .../widgets/resources/resources.jsx | 2 + src/components/widgets/widget/resource.jsx | 7 ++- src/pages/api/widgets/resources.js | 28 ++++++++++- 6 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 src/components/widgets/resources/network.jsx diff --git a/docs/installation/k8s.md b/docs/installation/k8s.md index 6805139b..24be2c34 100644 --- a/docs/installation/k8s.md +++ b/docs/installation/k8s.md @@ -175,6 +175,7 @@ data: expanded: true cpu: true memory: true + network: default - search: provider: duckduckgo target: _blank @@ -370,7 +371,7 @@ prevent unnecessary re-renders on page loads and window / tab focusing. The procedure for enabling sticky sessions depends on your Ingress controller. Below is an example using Traefik as the Ingress controller. -``` +```yaml apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: diff --git a/docs/widgets/info/resources.md b/docs/widgets/info/resources.md index 19323dc3..7fcf9c5c 100644 --- a/docs/widgets/info/resources.md +++ b/docs/widgets/info/resources.md @@ -24,9 +24,10 @@ _Note: unfortunately, the package used for getting CPU temp ([systeminformation] tempmin: 0 # optional, minimum cpu temp tempmax: 100 # optional, maximum cpu temp uptime: true - units: imperial # only used by cpu temp + units: imperial # only used by cpu temp, options: 'imperial' or 'metric' refresh: 3000 # optional, in ms diskUnits: bytes # optional, bytes (default) or bbytes. Only applies to disk + network: true # optional, uses 'default' if true or specify a network interface name ``` You can also pass a `label` option, which allows you to group resources under named sections, diff --git a/src/components/widgets/resources/network.jsx b/src/components/widgets/resources/network.jsx new file mode 100644 index 00000000..5b5cc004 --- /dev/null +++ b/src/components/widgets/resources/network.jsx @@ -0,0 +1,47 @@ +import useSWR from "swr"; +import { FaNetworkWired } from "react-icons/fa"; +import { useTranslation } from "next-i18next"; + +import Resource from "../widget/resource"; +import Error from "../widget/error"; + +export default function Network({ options, refresh = 1500 }) { + const { t } = useTranslation(); + // eslint-disable-next-line no-param-reassign + if (options.network === true) options.network = "default"; + + const { data, error } = useSWR(`/api/widgets/resources?type=network&interfaceName=${options.network}`, { + refreshInterval: refresh, + }); + + if (error || data?.error) { + return ; + } + + if (!data || !data.network) { + return ( + + ); + } + + return ( + + ); +} diff --git a/src/components/widgets/resources/resources.jsx b/src/components/widgets/resources/resources.jsx index 634e0ff5..db26caa7 100644 --- a/src/components/widgets/resources/resources.jsx +++ b/src/components/widgets/resources/resources.jsx @@ -6,6 +6,7 @@ import Cpu from "./cpu"; import Memory from "./memory"; import CpuTemp from "./cputemp"; import Uptime from "./uptime"; +import Network from "./network"; export default function Resources({ options }) { const { expanded, units, diskUnits, tempmin, tempmax } = options; @@ -23,6 +24,7 @@ export default function Resources({ options }) { )) : options.disk && } + {options.network && } {options.cputemp && ( )} diff --git a/src/components/widgets/widget/resource.jsx b/src/components/widgets/widget/resource.jsx index 8c975928..b1f73740 100644 --- a/src/components/widgets/widget/resource.jsx +++ b/src/components/widgets/widget/resource.jsx @@ -10,6 +10,7 @@ export default function Resource({ percentage, expanded = false, additionalClassNames = "", + wide = false, }) { const Icon = icon; @@ -18,7 +19,11 @@ export default function Resource({ className={`flex-none flex flex-row items-center mr-3 py-1.5 information-widget-resource ${additionalClassNames}`} > -
+
{value}
{label}
diff --git a/src/pages/api/widgets/resources.js b/src/pages/api/widgets/resources.js index 66449bff..4df544e8 100644 --- a/src/pages/api/widgets/resources.js +++ b/src/pages/api/widgets/resources.js @@ -7,7 +7,7 @@ const logger = createLogger("resources"); const si = require("systeminformation"); export default async function handler(req, res) { - const { type, target } = req.query; + const { type, target, interfaceName = "default" } = req.query; if (type === "cpu") { const load = await si.currentLoad(); @@ -57,6 +57,32 @@ export default async function handler(req, res) { }); } + if (type === "network") { + let networkData = await si.networkStats(); + let interfaceDefault; + logger.debug("networkData:", JSON.stringify(networkData)); + if (interfaceName && interfaceName !== "default") { + networkData = networkData.filter((network) => network.iface === interfaceName).at(0); + if (!networkData) { + return res.status(404).json({ + error: "Interface not found", + }); + } + } else { + interfaceDefault = await si.networkInterfaceDefault(); + networkData = networkData.filter((network) => network.iface === interfaceDefault).at(0); + if (!networkData) { + return res.status(404).json({ + error: "Default interface not found", + }); + } + } + return res.status(200).json({ + network: networkData, + interface: interfaceName !== "default" ? interfaceName : interfaceDefault, + }); + } + return res.status(400).json({ error: "invalid type", }); From cbf304a4c81df5bbec1164bae858a3d684867bc8 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sun, 24 Nov 2024 22:43:17 -0800 Subject: [PATCH 013/100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4f898387..58820942 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Homepage has built-in support for Docker, and can automatically discover and add ## Service Widgets -Homepage also has support for over 100 3rd party services, including all popular starr apps, and most popular self-hosted apps. Some examples include: Radarr, Sonarr, Lidarr, Bazarr, Ombi, Tautulli, Plex, Jellyfin, Emby, Transmission, qBittorrent, Deluge, Jackett, NZBGet, SABnzbd, etc. As well as service integrations, Homepage also has a number of information providers, sourcing information from a variety of external 3rd party APIs. See the [Service](https://gethomepage.dev/widgets/) page for more information. +Homepage also has support for hundreds of 3rd-party services, including all popular \*arr apps, and most popular self-hosted apps. Some examples include: Radarr, Sonarr, Lidarr, Bazarr, Ombi, Tautulli, Plex, Jellyfin, Emby, Transmission, qBittorrent, Deluge, Jackett, NZBGet, SABnzbd, etc. As well as service integrations, Homepage also has a number of information providers, sourcing information from a variety of external 3rd-party APIs. See the [Service](https://gethomepage.dev/widgets/) page for more information. ## Information Widgets From 385511f773daea10bd27c5673231582131f00b86 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sun, 24 Nov 2024 23:11:07 -0800 Subject: [PATCH 014/100] Fix: resources network better startup behavior --- src/components/widgets/resources/network.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/widgets/resources/network.jsx b/src/components/widgets/resources/network.jsx index 5b5cc004..a2a3acac 100644 --- a/src/components/widgets/resources/network.jsx +++ b/src/components/widgets/resources/network.jsx @@ -18,7 +18,7 @@ export default function Network({ options, refresh = 1500 }) { return ; } - if (!data || !data.network) { + if (!data || !data.network || !data.network.rx_sec || !data.network.tx_sec) { return ( Date: Wed, 27 Nov 2024 02:33:40 -0800 Subject: [PATCH 015/100] Enhancement: multiple widgets per service (#4338) --- docs/configs/docker.md | 12 ++ docs/configs/service-widgets.md | 20 ++- docs/widgets/index.md | 11 +- src/components/services/item.jsx | 4 +- src/components/services/widget.jsx | 10 +- src/pages/api/services/proxy.js | 6 +- src/utils/config/service-helpers.js | 159 ++++++++++++----------- src/utils/proxy/api-helpers.js | 1 + src/utils/proxy/handlers/credentialed.js | 4 +- src/utils/proxy/handlers/generic.js | 4 +- src/utils/proxy/handlers/jsonrpc.js | 4 +- src/utils/proxy/handlers/synology.js | 4 +- src/widgets/audiobookshelf/proxy.js | 4 +- src/widgets/beszel/proxy.js | 4 +- src/widgets/calendar/proxy.js | 4 +- src/widgets/crowdsec/proxy.js | 4 +- src/widgets/deluge/proxy.js | 4 +- src/widgets/flood/proxy.js | 4 +- src/widgets/freshrss/proxy.js | 4 +- src/widgets/fritzbox/proxy.js | 4 +- src/widgets/gamedig/proxy.js | 4 +- src/widgets/homeassistant/proxy.js | 4 +- src/widgets/homebox/proxy.js | 4 +- src/widgets/homebridge/proxy.js | 4 +- src/widgets/jackett/proxy.js | 4 +- src/widgets/jdownloader/proxy.js | 4 +- src/widgets/kavita/proxy.js | 4 +- src/widgets/minecraft/proxy.js | 4 +- src/widgets/npm/proxy.js | 4 +- src/widgets/omada/proxy.js | 4 +- src/widgets/openmediavault/proxy.js | 4 +- src/widgets/openwrt/proxy.js | 4 +- src/widgets/photoprism/proxy.js | 4 +- src/widgets/pihole/proxy.js | 4 +- src/widgets/plex/proxy.js | 4 +- src/widgets/pyload/proxy.js | 4 +- src/widgets/qbittorrent/proxy.js | 4 +- src/widgets/qnap/proxy.js | 4 +- src/widgets/rutorrent/proxy.js | 4 +- src/widgets/suwayomi/proxy.js | 4 +- src/widgets/tdarr/proxy.js | 4 +- src/widgets/transmission/proxy.js | 4 +- src/widgets/unifi/proxy.js | 8 +- src/widgets/urbackup/proxy.js | 4 +- src/widgets/watchtower/proxy.js | 4 +- src/widgets/xteve/proxy.js | 4 +- 46 files changed, 210 insertions(+), 169 deletions(-) diff --git a/docs/configs/docker.md b/docs/configs/docker.md index 51f6b523..7cea1fdc 100644 --- a/docs/configs/docker.md +++ b/docs/configs/docker.md @@ -153,6 +153,18 @@ labels: - homepage.widget.fields=["field1","field2"] # optional ``` +Multiple widgets can be specified by incrementing the index, e.g. + +```yaml +labels: ... + - homepage.widget[0].type=emby + - homepage.widget[0].url=http://emby.home + - homepage.widget[0].key=yourembyapikeyhere + - homepage.widget[1].type=uptimekuma + - homepage.widget[1].url=http://uptimekuma.home + - homepage.widget[1].slug=youreventslughere +``` + You can add specify fields for e.g. the [CustomAPI](../widgets/services/customapi.md) widget by using array-style dot notation: ```yaml diff --git a/docs/configs/service-widgets.md b/docs/configs/service-widgets.md index 9c54964e..df696f61 100644 --- a/docs/configs/service-widgets.md +++ b/docs/configs/service-widgets.md @@ -5,7 +5,7 @@ description: Service Widget Configuration Unless otherwise noted, URLs should not end with a `/` or other API path. Each widget will handle the path on its own. -Each service can have one widget attached to it (often matching the service type, but that's not forced). +Each service can have widgets attached to it (often matching the service type, but that's not forced). In addition to the href of the service, you can also specify the target location in which to open that link. See [Link Target](settings.md#link-target) for more details. @@ -22,6 +22,24 @@ Using Emby as an example, this is how you would attach the Emby service widget. key: apikeyapikeyapikeyapikeyapikey ``` +## Multiple Widgets + +Each service can have multiple widgets attached to it, for example: + +```yaml +- Emby: + icon: emby.png + href: http://emby.host.or.ip/ + description: Movies & TV Shows + widgets: + - type: emby + url: http://emby.host.or.ip + key: apikeyapikeyapikeyapikeyapikey + - type: uptimekuma + url: http://uptimekuma.host.or.ip:port + slug: statuspageslug +``` + ## Field Visibility Each widget can optionally provide a list of which fields should be visible via the `fields` widget property. If no fields are specified, then all fields will be displayed. The `fields` property must be a valid YAML array of strings. As an example, here is the entry for Sonarr showing only a couple of fields. diff --git a/docs/widgets/index.md b/docs/widgets/index.md index 8b81ee40..4bd45af7 100644 --- a/docs/widgets/index.md +++ b/docs/widgets/index.md @@ -19,10 +19,13 @@ Service widgets are used to display the status of a service, often a web service description: Watch movies and TV shows. server: localhost container: plex - widget: - type: tautulli - url: http://172.16.1.1:8181 - key: aabbccddeeffgghhiijjkkllmmnnoo + widgets: + - type: tautulli + url: http://172.16.1.1:8181 + key: aabbccddeeffgghhiijjkkllmmnnoo + - type: uptimekuma + url: http://172.16.1.2:8080 + slug: aaaaaaabbbbb ``` ## Info Widgets diff --git a/src/components/services/item.jsx b/src/components/services/item.jsx index a38dfaa3..54560d6f 100644 --- a/src/components/services/item.jsx +++ b/src/components/services/item.jsx @@ -154,7 +154,9 @@ export default function Item({ service, group, useEqualHeights }) {
)} - {service.widget && } + {service.widgets.map((widget) => ( + + ))}
); diff --git a/src/components/services/widget.jsx b/src/components/services/widget.jsx index 292b2b1c..61a21a66 100644 --- a/src/components/services/widget.jsx +++ b/src/components/services/widget.jsx @@ -3,22 +3,24 @@ import { useTranslation } from "next-i18next"; import ErrorBoundary from "components/errorboundry"; import components from "widgets/components"; -export default function Widget({ service }) { +export default function Widget({ widget, service }) { const { t } = useTranslation("common"); - const ServiceWidget = components[service.widget.type]; + const ServiceWidget = components[widget.type]; + const fullService = Object.apply({}, service); + fullService.widget = widget; if (ServiceWidget) { return ( - + ); } return (
-
{t("widget.missing_type", { type: service.widget.type })}
+
{t("widget.missing_type", { type: widget.type })}
); } diff --git a/src/pages/api/services/proxy.js b/src/pages/api/services/proxy.js index 90280c3d..3f8adc88 100644 --- a/src/pages/api/services/proxy.js +++ b/src/pages/api/services/proxy.js @@ -9,8 +9,8 @@ const logger = createLogger("servicesProxy"); export default async function handler(req, res) { try { - const { service, group } = req.query; - const serviceWidget = await getServiceWidget(group, service); + const { service, group, index } = req.query; + const serviceWidget = await getServiceWidget(group, service, index); let type = serviceWidget?.type; // exceptions @@ -41,7 +41,7 @@ export default async function handler(req, res) { const endpoint = mapping?.endpoint; const endpointProxy = mapping?.proxyHandler || serviceProxyHandler; - if (mapping.method && mapping.method !== req.method) { + if (mapping?.method && mapping.method !== req.method) { logger.debug("Unsupported method: %s", req.method); return res.status(403).json({ error: "Unsupported method" }); } diff --git a/src/utils/config/service-helpers.js b/src/utils/config/service-helpers.js index ea82c735..e6ef6173 100644 --- a/src/utils/config/service-helpers.js +++ b/src/utils/config/service-helpers.js @@ -354,8 +354,12 @@ export function cleanServiceGroups(groups) { if (typeof cleanedService.weight !== "number") { cleanedService.weight = 0; } - + if (!cleanedService.widgets) cleanedService.widgets = []; if (cleanedService.widget) { + cleanedService.widgets.push(cleanedService.widget); + delete cleanedService.widget; + } + cleanedService.widgets = cleanedService.widgets.map((widgetData, index) => { // whitelisted set of keys to pass to the frontend // alphabetical, grouped by widget(s) const { @@ -495,7 +499,7 @@ export function cleanServiceGroups(groups) { // spoolman spoolIds, - } = cleanedService.widget; + } = widgetData; let fieldsList = fields; if (typeof fields === "string") { @@ -507,160 +511,160 @@ export function cleanServiceGroups(groups) { } } - cleanedService.widget = { + const widget = { type, fields: fieldsList || null, hide_errors: hideErrors || false, service_name: service.name, service_group: serviceGroup.name, + index, }; if (type === "azuredevops") { - if (userEmail) cleanedService.widget.userEmail = userEmail; - if (repositoryId) cleanedService.widget.repositoryId = repositoryId; + if (userEmail) widget.userEmail = userEmail; + if (repositoryId) widget.repositoryId = repositoryId; } if (type === "beszel") { - if (systemId) cleanedService.widget.systemId = systemId; + if (systemId) widget.systemId = systemId; } if (type === "coinmarketcap") { - if (currency) cleanedService.widget.currency = currency; - if (symbols) cleanedService.widget.symbols = symbols; - if (slugs) cleanedService.widget.slugs = slugs; - if (defaultinterval) cleanedService.widget.defaultinterval = defaultinterval; + if (currency) widget.currency = currency; + if (symbols) widget.symbols = symbols; + if (slugs) widget.slugs = slugs; + if (defaultinterval) widget.defaultinterval = defaultinterval; } if (type === "docker") { - if (server) cleanedService.widget.server = server; - if (container) cleanedService.widget.container = container; + if (server) widget.server = server; + if (container) widget.container = container; } if (type === "unifi") { - if (site) cleanedService.widget.site = site; + if (site) widget.site = site; } if (type === "proxmox") { - if (node) cleanedService.widget.node = node; + if (node) widget.node = node; } if (type === "kubernetes") { - if (namespace) cleanedService.widget.namespace = namespace; - if (app) cleanedService.widget.app = app; - if (podSelector) cleanedService.widget.podSelector = podSelector; + if (namespace) widget.namespace = namespace; + if (app) widget.app = app; + if (podSelector) widget.podSelector = podSelector; } if (type === "iframe") { - if (src) cleanedService.widget.src = src; - if (classes) cleanedService.widget.classes = classes; - if (referrerPolicy) cleanedService.widget.referrerPolicy = referrerPolicy; - if (allowPolicy) cleanedService.widget.allowPolicy = allowPolicy; - if (allowFullscreen) cleanedService.widget.allowFullscreen = allowFullscreen; - if (loadingStrategy) cleanedService.widget.loadingStrategy = loadingStrategy; - if (allowScrolling) cleanedService.widget.allowScrolling = allowScrolling; - if (refreshInterval) cleanedService.widget.refreshInterval = refreshInterval; + if (src) widget.src = src; + if (classes) widget.classes = classes; + if (referrerPolicy) widget.referrerPolicy = referrerPolicy; + if (allowPolicy) widget.allowPolicy = allowPolicy; + if (allowFullscreen) widget.allowFullscreen = allowFullscreen; + if (loadingStrategy) widget.loadingStrategy = loadingStrategy; + if (allowScrolling) widget.allowScrolling = allowScrolling; + if (refreshInterval) widget.refreshInterval = refreshInterval; } if (["opnsense", "pfsense"].includes(type)) { - if (wan) cleanedService.widget.wan = wan; + if (wan) widget.wan = wan; } if (["emby", "jellyfin"].includes(type)) { - if (enableBlocks !== undefined) cleanedService.widget.enableBlocks = JSON.parse(enableBlocks); - if (enableNowPlaying !== undefined) cleanedService.widget.enableNowPlaying = JSON.parse(enableNowPlaying); + if (enableBlocks !== undefined) widget.enableBlocks = JSON.parse(enableBlocks); + if (enableNowPlaying !== undefined) widget.enableNowPlaying = JSON.parse(enableNowPlaying); } if (["emby", "jellyfin", "tautulli"].includes(type)) { if (expandOneStreamToTwoRows !== undefined) - cleanedService.widget.expandOneStreamToTwoRows = !!JSON.parse(expandOneStreamToTwoRows); - if (showEpisodeNumber !== undefined) - cleanedService.widget.showEpisodeNumber = !!JSON.parse(showEpisodeNumber); - if (enableUser !== undefined) cleanedService.widget.enableUser = !!JSON.parse(enableUser); + widget.expandOneStreamToTwoRows = !!JSON.parse(expandOneStreamToTwoRows); + if (showEpisodeNumber !== undefined) widget.showEpisodeNumber = !!JSON.parse(showEpisodeNumber); + if (enableUser !== undefined) widget.enableUser = !!JSON.parse(enableUser); } if (["sonarr", "radarr"].includes(type)) { - if (enableQueue !== undefined) cleanedService.widget.enableQueue = JSON.parse(enableQueue); + if (enableQueue !== undefined) widget.enableQueue = JSON.parse(enableQueue); } if (type === "truenas") { - if (enablePools !== undefined) cleanedService.widget.enablePools = JSON.parse(enablePools); - if (nasType !== undefined) cleanedService.widget.nasType = nasType; + if (enablePools !== undefined) widget.enablePools = JSON.parse(enablePools); + if (nasType !== undefined) widget.nasType = nasType; } if (["diskstation", "qnap"].includes(type)) { - if (volume) cleanedService.widget.volume = volume; + if (volume) widget.volume = volume; } if (type === "kopia") { - if (snapshotHost) cleanedService.widget.snapshotHost = snapshotHost; - if (snapshotPath) cleanedService.widget.snapshotPath = snapshotPath; + if (snapshotHost) widget.snapshotHost = snapshotHost; + if (snapshotPath) widget.snapshotPath = snapshotPath; } if (["glances", "immich", "mealie", "pfsense", "pihole"].includes(type)) { - if (version) cleanedService.widget.version = parseInt(version, 10); + if (version) widget.version = parseInt(version, 10); } if (type === "glances") { - if (metric) cleanedService.widget.metric = metric; + if (metric) widget.metric = metric; if (chart !== undefined) { - cleanedService.widget.chart = chart; + widget.chart = chart; } else { - cleanedService.widget.chart = true; + widget.chart = true; } - if (refreshInterval) cleanedService.widget.refreshInterval = refreshInterval; - if (pointsLimit) cleanedService.widget.pointsLimit = pointsLimit; - if (diskUnits) cleanedService.widget.diskUnits = diskUnits; + if (refreshInterval) widget.refreshInterval = refreshInterval; + if (pointsLimit) widget.pointsLimit = pointsLimit; + if (diskUnits) widget.diskUnits = diskUnits; } if (type === "mjpeg") { - if (stream) cleanedService.widget.stream = stream; - if (fit) cleanedService.widget.fit = fit; + if (stream) widget.stream = stream; + if (fit) widget.fit = fit; } if (type === "openmediavault") { - if (method) cleanedService.widget.method = method; + if (method) widget.method = method; } if (type === "openwrt") { - if (interfaceName) cleanedService.widget.interfaceName = interfaceName; + if (interfaceName) widget.interfaceName = interfaceName; } if (type === "customapi") { - if (mappings) cleanedService.widget.mappings = mappings; - if (display) cleanedService.widget.display = display; - if (refreshInterval) cleanedService.widget.refreshInterval = refreshInterval; + if (mappings) widget.mappings = mappings; + if (display) widget.display = display; + if (refreshInterval) widget.refreshInterval = refreshInterval; } if (type === "calendar") { - if (integrations) cleanedService.widget.integrations = integrations; - if (firstDayInWeek) cleanedService.widget.firstDayInWeek = firstDayInWeek; - if (view) cleanedService.widget.view = view; - if (maxEvents) cleanedService.widget.maxEvents = maxEvents; - if (previousDays) cleanedService.widget.previousDays = previousDays; - if (showTime) cleanedService.widget.showTime = showTime; - if (timezone) cleanedService.widget.timezone = timezone; + if (integrations) widget.integrations = integrations; + if (firstDayInWeek) widget.firstDayInWeek = firstDayInWeek; + if (view) widget.view = view; + if (maxEvents) widget.maxEvents = maxEvents; + if (previousDays) widget.previousDays = previousDays; + if (showTime) widget.showTime = showTime; + if (timezone) widget.timezone = timezone; } if (type === "hdhomerun") { - if (tuner !== undefined) cleanedService.widget.tuner = tuner; + if (tuner !== undefined) widget.tuner = tuner; } if (type === "healthchecks") { - if (uuid !== undefined) cleanedService.widget.uuid = uuid; + if (uuid !== undefined) widget.uuid = uuid; } if (type === "speedtest") { if (bitratePrecision !== undefined) { - cleanedService.widget.bitratePrecision = parseInt(bitratePrecision, 10); + widget.bitratePrecision = parseInt(bitratePrecision, 10); } } if (type === "stocks") { - if (watchlist) cleanedService.widget.watchlist = watchlist; - if (showUSMarketStatus) cleanedService.widget.showUSMarketStatus = showUSMarketStatus; + if (watchlist) widget.watchlist = watchlist; + if (showUSMarketStatus) widget.showUSMarketStatus = showUSMarketStatus; } if (type === "wgeasy") { - if (threshold !== undefined) cleanedService.widget.threshold = parseInt(threshold, 10); + if (threshold !== undefined) widget.threshold = parseInt(threshold, 10); } if (type === "frigate") { - if (enableRecentEvents !== undefined) cleanedService.widget.enableRecentEvents = enableRecentEvents; + if (enableRecentEvents !== undefined) widget.enableRecentEvents = enableRecentEvents; } if (type === "technitium") { - if (range !== undefined) cleanedService.widget.range = range; + if (range !== undefined) widget.range = range; } if (type === "lubelogger") { - if (vehicleID !== undefined) cleanedService.widget.vehicleID = parseInt(vehicleID, 10); + if (vehicleID !== undefined) widget.vehicleID = parseInt(vehicleID, 10); } if (type === "vikunja") { - if (enableTaskList !== undefined) cleanedService.widget.enableTaskList = !!enableTaskList; + if (enableTaskList !== undefined) widget.enableTaskList = !!enableTaskList; } if (type === "prometheusmetric") { - if (metrics) cleanedService.widget.metrics = metrics; - if (refreshInterval) cleanedService.widget.refreshInterval = refreshInterval; + if (metrics) widget.metrics = metrics; + if (refreshInterval) widget.refreshInterval = refreshInterval; } if (type === "spoolman") { - if (spoolIds !== undefined) cleanedService.widget.spoolIds = spoolIds; + if (spoolIds !== undefined) widget.spoolIds = spoolIds; } - } - + return widget; + }); return cleanedService; }), })); @@ -693,12 +697,11 @@ export async function getServiceItem(group, service) { return false; } -export default async function getServiceWidget(group, service) { +export default async function getServiceWidget(group, service, index) { const serviceItem = await getServiceItem(group, service); if (serviceItem) { - const { widget } = serviceItem; - return widget; + const { widget, widgets } = serviceItem; + return index > -1 && widgets ? widgets[index] : widget; } - return false; } diff --git a/src/utils/proxy/api-helpers.js b/src/utils/proxy/api-helpers.js index 8e0682db..a02ea623 100644 --- a/src/utils/proxy/api-helpers.js +++ b/src/utils/proxy/api-helpers.js @@ -12,6 +12,7 @@ export function getURLSearchParams(widget, endpoint) { const params = new URLSearchParams({ group: widget.service_group, service: widget.service_name, + index: widget.index, }); if (endpoint) { params.append("endpoint", endpoint); diff --git a/src/utils/proxy/handlers/credentialed.js b/src/utils/proxy/handlers/credentialed.js index cbe0422a..cea95196 100644 --- a/src/utils/proxy/handlers/credentialed.js +++ b/src/utils/proxy/handlers/credentialed.js @@ -9,10 +9,10 @@ import widgets from "widgets/widgets"; const logger = createLogger("credentialedProxyHandler"); export default async function credentialedProxyHandler(req, res, map) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (group && service) { - const widget = await getServiceWidget(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" }); diff --git a/src/utils/proxy/handlers/generic.js b/src/utils/proxy/handlers/generic.js index c6b9236b..2e788a98 100644 --- a/src/utils/proxy/handlers/generic.js +++ b/src/utils/proxy/handlers/generic.js @@ -8,10 +8,10 @@ import widgets from "widgets/widgets"; const logger = createLogger("genericProxyHandler"); export default async function genericProxyHandler(req, res, map) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (group && service) { - const widget = await getServiceWidget(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" }); diff --git a/src/utils/proxy/handlers/jsonrpc.js b/src/utils/proxy/handlers/jsonrpc.js index 3974dbdc..f9fb1883 100644 --- a/src/utils/proxy/handlers/jsonrpc.js +++ b/src/utils/proxy/handlers/jsonrpc.js @@ -65,10 +65,10 @@ export async function sendJsonRpcRequest(url, method, params, widget) { } export default async function jsonrpcProxyHandler(req, res) { - const { group, service, endpoint: method } = req.query; + const { group, service, endpoint: method, index } = req.query; if (group && service) { - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); const api = widgets?.[widget.type]?.api; const [, mapping] = Object.entries(widgets?.[widget.type]?.mappings).find(([, value]) => value.endpoint === method); diff --git a/src/utils/proxy/handlers/synology.js b/src/utils/proxy/handlers/synology.js index be44e810..030e53ba 100644 --- a/src/utils/proxy/handlers/synology.js +++ b/src/utils/proxy/handlers/synology.js @@ -131,13 +131,13 @@ function toError(url, synologyError) { } export default async function synologyProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (!group || !service) { return res.status(400).json({ error: "Invalid proxy service type" }); } - const serviceWidget = await getServiceWidget(group, service); + const serviceWidget = await getServiceWidget(group, service, index); const widget = widgets?.[serviceWidget.type]; const mapping = widget?.mappings?.[endpoint]; if (!widget.api || !mapping) { diff --git a/src/widgets/audiobookshelf/proxy.js b/src/widgets/audiobookshelf/proxy.js index 9701c1fe..1a89736b 100644 --- a/src/widgets/audiobookshelf/proxy.js +++ b/src/widgets/audiobookshelf/proxy.js @@ -23,14 +23,14 @@ async function retrieveFromAPI(url, key) { } export default async function audiobookshelfProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (!group || !service) { logger.debug("Invalid or missing service '%s' or group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/beszel/proxy.js b/src/widgets/beszel/proxy.js index 04083e42..61bc969b 100644 --- a/src/widgets/beszel/proxy.js +++ b/src/widgets/beszel/proxy.js @@ -34,10 +34,10 @@ async function login(loginUrl, username, password, service) { } export default async function beszelProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (group && service) { - const widget = await getServiceWidget(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" }); diff --git a/src/widgets/calendar/proxy.js b/src/widgets/calendar/proxy.js index cf754424..d36f30c9 100644 --- a/src/widgets/calendar/proxy.js +++ b/src/widgets/calendar/proxy.js @@ -5,10 +5,10 @@ import createLogger from "utils/logger"; const logger = createLogger("calendarProxyHandler"); export default async function calendarProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (group && service) { - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); const integration = widget.integrations?.find((i) => i.name === endpoint); if (integration) { diff --git a/src/widgets/crowdsec/proxy.js b/src/widgets/crowdsec/proxy.js index e78fbc5e..85803845 100644 --- a/src/widgets/crowdsec/proxy.js +++ b/src/widgets/crowdsec/proxy.js @@ -35,14 +35,14 @@ async function login(widget, service) { } export default async function crowdsecProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (!group || !service) { logger.error("Invalid or missing service '%s' or group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget || !widgets[widget.type].api) { logger.error("Invalid or missing widget for service '%s' in group '%s'", service, group); return res.status(400).json({ error: "Invalid widget configuration" }); diff --git a/src/widgets/deluge/proxy.js b/src/widgets/deluge/proxy.js index b86873a8..61329697 100644 --- a/src/widgets/deluge/proxy.js +++ b/src/widgets/deluge/proxy.js @@ -40,14 +40,14 @@ function login(url, password) { } export default async function delugeProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("Invalid or missing service '%s' or group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/flood/proxy.js b/src/widgets/flood/proxy.js index 3345ad7b..e0c10173 100644 --- a/src/widgets/flood/proxy.js +++ b/src/widgets/flood/proxy.js @@ -28,14 +28,14 @@ async function login(widget) { } export default async function floodProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (!group || !service) { logger.debug("Invalid or missing service '%s' or group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/freshrss/proxy.js b/src/widgets/freshrss/proxy.js index c08e5c87..881094bd 100644 --- a/src/widgets/freshrss/proxy.js +++ b/src/widgets/freshrss/proxy.js @@ -74,14 +74,14 @@ async function apiCall(widget, endpoint, service) { } export default async function freshrssProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("Invalid or missing service '%s' or group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); diff --git a/src/widgets/fritzbox/proxy.js b/src/widgets/fritzbox/proxy.js index a0a22d8b..d1a66d97 100644 --- a/src/widgets/fritzbox/proxy.js +++ b/src/widgets/fritzbox/proxy.js @@ -46,8 +46,8 @@ async function requestEndpoint(apiBaseUrl, service, action) { } export default async function fritzboxProxyHandler(req, res) { - const { group, service } = req.query; - const serviceWidget = await getServiceWidget(group, service); + const { group, service, index } = req.query; + const serviceWidget = await getServiceWidget(group, service, index); if (!serviceWidget) { res.status(500).json({ error: { message: "Service widget not found" } }); diff --git a/src/widgets/gamedig/proxy.js b/src/widgets/gamedig/proxy.js index 05fa615c..ecf6e4c6 100644 --- a/src/widgets/gamedig/proxy.js +++ b/src/widgets/gamedig/proxy.js @@ -7,8 +7,8 @@ const proxyName = "gamedigProxyHandler"; const logger = createLogger(proxyName); export default async function gamedigProxyHandler(req, res) { - const { group, service } = req.query; - const serviceWidget = await getServiceWidget(group, service); + const { group, service, index } = req.query; + const serviceWidget = await getServiceWidget(group, service, index); const url = new URL(serviceWidget.url); try { diff --git a/src/widgets/homeassistant/proxy.js b/src/widgets/homeassistant/proxy.js index fe488f86..e1f02ddb 100644 --- a/src/widgets/homeassistant/proxy.js +++ b/src/widgets/homeassistant/proxy.js @@ -62,14 +62,14 @@ async function getQuery(query, { url, key }) { } export default async function homeassistantProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("Invalid or missing service '%s' or group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); diff --git a/src/widgets/homebox/proxy.js b/src/widgets/homebox/proxy.js index 0d6fdf13..c91ce552 100644 --- a/src/widgets/homebox/proxy.js +++ b/src/widgets/homebox/proxy.js @@ -68,14 +68,14 @@ async function apiCall(widget, endpoint, service) { } export default async function homeboxProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("Invalid or missing service '%s' or group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); diff --git a/src/widgets/homebridge/proxy.js b/src/widgets/homebridge/proxy.js index 17dc8635..4da9197b 100644 --- a/src/widgets/homebridge/proxy.js +++ b/src/widgets/homebridge/proxy.js @@ -71,14 +71,14 @@ async function apiCall(widget, endpoint, service) { } export default async function homebridgeProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("Invalid or missing service '%s' or group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/jackett/proxy.js b/src/widgets/jackett/proxy.js index 5292695f..035309b3 100644 --- a/src/widgets/jackett/proxy.js +++ b/src/widgets/jackett/proxy.js @@ -25,14 +25,14 @@ async function fetchJackettCookie(widget, loginURL) { } export default async function jackettProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (!group || !service) { logger.error("Invalid or missing service '%s' or group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget || !widgets[widget.type].api) { logger.error("Invalid or missing widget for service '%s' in group '%s'", service, group); return res.status(400).json({ error: "Invalid widget configuration" }); diff --git a/src/widgets/jdownloader/proxy.js b/src/widgets/jdownloader/proxy.js index 88a92d95..ae8c845c 100644 --- a/src/widgets/jdownloader/proxy.js +++ b/src/widgets/jdownloader/proxy.js @@ -12,12 +12,12 @@ const proxyName = "jdownloaderProxyHandler"; const logger = createLogger(proxyName); async function getWidget(req) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("Invalid or missing service '%s' or group '%s'", service, group); return null; } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); return null; diff --git a/src/widgets/kavita/proxy.js b/src/widgets/kavita/proxy.js index b8e9813f..1c41c45f 100644 --- a/src/widgets/kavita/proxy.js +++ b/src/widgets/kavita/proxy.js @@ -70,14 +70,14 @@ async function apiCall(widget, endpoint, service) { } export default async function KavitaProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("Invalid or missing service '%s' or group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); diff --git a/src/widgets/minecraft/proxy.js b/src/widgets/minecraft/proxy.js index f7bac9d4..98d1be88 100644 --- a/src/widgets/minecraft/proxy.js +++ b/src/widgets/minecraft/proxy.js @@ -7,8 +7,8 @@ const proxyName = "minecraftProxyHandler"; const logger = createLogger(proxyName); export default async function minecraftProxyHandler(req, res) { - const { group, service } = req.query; - const serviceWidget = await getServiceWidget(group, service); + const { group, service, index } = req.query; + const serviceWidget = await getServiceWidget(group, service, index); const url = new URL(serviceWidget.url); try { const pingResponse = await pingWithPromise(url.hostname, url.port || 25565); diff --git a/src/widgets/npm/proxy.js b/src/widgets/npm/proxy.js index 978254f8..6c7ba09e 100644 --- a/src/widgets/npm/proxy.js +++ b/src/widgets/npm/proxy.js @@ -36,10 +36,10 @@ async function login(loginUrl, username, password, service) { } export default async function npmProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (group && service) { - const widget = await getServiceWidget(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" }); diff --git a/src/widgets/omada/proxy.js b/src/widgets/omada/proxy.js index 8e8994a5..f4da1293 100644 --- a/src/widgets/omada/proxy.js +++ b/src/widgets/omada/proxy.js @@ -33,10 +33,10 @@ async function login(loginUrl, username, password, controllerVersionMajor) { } export default async function omadaProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (group && service) { - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (widget) { const { url } = widget; diff --git a/src/widgets/openmediavault/proxy.js b/src/widgets/openmediavault/proxy.js index e1f97a56..9cda42e8 100644 --- a/src/widgets/openmediavault/proxy.js +++ b/src/widgets/openmediavault/proxy.js @@ -12,14 +12,14 @@ const BG_POLL_PERIOD = 500; const logger = createLogger(PROXY_NAME); async function getWidget(req) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("Invalid or missing service '%s' or group '%s'", service, group); return null; } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/openwrt/proxy.js b/src/widgets/openwrt/proxy.js index 977db8ca..0a0da3ff 100644 --- a/src/widgets/openwrt/proxy.js +++ b/src/widgets/openwrt/proxy.js @@ -17,14 +17,14 @@ const PARAMS = { }; async function getWidget(req) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("Invalid or missing service '%s' or group '%s'", service, group); return null; } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/photoprism/proxy.js b/src/widgets/photoprism/proxy.js index 509bfa0c..fe5096b3 100644 --- a/src/widgets/photoprism/proxy.js +++ b/src/widgets/photoprism/proxy.js @@ -6,14 +6,14 @@ import createLogger from "utils/logger"; const logger = createLogger("photoprismProxyHandler"); export default async function photoprismProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("Invalid or missing service '%s' or group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/pihole/proxy.js b/src/widgets/pihole/proxy.js index 35873fa9..bf24624d 100644 --- a/src/widgets/pihole/proxy.js +++ b/src/widgets/pihole/proxy.js @@ -33,7 +33,7 @@ async function login(widget, service) { } export default async function piholeProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; let endpoint = "stats/summary"; if (!group || !service) { @@ -41,7 +41,7 @@ export default async function piholeProxyHandler(req, res) { return res.status(400).json({ error: "Invalid proxy service type" }); } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.error("Invalid or missing widget for service '%s' in group '%s'", service, group); return res.status(400).json({ error: "Invalid widget configuration" }); diff --git a/src/widgets/plex/proxy.js b/src/widgets/plex/proxy.js index d8033065..2956f280 100644 --- a/src/widgets/plex/proxy.js +++ b/src/widgets/plex/proxy.js @@ -16,14 +16,14 @@ const tvCacheKey = `${proxyName}__tv`; const logger = createLogger(proxyName); async function getWidget(req) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("Invalid or missing service '%s' or group '%s'", service, group); return null; } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/pyload/proxy.js b/src/widgets/pyload/proxy.js index d9469d1c..a380c865 100644 --- a/src/widgets/pyload/proxy.js +++ b/src/widgets/pyload/proxy.js @@ -67,11 +67,11 @@ async function login(loginUrl, service, username, password = "") { } export default async function pyloadProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; try { if (group && service) { - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (widget) { const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget })); diff --git a/src/widgets/qbittorrent/proxy.js b/src/widgets/qbittorrent/proxy.js index e1a0f055..aead7582 100644 --- a/src/widgets/qbittorrent/proxy.js +++ b/src/widgets/qbittorrent/proxy.js @@ -21,14 +21,14 @@ async function login(widget) { } export default async function qbittorrentProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (!group || !service) { logger.debug("Invalid or missing service '%s' or group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/qnap/proxy.js b/src/widgets/qnap/proxy.js index 508c8a46..07917d28 100644 --- a/src/widgets/qnap/proxy.js +++ b/src/widgets/qnap/proxy.js @@ -77,14 +77,14 @@ async function apiCall(widget, endpoint, service) { } export default async function qnapProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("Invalid or missing service '%s' or group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); diff --git a/src/widgets/rutorrent/proxy.js b/src/widgets/rutorrent/proxy.js index 47c76191..e0ae44fe 100644 --- a/src/widgets/rutorrent/proxy.js +++ b/src/widgets/rutorrent/proxy.js @@ -45,10 +45,10 @@ const getTorrentInfo = (data) => ({ }); export default async function rutorrentProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (group && service) { - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (widget) { const api = widgets?.[widget.type]?.api; diff --git a/src/widgets/suwayomi/proxy.js b/src/widgets/suwayomi/proxy.js index d4d71675..def811cc 100644 --- a/src/widgets/suwayomi/proxy.js +++ b/src/widgets/suwayomi/proxy.js @@ -114,14 +114,14 @@ function extractCounts(responseJSON, fields) { } export default async function suwayomiProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (!group || !service) { logger.debug("Invalid or missing service '%s' or group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/tdarr/proxy.js b/src/widgets/tdarr/proxy.js index 9e26fdc0..88da30fd 100644 --- a/src/widgets/tdarr/proxy.js +++ b/src/widgets/tdarr/proxy.js @@ -8,14 +8,14 @@ const proxyName = "tdarrProxyHandler"; const logger = createLogger(proxyName); export default async function tdarrProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("Invalid or missing service '%s' or group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/transmission/proxy.js b/src/widgets/transmission/proxy.js index 823def05..8b8049bc 100644 --- a/src/widgets/transmission/proxy.js +++ b/src/widgets/transmission/proxy.js @@ -11,14 +11,14 @@ const headerCacheKey = `${proxyName}__headers`; const logger = createLogger(proxyName); export default async function transmissionProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("Invalid or missing service '%s' or group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/unifi/proxy.js b/src/widgets/unifi/proxy.js index 98c98f37..559065e3 100644 --- a/src/widgets/unifi/proxy.js +++ b/src/widgets/unifi/proxy.js @@ -14,13 +14,13 @@ const prefixCacheKey = `${proxyName}__prefix`; const logger = createLogger(proxyName); async function getWidget(req) { - const { group, service } = req.query; + const { group, service, index } = req.query; let widget = null; if (group === "unifi_console" && service === "unifi_console") { // info widget - const index = req.query?.query ? JSON.parse(req.query.query).index : undefined; - widget = await getPrivateWidgetOptions("unifi_console", index); + const infowidgetIndex = req.query?.query ? JSON.parse(req.query.query).index : undefined; + widget = await getPrivateWidgetOptions("unifi_console", infowidgetIndex); if (!widget) { logger.debug("Error retrieving settings for this Unifi widget"); return null; @@ -32,7 +32,7 @@ async function getWidget(req) { return null; } - widget = await getServiceWidget(group, service); + widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/urbackup/proxy.js b/src/widgets/urbackup/proxy.js index 94b8eeff..4e7a0a8d 100644 --- a/src/widgets/urbackup/proxy.js +++ b/src/widgets/urbackup/proxy.js @@ -3,8 +3,8 @@ import { UrbackupServer } from "urbackup-server-api"; import getServiceWidget from "utils/config/service-helpers"; export default async function urbackupProxyHandler(req, res) { - const { group, service } = req.query; - const serviceWidget = await getServiceWidget(group, service); + const { group, service, index } = req.query; + const serviceWidget = await getServiceWidget(group, service, index); const server = new UrbackupServer({ url: serviceWidget.url, diff --git a/src/widgets/watchtower/proxy.js b/src/widgets/watchtower/proxy.js index b3155a1e..588d08ee 100644 --- a/src/widgets/watchtower/proxy.js +++ b/src/widgets/watchtower/proxy.js @@ -8,14 +8,14 @@ const proxyName = "watchtowerProxyHandler"; const logger = createLogger(proxyName); export default async function watchtowerProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (!group || !service) { logger.debug("Invalid or missing service '%s' or group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/xteve/proxy.js b/src/widgets/xteve/proxy.js index 421f2b49..453e3645 100644 --- a/src/widgets/xteve/proxy.js +++ b/src/widgets/xteve/proxy.js @@ -7,13 +7,13 @@ import getServiceWidget from "utils/config/service-helpers"; const logger = createLogger("xteveProxyHandler"); export default async function xteveProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { return res.status(400).json({ error: "Invalid proxy service type" }); } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); const api = widgets?.[widget.type]?.api; if (!api) { return res.status(403).json({ error: "Service does not support API calls" }); From be8363cc3514749f54c5060c6548138129612e82 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Wed, 27 Nov 2024 17:01:47 -0800 Subject: [PATCH 016/100] Feature: nested groups (#4346) --- src/components/services/group.jsx | 33 ++++++++-- src/components/services/item.jsx | 6 +- src/components/services/list.jsx | 4 +- src/components/services/ping.jsx | 4 +- src/components/services/site-monitor.jsx | 4 +- src/pages/api/ping.js | 6 +- src/pages/api/siteMonitor.js | 6 +- src/pages/index.jsx | 6 +- src/utils/config/api-response.js | 24 +++++-- src/utils/config/service-helpers.js | 81 ++++++++++++++++-------- 10 files changed, 119 insertions(+), 55 deletions(-) diff --git a/src/components/services/group.jsx b/src/components/services/group.jsx index cdbb89f3..f25f7ec1 100644 --- a/src/components/services/group.jsx +++ b/src/components/services/group.jsx @@ -3,12 +3,13 @@ import classNames from "classnames"; import { Disclosure, Transition } from "@headlessui/react"; import { MdKeyboardArrowDown } from "react-icons/md"; +import { columnMap } from "../../utils/layout/columns"; + import List from "components/services/list"; import ResolvedIcon from "components/resolvedicon"; export default function ServicesGroup({ group, - services, layout, fiveColumns, disableCollapse, @@ -23,7 +24,7 @@ export default function ServicesGroup({ return (
)}

- {services.name} + {group.name}

- + + {group.groups?.length > 0 && ( +
+ {group.groups.map((subgroup) => ( + + ))} +
+ )}
diff --git a/src/components/services/item.jsx b/src/components/services/item.jsx index 54560d6f..adf5fc97 100644 --- a/src/components/services/item.jsx +++ b/src/components/services/item.jsx @@ -12,7 +12,7 @@ import Kubernetes from "widgets/kubernetes/component"; import { SettingsContext } from "utils/contexts/settings"; import ResolvedIcon from "components/resolvedicon"; -export default function Item({ service, group, useEqualHeights }) { +export default function Item({ service, groupName, useEqualHeights }) { const hasLink = service.href && service.href !== "#"; const { settings } = useContext(SettingsContext); const showStats = service.showStats === false ? false : settings.showStats; @@ -90,14 +90,14 @@ export default function Item({ service, group, useEqualHeights }) { > {service.ping && (
- + Ping status
)} {service.siteMonitor && (
- + Site monitor status
)} diff --git a/src/components/services/list.jsx b/src/components/services/list.jsx index f3fd6e2a..c15d6aed 100644 --- a/src/components/services/list.jsx +++ b/src/components/services/list.jsx @@ -4,7 +4,7 @@ import { columnMap } from "../../utils/layout/columns"; import Item from "components/services/item"; -export default function List({ group, services, layout, useEqualHeights }) { +export default function List({ groupName, services, layout, useEqualHeights }) { return (
    s).join("-")} service={service} - group={group} + groupName={groupName} useEqualHeights={layout?.useEqualHeights ?? useEqualHeights} /> ))} diff --git a/src/components/services/ping.jsx b/src/components/services/ping.jsx index f72d40b3..670f9d4b 100644 --- a/src/components/services/ping.jsx +++ b/src/components/services/ping.jsx @@ -1,9 +1,9 @@ import { useTranslation } from "react-i18next"; import useSWR from "swr"; -export default function Ping({ group, service, style }) { +export default function Ping({ groupName, serviceName, style }) { const { t } = useTranslation(); - const { data, error } = useSWR(`/api/ping?${new URLSearchParams({ group, service }).toString()}`, { + const { data, error } = useSWR(`/api/ping?${new URLSearchParams({ groupName, serviceName }).toString()}`, { refreshInterval: 30000, }); diff --git a/src/components/services/site-monitor.jsx b/src/components/services/site-monitor.jsx index 3d5ef79e..4dceb44c 100644 --- a/src/components/services/site-monitor.jsx +++ b/src/components/services/site-monitor.jsx @@ -1,9 +1,9 @@ import { useTranslation } from "react-i18next"; import useSWR from "swr"; -export default function SiteMonitor({ group, service, style }) { +export default function SiteMonitor({ groupName, serviceName, style }) { const { t } = useTranslation(); - const { data, error } = useSWR(`/api/siteMonitor?${new URLSearchParams({ group, service }).toString()}`, { + const { data, error } = useSWR(`/api/siteMonitor?${new URLSearchParams({ groupName, serviceName }).toString()}`, { refreshInterval: 30000, }); diff --git a/src/pages/api/ping.js b/src/pages/api/ping.js index e540fa68..8ef64ffc 100644 --- a/src/pages/api/ping.js +++ b/src/pages/api/ping.js @@ -6,10 +6,10 @@ import createLogger from "utils/logger"; const logger = createLogger("ping"); export default async function handler(req, res) { - const { group, service } = req.query; - const serviceItem = await getServiceItem(group, service); + const { groupName, serviceName } = req.query; + const serviceItem = await getServiceItem(groupName, serviceName); if (!serviceItem) { - logger.debug(`No service item found for group ${group} named ${service}`); + logger.debug(`No service item found for group ${groupName} named ${serviceName}`); return res.status(400).send({ error: "Unable to find service, see log for details.", }); diff --git a/src/pages/api/siteMonitor.js b/src/pages/api/siteMonitor.js index 9e030d74..072d3d4c 100644 --- a/src/pages/api/siteMonitor.js +++ b/src/pages/api/siteMonitor.js @@ -7,10 +7,10 @@ import { httpProxy } from "utils/proxy/http"; const logger = createLogger("siteMonitor"); export default async function handler(req, res) { - const { group, service } = req.query; - const serviceItem = await getServiceItem(group, service); + const { groupName, serviceName } = req.query; + const serviceItem = await getServiceItem(groupName, serviceName); if (!serviceItem) { - logger.debug(`No service item found for group ${group} named ${service}`); + logger.debug(`No service item found for group ${groupName} named ${serviceName}`); return res.status(400).send({ error: "Unable to find service, see log for details.", }); diff --git a/src/pages/index.jsx b/src/pages/index.jsx index dd0df95f..7a7fdef0 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -291,8 +291,7 @@ function Home({ initialSettings }) { group.services ? ( ( { + if (group.name === mergedGroup.name) { + // eslint-disable-next-line no-param-reassign + group.services = mergedGroup.services; + } else if (group.groups) { + mergeSubgroups(group.groups, mergedGroup); + } + }); +} + export async function servicesResponse() { let discoveredDockerServices; let discoveredKubernetesServices; @@ -140,25 +152,29 @@ export async function servicesResponse() { const definedLayouts = initialSettings.layout ? Object.keys(initialSettings.layout) : null; mergedGroupsNames.forEach((groupName) => { - const discoveredDockerGroup = discoveredDockerServices.find((group) => group.name === groupName) || { + const discoveredDockerGroup = findGroupByName(discoveredDockerServices, groupName) || { services: [], }; - const discoveredKubernetesGroup = discoveredKubernetesServices.find((group) => group.name === groupName) || { + const discoveredKubernetesGroup = findGroupByName(discoveredKubernetesServices, groupName) || { services: [], }; - const configuredGroup = configuredServices.find((group) => group.name === groupName) || { services: [] }; + const configuredGroup = findGroupByName(configuredServices, groupName) || { services: [] }; const mergedGroup = { name: groupName, services: [...discoveredDockerGroup.services, ...discoveredKubernetesGroup.services, ...configuredGroup.services] .filter((service) => service) .sort(compareServices), + groups: [...configuredGroup.groups], }; if (definedLayouts) { const layoutIndex = definedLayouts.findIndex((layout) => layout === mergedGroup.name); if (layoutIndex > -1) sortedGroups[layoutIndex] = mergedGroup; - else unsortedGroups.push(mergedGroup); + else if (configuredGroup.name) { + // this is a nested group, so find the parent group and merge the services + mergeSubgroups(configuredServices, mergedGroup); + } else unsortedGroups.push(mergedGroup); } else { unsortedGroups.push(mergedGroup); } diff --git a/src/utils/config/service-helpers.js b/src/utils/config/service-helpers.js index e6ef6173..0f1e2c8c 100644 --- a/src/utils/config/service-helpers.js +++ b/src/utils/config/service-helpers.js @@ -13,6 +13,38 @@ import * as shvl from "utils/config/shvl"; const logger = createLogger("service-helpers"); +function parseServicesToGroups(services) { + if (!services) { + return []; + } + + // map easy to write YAML objects into easy to consume JS arrays + return services.map((serviceGroup) => { + const name = Object.keys(serviceGroup)[0]; + let groups = []; + const serviceGroupServices = []; + serviceGroup[name].forEach((entries) => { + const entryName = Object.keys(entries)[0]; + if (Array.isArray(entries[entryName])) { + groups = groups.concat(parseServicesToGroups([{ [entryName]: entries[entryName] }])); + } else { + serviceGroupServices.push({ + name: entryName, + ...entries[entryName], + weight: entries[entryName].weight || serviceGroupServices.length * 100, // default weight + type: "service", + }); + } + }); + return { + name, + type: "group", + services: serviceGroupServices, + groups, + }; + }); +} + export async function servicesFromConfig() { checkAndCopyConfig("services.yaml"); @@ -20,31 +52,7 @@ export async function servicesFromConfig() { const rawFileContents = await fs.readFile(servicesYaml, "utf8"); const fileContents = substituteEnvironmentVars(rawFileContents); const services = yaml.load(fileContents); - - if (!services) { - return []; - } - - // map easy to write YAML objects into easy to consume JS arrays - const servicesArray = services.map((servicesGroup) => ({ - name: Object.keys(servicesGroup)[0], - services: servicesGroup[Object.keys(servicesGroup)[0]].map((entries) => ({ - name: Object.keys(entries)[0], - ...entries[Object.keys(entries)[0]], - type: "service", - })), - })); - - // add default weight to services based on their position in the configuration - servicesArray.forEach((group, groupIndex) => { - group.services.forEach((service, serviceIndex) => { - if (service.weight === undefined) { - servicesArray[groupIndex].services[serviceIndex].weight = (serviceIndex + 1) * 100; - } - }); - }); - - return servicesArray; + return parseServicesToGroups(services); } export async function servicesFromDocker() { @@ -667,13 +675,30 @@ export function cleanServiceGroups(groups) { }); return cleanedService; }), + type: serviceGroup.type || "group", + groups: serviceGroup.groups ? cleanServiceGroups(serviceGroup.groups) : [], })); } +export function findGroupByName(groups, name) { + for (let i = 0; i < groups.length; i += 1) { + const group = groups[i]; + if (group.name === name) { + return group; + } else if (group.groups) { + const foundGroup = findGroupByName(group.groups, name); + if (foundGroup) { + return foundGroup; + } + } + } + return null; +} + export async function getServiceItem(group, service) { const configuredServices = await servicesFromConfig(); - const serviceGroup = configuredServices.find((g) => g.name === group); + const serviceGroup = findGroupByName(configuredServices, group); if (serviceGroup) { const serviceEntry = serviceGroup.services.find((s) => s.name === service); if (serviceEntry) return serviceEntry; @@ -681,14 +706,14 @@ export async function getServiceItem(group, service) { const discoveredServices = await servicesFromDocker(); - const dockerServiceGroup = discoveredServices.find((g) => g.name === group); + const dockerServiceGroup = findGroupByName(discoveredServices, group); if (dockerServiceGroup) { const dockerServiceEntry = dockerServiceGroup.services.find((s) => s.name === service); if (dockerServiceEntry) return dockerServiceEntry; } const kubernetesServices = await servicesFromKubernetes(); - const kubernetesServiceGroup = kubernetesServices.find((g) => g.name === group); + const kubernetesServiceGroup = findGroupByName(kubernetesServices, group); if (kubernetesServiceGroup) { const kubernetesServiceEntry = kubernetesServiceGroup.services.find((s) => s.name === service); if (kubernetesServiceEntry) return kubernetesServiceEntry; From aaf4a3e92f2e3bc6933f3a2699c3b764818ff5bd Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Wed, 27 Nov 2024 17:59:15 -0800 Subject: [PATCH 017/100] Add note --- src/utils/config/service-helpers.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/config/service-helpers.js b/src/utils/config/service-helpers.js index 0f1e2c8c..e3c491ac 100644 --- a/src/utils/config/service-helpers.js +++ b/src/utils/config/service-helpers.js @@ -681,6 +681,7 @@ export function cleanServiceGroups(groups) { } export function findGroupByName(groups, name) { + // Deep search for a group by name. Using for loop allows for early return for (let i = 0; i < groups.length; i += 1) { const group = groups[i]; if (group.name === name) { From 230da3d2ebddb9031f601a871a7483430500617f Mon Sep 17 00:00:00 2001 From: DamitusThyYeetus123 <108782125+DamitusThyYeetus123@users.noreply.github.com> Date: Thu, 28 Nov 2024 14:45:30 +1100 Subject: [PATCH 018/100] Enhancement: support hrefs for info widgets (#4347) Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com> --- src/components/widgets/widget/container.jsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/components/widgets/widget/container.jsx b/src/components/widgets/widget/container.jsx index c9240dd3..4a1fdd37 100644 --- a/src/components/widgets/widget/container.jsx +++ b/src/components/widgets/widget/container.jsx @@ -1,10 +1,13 @@ import classNames from "classnames"; +import { useContext } from "react"; import WidgetIcon from "./widget_icon"; import PrimaryText from "./primary_text"; import SecondaryText from "./secondary_text"; import Raw from "./raw"; +import { SettingsContext } from "utils/contexts/settings"; + export function getAllClasses(options, additionalClassNames = "") { if (options?.style?.header === "boxedWidgets") { if (options?.style?.cardBlur !== undefined) { @@ -56,7 +59,17 @@ export function getBottomBlock(children) { } export default function Container({ children = [], options, additionalClassNames = "" }) { - return ( + const { settings } = useContext(SettingsContext); + return options.href ? ( + + {getInnerBlock(children)} + {getBottomBlock(children)} + + ) : (
    {getInnerBlock(children)} {getBottomBlock(children)} From 6d829bce7993795ecaa7c89206afb904948f4cb5 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Wed, 27 Nov 2024 22:12:59 -0800 Subject: [PATCH 019/100] Enhancement: use css color-scheme (#4349) --- src/pages/index.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/index.jsx b/src/pages/index.jsx index 7a7fdef0..3f8ebf86 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -452,6 +452,7 @@ function Home({ initialSettings }) { } export default function Wrapper({ initialSettings, fallback }) { + const { theme } = useContext(ThemeContext); const wrappedStyle = {}; let backgroundBlur = false; let backgroundSaturate = false; @@ -482,8 +483,9 @@ export default function Wrapper({ initialSettings, fallback }) { id="page_wrapper" className={classNames( "relative", - initialSettings.theme && initialSettings.theme, + theme && theme, initialSettings.color && `theme-${initialSettings.color}`, + theme === "dark" ? "scheme-dark" : "scheme-light", )} >
    Date: Wed, 27 Nov 2024 22:49:14 -0800 Subject: [PATCH 020/100] Documentation: doc updates for nesting, reorganizing, fixes --- docs/configs/info-widgets.md | 24 +++++++++++ docs/configs/service-widgets.md | 58 ------------------------- docs/configs/services.md | 71 +++++++++++++++++++++++++++++++ docs/configs/settings.md | 29 +++++++++++-- docs/troubleshooting/index.md | 2 +- docs/widgets/authoring/proxies.md | 2 +- docs/widgets/index.md | 4 ++ docs/widgets/info/openmeteo.md | 2 +- docs/widgets/info/weather.md | 22 ---------- mkdocs.yml | 4 +- src/skeleton/services.yaml | 2 +- src/skeleton/settings.yaml | 2 +- src/skeleton/widgets.yaml | 2 +- 13 files changed, 132 insertions(+), 92 deletions(-) create mode 100644 docs/configs/info-widgets.md delete mode 100644 docs/configs/service-widgets.md delete mode 100644 docs/widgets/info/weather.md diff --git a/docs/configs/info-widgets.md b/docs/configs/info-widgets.md new file mode 100644 index 00000000..76314396 --- /dev/null +++ b/docs/configs/info-widgets.md @@ -0,0 +1,24 @@ +--- +title: Information Widgets +description: Homepage info widgets. +--- + +Information widgets are widgets that provide information about your system or environment and are displayed at the top of the homepage. You can find a list of all available info widgets under the [Info Widgets](../widgets/info/index.md) section. + +Info widgets are defined in the widgets.yaml + +Each widget has its own configuration options, which are detailed in the widget's documentation. + +## Layout + +Info widgets are displayed in the order they are defined in the `widgets.yaml` file. You can change the order by moving the widgets around in the file. However, some widgets (weather, search and datetime) are aligned to the right side of the screen which can affect the layout of the widgets. + +## Adding A Link + +You can add a link to an info widget such as the logo or text widgets by adding an `href` option, for example: + +```yaml +logo: + href: https://example.com + target: _blank # Optional, can be set in settings +``` diff --git a/docs/configs/service-widgets.md b/docs/configs/service-widgets.md deleted file mode 100644 index df696f61..00000000 --- a/docs/configs/service-widgets.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -title: Service Widgets -description: Service Widget Configuration ---- - -Unless otherwise noted, URLs should not end with a `/` or other API path. Each widget will handle the path on its own. - -Each service can have widgets attached to it (often matching the service type, but that's not forced). - -In addition to the href of the service, you can also specify the target location in which to open that link. See [Link Target](settings.md#link-target) for more details. - -Using Emby as an example, this is how you would attach the Emby service widget. - -```yaml -- Emby: - icon: emby.png - href: http://emby.host.or.ip/ - description: Movies & TV Shows - widget: - type: emby - url: http://emby.host.or.ip - key: apikeyapikeyapikeyapikeyapikey -``` - -## Multiple Widgets - -Each service can have multiple widgets attached to it, for example: - -```yaml -- Emby: - icon: emby.png - href: http://emby.host.or.ip/ - description: Movies & TV Shows - widgets: - - type: emby - url: http://emby.host.or.ip - key: apikeyapikeyapikeyapikeyapikey - - type: uptimekuma - url: http://uptimekuma.host.or.ip:port - slug: statuspageslug -``` - -## Field Visibility - -Each widget can optionally provide a list of which fields should be visible via the `fields` widget property. If no fields are specified, then all fields will be displayed. The `fields` property must be a valid YAML array of strings. As an example, here is the entry for Sonarr showing only a couple of fields. - -**In all cases a widget will work and display all fields without specifying the `fields` property.** - -```yaml -- Sonarr: - icon: sonarr.png - href: http://sonarr.host.or.ip - widget: - type: sonarr - fields: ["wanted", "queued"] - url: http://sonarr.host.or.ip - key: apikeyapikeyapikeyapikeyapikey -``` diff --git a/docs/configs/services.md b/docs/configs/services.md index 9cb75177..6ef25c39 100644 --- a/docs/configs/services.md +++ b/docs/configs/services.md @@ -21,6 +21,23 @@ Groups are defined as top-level array entries. Service Groups +### Nested Groups + +Groups can be nested by using the same format as the top-level groups. + +```yaml +- Group A: + - Service A: + href: http://localhost/ + + - Group B: + - Service B: + href: http://localhost/ + + - Service C: + href: http://localhost/ +``` + ## Services Services are defined as array entries on groups, @@ -43,6 +60,60 @@ Services are defined as array entries on groups, Service Services +### Service Widgets + +Each service can have widgets attached to it (often matching the service type, but that's not forced). + +In addition to the href of the service, you can also specify the target location in which to open that link. See [Link Target](settings.md#link-target) for more details. + +Using Emby as an example, this is how you would attach the Emby service widget. + +```yaml +- Emby: + icon: emby.png + href: http://emby.host.or.ip/ + description: Movies & TV Shows + widget: + type: emby + url: http://emby.host.or.ip + key: apikeyapikeyapikeyapikeyapikey +``` + +#### Multiple Widgets + +Each service can have multiple widgets attached to it, for example: + +```yaml +- Emby: + icon: emby.png + href: http://emby.host.or.ip/ + description: Movies & TV Shows + widgets: + - type: emby + url: http://emby.host.or.ip + key: apikeyapikeyapikeyapikeyapikey + - type: uptimekuma + url: http://uptimekuma.host.or.ip:port + slug: statuspageslug +``` + +#### Field Visibility + +Each widget can optionally provide a list of which fields should be visible via the `fields` widget property. If no fields are specified, then all fields will be displayed. The `fields` property must be a valid YAML array of strings. As an example, here is the entry for Sonarr showing only a couple of fields. + +**In all cases a widget will work and display all fields without specifying the `fields` property.** + +```yaml +- Sonarr: + icon: sonarr.png + href: http://sonarr.host.or.ip + widget: + type: sonarr + fields: ["wanted", "queued"] + url: http://sonarr.host.or.ip + key: apikeyapikeyapikeyapikeyapikey +``` + ## Descriptions Services may have descriptions, diff --git a/docs/configs/settings.md b/docs/configs/settings.md index 2f387a65..7e1815bb 100644 --- a/docs/configs/settings.md +++ b/docs/configs/settings.md @@ -137,6 +137,27 @@ layout: columns: 3 ``` +### Nested Groups + +If your services config has nested groups, you can apply settings to these groups by nesting them in the layout block +and using the same settings. For example + +```yaml +layout: + Group A: + style: row + columns: 4 + Group C: + style: row + columns: 2 + Nested Group A: + style: row + columns: 2 + Nested Group B: + style: row + columns: 2 +``` + ### Headers You can hide headers for each section in the layout as well by passing `header` as false, like so: @@ -348,12 +369,12 @@ This can also be set for individual services. Note setting this at the service l ## Providers -The `providers` section allows you to define shared API provider options and secrets. Currently this allows you to define your weather API keys in secret and is also the location of the Longhorn URL and credentials. +The `providers` section allows you to define shared API provider options and secrets. ```yaml providers: openweathermap: openweathermapapikey - weatherapi: weatherapiapikey + finnhub: yourfinnhubapikeyhere longhorn: url: https://longhorn.example.com username: admin @@ -363,10 +384,10 @@ providers: You can then pass `provider` instead of `apiKey` in your widget configuration. ```yaml -- weatherapi: +- openweathermap: latitude: 50.449684 longitude: 30.525026 - provider: weatherapi + provider: openweathermap ``` ## Quick Launch diff --git a/docs/troubleshooting/index.md b/docs/troubleshooting/index.md index 82a7381c..bbde4cf3 100644 --- a/docs/troubleshooting/index.md +++ b/docs/troubleshooting/index.md @@ -17,7 +17,7 @@ hide: All service widgets work essentially the same, that is, homepage makes a proxied call to an API made available by that service. The majority of the time widgets don't work it is a configuration issue. Of course, sometimes things do break. Some basic steps to try: -1. Ensure that you follow the rule mentioned on https://gethomepage.dev/configs/service-widgets/. **Unless otherwise noted, URLs should not end with a / or other API path. Each widget will handle the path on its own.**. This is very important as including a trailing slash can result in an error. +1. **URLs should not end with a / or other API path. Each widget will handle the path on its own.**. Including a trailing slash can result in an error. 2. Verify the homepage installation can connect to the IP address or host you are using for the widget `url`. This is most simply achieved by pinging the server from the homepage machine, in Docker this means _from inside the container_ itself, e.g.: diff --git a/docs/widgets/authoring/proxies.md b/docs/widgets/authoring/proxies.md index 15cdb670..a8b8073e 100644 --- a/docs/widgets/authoring/proxies.md +++ b/docs/widgets/authoring/proxies.md @@ -50,7 +50,7 @@ You can also pass API keys from the widget configuration to the proxy handler, f ### `credentialedProxyHandler` -A proxy handler that makes authenticated by setting request headers. Credentials are pulled from the widgets configuration. +A proxy handler that makes authenticated requests by setting request headers. Credentials are pulled from the widgets configuration. By default the key is passed as an `X-API-Key` header. If you need to pass the key as something else, either add a case to the credentialedProxyHandler or create a new proxy handler. diff --git a/docs/widgets/index.md b/docs/widgets/index.md index 4bd45af7..fbb8edc6 100644 --- a/docs/widgets/index.md +++ b/docs/widgets/index.md @@ -28,6 +28,8 @@ Service widgets are used to display the status of a service, often a web service slug: aaaaaaabbbbb ``` +More detail on configuring service widgets can be found in the [Service Widgets Config](../configs/services.md) section. + ## Info Widgets Info widgets are used to display information in the header, often about your system or environment. Info widgets are defined your `widgets.yaml` file. Here's an example: @@ -39,3 +41,5 @@ Info widgets are used to display information in the header, often about your sys longitude: -117.51 cache: 5 ``` + +More detail on configuring info widgets can be found in the [Info Widgets Config](../configs/info-widgets.md) section. diff --git a/docs/widgets/info/openmeteo.md b/docs/widgets/info/openmeteo.md index fb5bb171..ec84ab17 100644 --- a/docs/widgets/info/openmeteo.md +++ b/docs/widgets/info/openmeteo.md @@ -3,7 +3,7 @@ title: Open-Meteo description: Open-Meteo Information Widget Configuration --- -No registration is required at all! See [https://open-meteo.com/en/docs](https://open-meteo.com/en/docs) +Homepage's recommended weather widget. No registration is required at all! See [https://open-meteo.com/en/docs](https://open-meteo.com/en/docs) ```yaml - openmeteo: diff --git a/docs/widgets/info/weather.md b/docs/widgets/info/weather.md deleted file mode 100644 index ab13b673..00000000 --- a/docs/widgets/info/weather.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -title: Weather API -description: Weather API Information Widget Configuration ---- - -**Note: this widget is considered 'deprecated' since there is no longer a free Weather API tier for new members. See the openmeteo or openweathermap widgets for alternatives.** - -The free tier is all that's required, you will need to [register](https://www.weatherapi.com/signup.aspx) and grab your API key. - -```yaml -- weatherapi: - label: Kyiv # optional - latitude: 50.449684 - longitude: 30.525026 - units: metric # or imperial - apiKey: yourweatherapikey - cache: 5 # Time in minutes to cache API responses, to stay within limits - format: # optional, Intl.NumberFormat options - maximumFractionDigits: 1 -``` - -You can optionally not pass a `latitude` and `longitude` and the widget will use your current location (requires a secure context, eg. HTTPS). diff --git a/mkdocs.yml b/mkdocs.yml index a19d3b83..fa2188ad 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -21,8 +21,8 @@ nav: - configs/index.md - configs/settings.md - configs/bookmarks.md + - configs/info-widgets.md - configs/services.md - - configs/service-widgets.md - configs/kubernetes.md - configs/docker.md - configs/custom-css-js.md @@ -142,6 +142,7 @@ nav: - widgets/services/spoolman.md - widgets/services/stash.md - widgets/services/stocks.md + - widgets/services/suwayomi.md - widgets/services/swagdashboard.md - widgets/services/syncthing-relay-server.md - widgets/services/tailscale.md @@ -177,7 +178,6 @@ nav: - widgets/info/search.md - widgets/info/stocks.md - widgets/info/unifi_controller.md - - widgets/info/weather.md - "Learn": - widgets/authoring/index.md - "Getting Started": widgets/authoring/getting-started.md diff --git a/src/skeleton/services.yaml b/src/skeleton/services.yaml index 77626b1c..39b37926 100644 --- a/src/skeleton/services.yaml +++ b/src/skeleton/services.yaml @@ -1,6 +1,6 @@ --- # For configuration options and examples, please see: -# https://gethomepage.dev/configs/services +# https://gethomepage.dev/configs/services/ - My First Group: - My First Service: diff --git a/src/skeleton/settings.yaml b/src/skeleton/settings.yaml index 141053f5..2e828c08 100644 --- a/src/skeleton/settings.yaml +++ b/src/skeleton/settings.yaml @@ -1,6 +1,6 @@ --- # For configuration options and examples, please see: -# https://gethomepage.dev/configs/settings +# https://gethomepage.dev/configs/settings/ providers: openweathermap: openweathermapapikey diff --git a/src/skeleton/widgets.yaml b/src/skeleton/widgets.yaml index 23c8d613..b1cf0f55 100644 --- a/src/skeleton/widgets.yaml +++ b/src/skeleton/widgets.yaml @@ -1,6 +1,6 @@ --- # For configuration options and examples, please see: -# https://gethomepage.dev/configs/service-widgets +# https://gethomepage.dev/configs/info-widgets/ - resources: cpu: true From 5cc487a96ddd6327d2d7a0d6acfabc3d5f960e59 Mon Sep 17 00:00:00 2001 From: zombaru <16330202+zombaru@users.noreply.github.com> Date: Thu, 28 Nov 2024 20:15:28 -0800 Subject: [PATCH 021/100] Documentation: Add missing admonition type to UniFi docs (#4353) --- docs/widgets/info/unifi_controller.md | 2 +- docs/widgets/services/unifi-controller.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/widgets/info/unifi_controller.md b/docs/widgets/info/unifi_controller.md index b77d8ed0..e16ca40b 100644 --- a/docs/widgets/info/unifi_controller.md +++ b/docs/widgets/info/unifi_controller.md @@ -7,7 +7,7 @@ _(Find the Unifi Controller service widget [here](../services/unifi-controller.m You can display general connectivity status from your Unifi (Network) Controller. -!!! +!!! warning When authenticating you will want to use a local account that has at least read privileges. diff --git a/docs/widgets/services/unifi-controller.md b/docs/widgets/services/unifi-controller.md index d137c2a9..c5efc688 100644 --- a/docs/widgets/services/unifi-controller.md +++ b/docs/widgets/services/unifi-controller.md @@ -9,7 +9,7 @@ _(Find the Unifi Controller information widget [here](../info/unifi_controller.m You can display general connectivity status from your Unifi (Network) Controller. -!!! +!!! warning When authenticating you will want to use a local account that has at least read privileges. From 276a1c3ef423f7b027a84ad5c8ac7ef30d04ca2f Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 28 Nov 2024 21:54:22 -0800 Subject: [PATCH 022/100] Chore: better tailscale error handling --- src/widgets/tailscale/component.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/widgets/tailscale/component.jsx b/src/widgets/tailscale/component.jsx index 3929b2ed..d3c937d5 100644 --- a/src/widgets/tailscale/component.jsx +++ b/src/widgets/tailscale/component.jsx @@ -11,8 +11,8 @@ export default function Component({ service }) { const { data: statsData, error: statsError } = useWidgetAPI(widget, "device"); - if (statsError) { - return ; + if (statsError || statsData?.message) { + return ; } if (!statsData) { From a28952ce698e330c9cfef55289acf8c5cc30efe6 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Fri, 29 Nov 2024 10:14:53 -0800 Subject: [PATCH 023/100] Chore: move custom css loading, add letter-spacing (#4359) --- src/pages/_document.jsx | 2 ++ src/pages/index.jsx | 2 -- src/styles/globals.css | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/_document.jsx b/src/pages/_document.jsx index bfe3fc93..e69ca007 100644 --- a/src/pages/_document.jsx +++ b/src/pages/_document.jsx @@ -10,6 +10,8 @@ export default function Document() { /> + + {/* eslint-disable-line @next/next/no-css-tags */}
    diff --git a/src/pages/index.jsx b/src/pages/index.jsx index 3f8ebf86..0bdc78b6 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -374,8 +374,6 @@ function Home({ initialSettings }) { )} - - {/* eslint-disable-line @next/next/no-css-tags */}