From 7b7740563eb471092b9e2b3e7830f02c54866012 Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+shamoon@users.noreply.github.com> Date: Sat, 22 Oct 2022 22:48:25 -0700 Subject: [PATCH 1/8] Basic widget data validation --- public/locales/en/common.json | 6 ++- src/components/services/widget/error.jsx | 48 ++++++++++++++++++++++++ src/utils/proxy/handlers/credentialed.js | 5 +++ src/utils/proxy/handlers/generic.js | 7 ++++ src/utils/proxy/http.js | 2 +- src/utils/proxy/validate-widget-data.js | 22 +++++++++++ 6 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 src/components/services/widget/error.jsx create mode 100644 src/utils/proxy/validate-widget-data.js diff --git a/public/locales/en/common.json b/public/locales/en/common.json index cc946e783..c4b41f057 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -13,7 +13,11 @@ "widget": { "missing_type": "Missing Widget Type: {{type}}", "api_error": "API Error", - "status": "Status" + "status": "Status", + "debug_info": "Debug Information", + "url": "URL", + "raw_error": "Raw Error", + "response_data": "Response Data" }, "weather": { "current": "Current Location", diff --git a/src/components/services/widget/error.jsx b/src/components/services/widget/error.jsx new file mode 100644 index 000000000..a6d400e8d --- /dev/null +++ b/src/components/services/widget/error.jsx @@ -0,0 +1,48 @@ +import { useTranslation } from "react-i18next"; + +function displayError(error) { + return JSON.stringify(error[1] ? error[1] : error, null, 4); +} + +function displayData(data) { + return (data.type === 'Buffer') ? Buffer.from(data).toString() : JSON.stringify(data, 4); +} + +export default function Error({ error }) { + const { t } = useTranslation(); + + if (error?.data?.error) { + error = error.data.error; // eslint-disable-line no-param-reassign + } + + return ( +
+
Something went wrong.
+
+ {t("widget.debug_info")} +
+
    +
  • + {t("widget.api_error")}: {error.message} +
  • + {error.url &&
  • + {t("widget.url")}: {error.url} +
  • } + {error.rawError &&
  • + {t("widget.raw_error")}: +
    + {displayError(error.rawError)} +
    +
  • } + {error.data &&
  • + {t("widget.response_data")}: +
    + {displayData(error.data)} +
    +
  • } +
+
+
+
+ ); +} diff --git a/src/utils/proxy/handlers/credentialed.js b/src/utils/proxy/handlers/credentialed.js index 54c393b17..f1436d511 100644 --- a/src/utils/proxy/handlers/credentialed.js +++ b/src/utils/proxy/handlers/credentialed.js @@ -1,5 +1,6 @@ import getServiceWidget from "utils/config/service-helpers"; import { formatApiCall } from "utils/proxy/api-helpers"; +import validateWidgetData from "utils/proxy/validate-widget-data"; import { httpProxy } from "utils/proxy/http"; import createLogger from "utils/logger"; import widgets from "widgets/widgets"; @@ -54,6 +55,10 @@ export default async function credentialedProxyHandler(req, res) { logger.debug("HTTP Error %d calling %s//%s%s...", status, url.protocol, url.hostname, url.pathname); } + if (!validateWidgetData(widget, endpoint, data)) { + return res.status(500).json({error: {message: "Invalid data", url, data}}); + } + if (contentType) res.setHeader("Content-Type", contentType); return res.status(status).send(data); } diff --git a/src/utils/proxy/handlers/generic.js b/src/utils/proxy/handlers/generic.js index f93c83f2f..02c3d4c38 100644 --- a/src/utils/proxy/handlers/generic.js +++ b/src/utils/proxy/handlers/generic.js @@ -1,5 +1,6 @@ import getServiceWidget from "utils/config/service-helpers"; import { formatApiCall } from "utils/proxy/api-helpers"; +import validateWidgetData from "utils/proxy/validate-widget-data"; import { httpProxy } from "utils/proxy/http"; import createLogger from "utils/logger"; import widgets from "widgets/widgets"; @@ -32,6 +33,11 @@ export default async function genericProxyHandler(req, res, map) { }); let resultData = data; + + if (!validateWidgetData(widget, endpoint, resultData)) { + return res.status(status).json({error: {message: "Invalid data", url, data: resultData}}); + } + if (status === 200 && map) { resultData = map(data); } @@ -44,6 +50,7 @@ export default async function genericProxyHandler(req, res, map) { if (status >= 400) { logger.debug("HTTP Error %d calling %s//%s%s...", status, url.protocol, url.hostname, url.pathname); + return res.status(status).json({error: {message: "HTTP Error", url, data}}); } return res.status(status).send(resultData); diff --git a/src/utils/proxy/http.js b/src/utils/proxy/http.js index 4eba83f32..93538202d 100644 --- a/src/utils/proxy/http.js +++ b/src/utils/proxy/http.js @@ -98,6 +98,6 @@ export async function httpProxy(url, params = {}) { catch (err) { logger.error("Error calling %s//%s%s...", url.protocol, url.hostname, url.pathname); logger.error(err); - return [500, "application/json", { error: "Unexpected error" }, null]; + return [500, "application/json", { error: {message: err?.message ?? "Unknown error", url, rawError: err} }, null]; } } diff --git a/src/utils/proxy/validate-widget-data.js b/src/utils/proxy/validate-widget-data.js new file mode 100644 index 000000000..a9664363c --- /dev/null +++ b/src/utils/proxy/validate-widget-data.js @@ -0,0 +1,22 @@ +import widgets from "widgets/widgets"; + +export default function validateWidgetData(widget, endpoint, data) { + let valid = true; + let dataParsed; + try { + dataParsed = JSON.parse(data); + } catch (e) { + valid = false; + } + + if (dataParsed) { + const validate = widgets[widget.type]?.mappings?.[endpoint]?.validate; + validate.forEach(key => { + if (dataParsed[key] === undefined) { + valid = false; + } + }); + } + + return valid; +} From 21017e4716b1b79fcfa39ac0e60703731cb473ba Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+shamoon@users.noreply.github.com> Date: Sat, 22 Oct 2022 23:00:51 -0700 Subject: [PATCH 2/8] Add detailed Error component for service widgets --- src/components/services/widget/container.jsx | 8 +++----- src/widgets/adguard/component.jsx | 6 +++--- src/widgets/authentik/component.jsx | 5 +++-- src/widgets/bazarr/component.jsx | 5 +++-- src/widgets/changedetectionio/component.jsx | 8 ++++---- src/widgets/coinmarketcap/component.jsx | 5 +++-- src/widgets/docker/component.jsx | 5 +++-- src/widgets/emby/component.jsx | 5 +++-- src/widgets/gotify/component.jsx | 9 +++------ src/widgets/jackett/component.jsx | 5 +++-- src/widgets/jellyseerr/component.jsx | 9 +++------ src/widgets/lidarr/component.jsx | 5 +++-- src/widgets/mastodon/component.jsx | 5 +++-- src/widgets/npm/component.jsx | 8 ++------ src/widgets/nzbget/component.jsx | 5 +++-- src/widgets/ombi/component.jsx | 9 +++------ src/widgets/overseerr/component.jsx | 9 +++------ src/widgets/pihole/component.jsx | 5 +++-- src/widgets/plex/component.jsx | 5 +++-- src/widgets/plex/proxy.js | 7 ++++++- src/widgets/portainer/component.jsx | 5 +++-- src/widgets/prowlarr/component.jsx | 9 +++------ src/widgets/proxmox/component.jsx | 5 +++-- src/widgets/qbittorrent/component.jsx | 5 +++-- src/widgets/radarr/component.jsx | 9 +++------ src/widgets/readarr/component.jsx | 5 +++-- src/widgets/rutorrent/component.jsx | 5 +++-- src/widgets/sabnzbd/component.jsx | 5 +++-- src/widgets/sonarr/component.jsx | 9 +++------ src/widgets/speedtest/component.jsx | 5 +++-- src/widgets/strelaysrv/component.jsx | 5 +++-- src/widgets/tautulli/component.jsx | 7 ++++--- src/widgets/traefik/component.jsx | 9 +++------ src/widgets/transmission/component.jsx | 5 +++-- src/widgets/transmission/proxy.js | 1 + src/widgets/unifi/component.jsx | 3 ++- src/widgets/unifi/proxy.js | 6 ++++-- 37 files changed, 113 insertions(+), 113 deletions(-) diff --git a/src/components/services/widget/container.jsx b/src/components/services/widget/container.jsx index 60536e86b..945b8f6f1 100644 --- a/src/components/services/widget/container.jsx +++ b/src/components/services/widget/container.jsx @@ -1,10 +1,8 @@ +import Error from "./error"; + export default function Container({ error = false, children, service }) { if (error) { - return ( -
-
{error}
-
- ); + return } let visibleChildren = children; diff --git a/src/widgets/adguard/component.jsx b/src/widgets/adguard/component.jsx index 0c78113d0..3147ffcf8 100644 --- a/src/widgets/adguard/component.jsx +++ b/src/widgets/adguard/component.jsx @@ -11,10 +11,10 @@ export default function Component({ service }) { const { data: adguardData, error: adguardError } = useWidgetAPI(widget, "stats"); - if (adguardError) { - return ; + if (adguardError || adguardData?.error) { + const finalError = adguardError ?? adguardData.error; + return ; } - if (!adguardData) { return ( diff --git a/src/widgets/authentik/component.jsx b/src/widgets/authentik/component.jsx index 31f864d17..d4aa8dba7 100644 --- a/src/widgets/authentik/component.jsx +++ b/src/widgets/authentik/component.jsx @@ -13,8 +13,9 @@ export default function Component({ service }) { const { data: loginsData, error: loginsError } = useWidgetAPI(widget, "login"); const { data: failedLoginsData, error: failedLoginsError } = useWidgetAPI(widget, "login_failed"); - if (usersError || loginsError || failedLoginsError) { - return ; + if (usersError || usersData?.error || loginsError || loginsData?.error || failedLoginsError || failedLoginsData?.error) { + const finalError = usersError ?? usersData?.error ?? loginsError ?? loginsData?.error ?? failedLoginsError ?? failedLoginsData?.error; + return ; } if (!usersData || !loginsData || !failedLoginsData) { diff --git a/src/widgets/bazarr/component.jsx b/src/widgets/bazarr/component.jsx index 24fef1ce2..070f82326 100644 --- a/src/widgets/bazarr/component.jsx +++ b/src/widgets/bazarr/component.jsx @@ -12,8 +12,9 @@ export default function Component({ service }) { const { data: episodesData, error: episodesError } = useWidgetAPI(widget, "episodes"); const { data: moviesData, error: moviesError } = useWidgetAPI(widget, "movies"); - if (episodesError || moviesError) { - return ; + if (moviesError || moviesData?.error || episodesError || episodesData?.error) { + const finalError = moviesError ?? moviesData?.error ?? episodesError ?? episodesData?.error; + return ; } if (!episodesData || !moviesData) { diff --git a/src/widgets/changedetectionio/component.jsx b/src/widgets/changedetectionio/component.jsx index 70936489f..ce29691f2 100644 --- a/src/widgets/changedetectionio/component.jsx +++ b/src/widgets/changedetectionio/component.jsx @@ -9,12 +9,12 @@ export default function Component({ service }) { const { widget } = service; - const { data } = useWidgetAPI(widget, "info"); + const { data, error } = useWidgetAPI(widget, "info"); - if (!data) { - return ; + if (error || data?.error) { + const finalError = error ?? data.error; + return ; } - const totalObserved = Object.keys(data).length; let diffsDetected = 0; diff --git a/src/widgets/coinmarketcap/component.jsx b/src/widgets/coinmarketcap/component.jsx index 0b970b315..fa5844bdf 100644 --- a/src/widgets/coinmarketcap/component.jsx +++ b/src/widgets/coinmarketcap/component.jsx @@ -36,8 +36,9 @@ export default function Component({ service }) { ); } - if (statsError) { - return ; + if (statsError || statsData?.error) { + const finalError = statsError ?? statsData.error; + return ; } if (!statsData || !dateRange) { diff --git a/src/widgets/docker/component.jsx b/src/widgets/docker/component.jsx index 542fbde75..bdc49be38 100644 --- a/src/widgets/docker/component.jsx +++ b/src/widgets/docker/component.jsx @@ -17,8 +17,9 @@ export default function Component({ service }) { const { data: statsData, error: statsError } = useSWR(`/api/docker/stats/${widget.container}/${widget.server || ""}`); - if (statsError || statusError) { - return ; + if (statsError || statsData?.error || statusError || statusData?.error) { + const finalError = statsError ?? statsData?.error ?? statusError ?? statusData?.error; + return ; } if (statusData && statusData.status !== "running") { diff --git a/src/widgets/emby/component.jsx b/src/widgets/emby/component.jsx index 59d66c056..26371c814 100644 --- a/src/widgets/emby/component.jsx +++ b/src/widgets/emby/component.jsx @@ -3,8 +3,8 @@ import { useTranslation } from "next-i18next"; import { BsVolumeMuteFill, BsFillPlayFill, BsPauseFill, BsCpu, BsFillCpuFill } from "react-icons/bs"; import { MdOutlineSmartDisplay } from "react-icons/md"; -import Container from "components/services/widget/container"; import { formatProxyUrl, formatProxyUrlWithSegments } from "utils/proxy/api-helpers"; +import Container from "components/services/widget/container"; function ticksToTime(ticks) { const milliseconds = ticks / 10000; @@ -172,7 +172,8 @@ export default function Component({ service }) { } if (sessionsError || sessionsData?.error) { - return ; + const finalError = sessionsError ?? sessionsData.error; + return ; } if (!sessionsData) { diff --git a/src/widgets/gotify/component.jsx b/src/widgets/gotify/component.jsx index 40f5793bf..cedc3f84a 100644 --- a/src/widgets/gotify/component.jsx +++ b/src/widgets/gotify/component.jsx @@ -1,20 +1,17 @@ -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: appsData, error: appsError } = useWidgetAPI(widget, "application"); const { data: messagesData, error: messagesError } = useWidgetAPI(widget, "message"); const { data: clientsData, error: clientsError } = useWidgetAPI(widget, "client"); - if (appsError || messagesError || clientsError) { - return ; + if (appsError || appsData?.error || messagesError || messagesData?.error || clientsError || clientsData?.error) { + const finalError = appsError ?? appsData?.error ?? messagesError ?? messagesData?.error ?? clientsError ?? clientsData?.error; + return ; } diff --git a/src/widgets/jackett/component.jsx b/src/widgets/jackett/component.jsx index 9629e2669..e9fcd95a8 100644 --- a/src/widgets/jackett/component.jsx +++ b/src/widgets/jackett/component.jsx @@ -11,8 +11,9 @@ export default function Component({ service }) { const { data: indexersData, error: indexersError } = useWidgetAPI(widget, "indexers"); - if (indexersError) { - return ; + if (indexersError || indexersData?.error) { + const finalError = indexersError ?? indexersData.error; + return ; } if (!indexersData) { diff --git a/src/widgets/jellyseerr/component.jsx b/src/widgets/jellyseerr/component.jsx index 217e406eb..aece8560f 100644 --- a/src/widgets/jellyseerr/component.jsx +++ b/src/widgets/jellyseerr/component.jsx @@ -1,18 +1,15 @@ -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: statsData, error: statsError } = useWidgetAPI(widget, "request/count"); - if (statsError) { - return ; + if (statsError || statsData?.error) { + const finalError = statsError ?? statsData.error; + return ; } if (!statsData) { diff --git a/src/widgets/lidarr/component.jsx b/src/widgets/lidarr/component.jsx index 343760e78..5951e094c 100644 --- a/src/widgets/lidarr/component.jsx +++ b/src/widgets/lidarr/component.jsx @@ -13,8 +13,9 @@ export default function Component({ service }) { const { data: wantedData, error: wantedError } = useWidgetAPI(widget, "wanted/missing"); const { data: queueData, error: queueError } = useWidgetAPI(widget, "queue/status"); - if (albumsError || wantedError || queueError) { - return ; + if (albumsError || albumsData?.error || wantedError || wantedData?.error || queueError || queueData?.error) { + const finalError = albumsError ?? albumsData?.error ?? wantedError ?? wantedData?.error ?? queueError ?? queueData?.error; + return ; } if (!albumsData || !wantedData || !queueData) { diff --git a/src/widgets/mastodon/component.jsx b/src/widgets/mastodon/component.jsx index ec12fca11..477d648ec 100644 --- a/src/widgets/mastodon/component.jsx +++ b/src/widgets/mastodon/component.jsx @@ -11,8 +11,9 @@ export default function Component({ service }) { const { data: statsData, error: statsError } = useWidgetAPI(widget, "instance"); - if (statsError) { - return ; + if (statsError || statsData?.error) { + const finalError = statsError ?? statsData.error; + return ; } if (!statsData) { diff --git a/src/widgets/npm/component.jsx b/src/widgets/npm/component.jsx index 92aef0359..b6d42ad98 100644 --- a/src/widgets/npm/component.jsx +++ b/src/widgets/npm/component.jsx @@ -1,18 +1,14 @@ -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: infoData, error: infoError } = useWidgetAPI(widget, "nginx/proxy-hosts"); - if (infoError || infoData?.error) { - return ; + if (infoError) { + return ; } if (!infoData) { diff --git a/src/widgets/nzbget/component.jsx b/src/widgets/nzbget/component.jsx index f9ace707a..92408982a 100644 --- a/src/widgets/nzbget/component.jsx +++ b/src/widgets/nzbget/component.jsx @@ -11,8 +11,9 @@ export default function Component({ service }) { const { data: statusData, error: statusError } = useWidgetAPI(widget, "status"); - if (statusError) { - return ; + if (statusError || statusData?.error) { + const finalError = statusError ?? statusData.error; + return ; } if (!statusData) { diff --git a/src/widgets/ombi/component.jsx b/src/widgets/ombi/component.jsx index 60128c379..a5a0a0b73 100644 --- a/src/widgets/ombi/component.jsx +++ b/src/widgets/ombi/component.jsx @@ -1,18 +1,15 @@ -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: statsData, error: statsError } = useWidgetAPI(widget, "Request/count"); - if (statsError) { - return ; + if (statsError || statsData?.error) { + const finalError = statsError ?? statsData.error; + return ; } if (!statsData) { diff --git a/src/widgets/overseerr/component.jsx b/src/widgets/overseerr/component.jsx index 47131f6ee..f0269b4f1 100644 --- a/src/widgets/overseerr/component.jsx +++ b/src/widgets/overseerr/component.jsx @@ -1,18 +1,15 @@ -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: statsData, error: statsError } = useWidgetAPI(widget, "request/count"); - if (statsError) { - return ; + if (statsError || statsData?.error) { + const finalError = statsError ?? statsData.error; + return ; } if (!statsData) { diff --git a/src/widgets/pihole/component.jsx b/src/widgets/pihole/component.jsx index 17a186271..cb77cc2b6 100644 --- a/src/widgets/pihole/component.jsx +++ b/src/widgets/pihole/component.jsx @@ -11,8 +11,9 @@ export default function Component({ service }) { const { data: piholeData, error: piholeError } = useWidgetAPI(widget, "api.php"); - if (piholeError) { - return ; + if (piholeError || piholeData?.error) { + const finalError = piholeError ?? piholeData.error; + return ; } if (!piholeData) { diff --git a/src/widgets/plex/component.jsx b/src/widgets/plex/component.jsx index 9de60fd1e..cf6d705cf 100644 --- a/src/widgets/plex/component.jsx +++ b/src/widgets/plex/component.jsx @@ -14,8 +14,9 @@ export default function Component({ service }) { refreshInterval: 5000, }); - if (plexAPIError) { - return ; + if (plexAPIError || plexData?.error) { + const finalError = plexAPIError ?? plexData.error; + return ; } if (!plexData) { diff --git a/src/widgets/plex/proxy.js b/src/widgets/plex/proxy.js index 46ebb27c1..c016023dd 100644 --- a/src/widgets/plex/proxy.js +++ b/src/widgets/plex/proxy.js @@ -44,7 +44,7 @@ async function fetchFromPlexAPI(endpoint, widget) { if (status !== 200) { logger.error("HTTP %d communicating with Plex. Data: %s", status, data.toString()); - return [status, data.toString()]; + return [status, data]; } try { @@ -65,6 +65,11 @@ export default async function plexProxyHandler(req, res) { logger.debug("Getting streams from Plex API"); let streams; let [status, apiData] = await fetchFromPlexAPI("/status/sessions", widget); + + if (status !== 200) { + return res.status(status).json({error: {message: "HTTP error communicating with Plex API", data: apiData}}); + } + if (apiData && apiData.MediaContainer) { streams = apiData.MediaContainer._attributes.size; } diff --git a/src/widgets/portainer/component.jsx b/src/widgets/portainer/component.jsx index bd44d77e0..87c173dd8 100644 --- a/src/widgets/portainer/component.jsx +++ b/src/widgets/portainer/component.jsx @@ -13,8 +13,9 @@ export default function Component({ service }) { all: 1, }); - if (containersError) { - return ; + if (containersError || containersData?.error) { + const finalError = containersError ?? containersData.error; + return ; } if (!containersData) { diff --git a/src/widgets/prowlarr/component.jsx b/src/widgets/prowlarr/component.jsx index bb0825193..6649fdefd 100644 --- a/src/widgets/prowlarr/component.jsx +++ b/src/widgets/prowlarr/component.jsx @@ -1,19 +1,16 @@ -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: indexersData, error: indexersError } = useWidgetAPI(widget, "indexer"); const { data: grabsData, error: grabsError } = useWidgetAPI(widget, "indexerstats"); - if (indexersError || grabsError) { - return ; + if (indexersError || indexersData?.error || grabsError || grabsData?.error) { + const finalError = indexersError ?? indexersData?.error ?? grabsError ?? grabsData?.error; + return ; } if (!indexersData || !grabsData) { diff --git a/src/widgets/proxmox/component.jsx b/src/widgets/proxmox/component.jsx index 9cdb26f7f..79d2d8814 100644 --- a/src/widgets/proxmox/component.jsx +++ b/src/widgets/proxmox/component.jsx @@ -15,8 +15,9 @@ export default function Component({ service }) { const { data: clusterData, error: clusterError } = useWidgetAPI(widget, "cluster/resources"); - if (clusterError) { - return ; + if (clusterError || clusterData?.error) { + const finalError = clusterError ?? clusterData.error; + return ; } if (!clusterData || !clusterData.data) { diff --git a/src/widgets/qbittorrent/component.jsx b/src/widgets/qbittorrent/component.jsx index 4d3a3585d..541032a1f 100644 --- a/src/widgets/qbittorrent/component.jsx +++ b/src/widgets/qbittorrent/component.jsx @@ -11,8 +11,9 @@ export default function Component({ service }) { const { data: torrentData, error: torrentError } = useWidgetAPI(widget, "torrents/info"); - if (torrentError) { - return ; + if (torrentError || torrentData?.error) { + const finalError = torrentError ?? torrentData.error; + return ; } if (!torrentData) { diff --git a/src/widgets/radarr/component.jsx b/src/widgets/radarr/component.jsx index fe5a69684..679ce4fd0 100644 --- a/src/widgets/radarr/component.jsx +++ b/src/widgets/radarr/component.jsx @@ -1,19 +1,16 @@ -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: moviesData, error: moviesError } = useWidgetAPI(widget, "movie"); const { data: queuedData, error: queuedError } = useWidgetAPI(widget, "queue/status"); - if (moviesError || queuedError) { - return ; + if (moviesError || moviesData?.error || queuedError || queuedData?.error) { + const finalError = moviesError ?? moviesData?.error ?? queuedError ?? queuedData?.error; + return ; } if (!moviesData || !queuedData) { diff --git a/src/widgets/readarr/component.jsx b/src/widgets/readarr/component.jsx index 1e55d7cd9..ddf2dfd7c 100644 --- a/src/widgets/readarr/component.jsx +++ b/src/widgets/readarr/component.jsx @@ -13,8 +13,9 @@ export default function Component({ service }) { const { data: wantedData, error: wantedError } = useWidgetAPI(widget, "wanted/missing"); const { data: queueData, error: queueError } = useWidgetAPI(widget, "queue/status"); - if (booksError || wantedError || queueError) { - return ; + if (booksError || booksData?.error || wantedError || wantedData?.error || queueError || queueData?.error) { + const finalError = booksError ?? booksData?.error ?? wantedError ?? wantedData?.error ?? queueError ?? queueData?.error; + return ; } if (!booksData || !wantedData || !queueData) { diff --git a/src/widgets/rutorrent/component.jsx b/src/widgets/rutorrent/component.jsx index 279bdf0e5..4f7340f58 100644 --- a/src/widgets/rutorrent/component.jsx +++ b/src/widgets/rutorrent/component.jsx @@ -11,8 +11,9 @@ export default function Component({ service }) { const { data: statusData, error: statusError } = useWidgetAPI(widget); - if (statusError) { - return ; + if (statusError || statusData?.error) { + const finalError = statusError ?? statusData.error; + return ; } if (!statusData) { diff --git a/src/widgets/sabnzbd/component.jsx b/src/widgets/sabnzbd/component.jsx index c4e64c9ad..db49160b2 100644 --- a/src/widgets/sabnzbd/component.jsx +++ b/src/widgets/sabnzbd/component.jsx @@ -21,8 +21,9 @@ export default function Component({ service }) { const { data: queueData, error: queueError } = useWidgetAPI(widget, "queue"); - if (queueError) { - return ; + if (queueError || queueData?.error) { + const finalError = queueError ?? queueData.error; + return ; } if (!queueData) { diff --git a/src/widgets/sonarr/component.jsx b/src/widgets/sonarr/component.jsx index 8618b5126..28751eb66 100644 --- a/src/widgets/sonarr/component.jsx +++ b/src/widgets/sonarr/component.jsx @@ -1,20 +1,17 @@ -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: wantedData, error: wantedError } = useWidgetAPI(widget, "wanted/missing"); const { data: queuedData, error: queuedError } = useWidgetAPI(widget, "queue"); const { data: seriesData, error: seriesError } = useWidgetAPI(widget, "series"); - if (wantedError || queuedError || seriesError) { - return ; + if (wantedError || wantedData?.error || queuedError || queuedData?.error || seriesError || seriesData?.error) { + const finalError = wantedError ?? wantedData?.error ?? queuedError ?? queuedData?.error ?? seriesError ?? seriesData?.error; + return ; } if (!wantedData || !queuedData || !seriesData) { diff --git a/src/widgets/speedtest/component.jsx b/src/widgets/speedtest/component.jsx index e93c69637..b8bc7fdd6 100644 --- a/src/widgets/speedtest/component.jsx +++ b/src/widgets/speedtest/component.jsx @@ -11,8 +11,9 @@ export default function Component({ service }) { const { data: speedtestData, error: speedtestError } = useWidgetAPI(widget, "speedtest/latest"); - if (speedtestError || (speedtestData && !speedtestData.data)) { - return ; + if (speedtestError || speedtestData?.error) { + const finalError = speedtestError ?? speedtestData.error; + return ; } if (!speedtestData) { diff --git a/src/widgets/strelaysrv/component.jsx b/src/widgets/strelaysrv/component.jsx index c7b887db5..3366ffc29 100644 --- a/src/widgets/strelaysrv/component.jsx +++ b/src/widgets/strelaysrv/component.jsx @@ -11,8 +11,9 @@ export default function Component({ service }) { const { data: statsData, error: statsError } = useWidgetAPI(widget, "status"); - if (statsError) { - return ; + if (statsError || statsData?.error) { + const finalError = statsError ?? statsData.error; + return ; } if (!statsData) { diff --git a/src/widgets/tautulli/component.jsx b/src/widgets/tautulli/component.jsx index 98207c43c..d77f2b1e4 100644 --- a/src/widgets/tautulli/component.jsx +++ b/src/widgets/tautulli/component.jsx @@ -4,8 +4,8 @@ import { useTranslation } from "next-i18next"; import { BsFillPlayFill, BsPauseFill, BsCpu, BsFillCpuFill } from "react-icons/bs"; import { MdOutlineSmartDisplay, MdSmartDisplay } from "react-icons/md"; -import Container from "components/services/widget/container"; import { formatProxyUrl } from "utils/proxy/api-helpers"; +import Container from "components/services/widget/container"; function millisecondsToTime(milliseconds) { const seconds = Math.floor((milliseconds / 1000) % 60); @@ -123,8 +123,9 @@ export default function Component({ service }) { refreshInterval: 5000, }); - if (activityError) { - return ; + if (activityError || activityData?.error) { + const finalError = activityError ?? activityData.error; + return ; } if (!activityData) { diff --git a/src/widgets/traefik/component.jsx b/src/widgets/traefik/component.jsx index d24edb9e5..bee59c290 100644 --- a/src/widgets/traefik/component.jsx +++ b/src/widgets/traefik/component.jsx @@ -1,18 +1,15 @@ -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: traefikData, error: traefikError } = useWidgetAPI(widget, "overview"); - if (traefikError) { - return ; + if (traefikError || traefikData?.error) { + const finalError = traefikError ?? traefikData.error; + return ; } if (!traefikData) { diff --git a/src/widgets/transmission/component.jsx b/src/widgets/transmission/component.jsx index 3c2f38ef1..6cc8efe6e 100644 --- a/src/widgets/transmission/component.jsx +++ b/src/widgets/transmission/component.jsx @@ -11,8 +11,9 @@ export default function Component({ service }) { const { data: torrentData, error: torrentError } = useWidgetAPI(widget); - if (torrentError) { - return ; + if (torrentError || torrentData?.error) { + const finalError = torrentError ?? torrentData.error; + return ; } if (!torrentData) { diff --git a/src/widgets/transmission/proxy.js b/src/widgets/transmission/proxy.js index 34bbd480d..cdc1e9c92 100644 --- a/src/widgets/transmission/proxy.js +++ b/src/widgets/transmission/proxy.js @@ -68,6 +68,7 @@ export default async function transmissionProxyHandler(req, res) { if (status !== 200) { logger.error("Error getting data from Transmission: %d. Data: %s", status, data); + return res.status(500).send({error: {message:"Error getting data from Transmission", url, data}}); } if (contentType) res.setHeader("Content-Type", contentType); diff --git a/src/widgets/unifi/component.jsx b/src/widgets/unifi/component.jsx index 9a3232599..ea161d674 100644 --- a/src/widgets/unifi/component.jsx +++ b/src/widgets/unifi/component.jsx @@ -12,7 +12,8 @@ export default function Component({ service }) { const { data: statsData, error: statsError } = useWidgetAPI(widget, "stat/sites"); if (statsError || statsData?.error) { - return ; + const finalError = statsError ?? statsData.error; + return ; } const defaultSite = statsData?.data?.find(s => s.name === "default"); diff --git a/src/widgets/unifi/proxy.js b/src/widgets/unifi/proxy.js index 95ac331e0..abb5986f4 100644 --- a/src/widgets/unifi/proxy.js +++ b/src/widgets/unifi/proxy.js @@ -74,7 +74,7 @@ export default async function unifiProxyHandler(req, res) { // don't make two requests each time data from Unifi is required [status, contentType, data, responseHeaders] = await httpProxy(widget.url); prefix = ""; - if (responseHeaders["x-csrf-token"]) { + if (responseHeaders?.["x-csrf-token"]) { prefix = udmpPrefix; } cache.put(prefixCacheKey, prefix); @@ -88,13 +88,14 @@ export default async function unifiProxyHandler(req, res) { setCookieHeader(url, params); [status, contentType, data, responseHeaders] = await httpProxy(url, params); + if (status === 401) { logger.debug("Unifi isn't logged in or rejected the reqeust, attempting login."); [status, contentType, data, responseHeaders] = await login(widget); if (status !== 200) { logger.error("HTTP %d logging in to Unifi. Data: %s", status, data); - return res.status(status).end(data); + return res.status(status).json({error: {message: `HTTP Error ${status}`, url, data}}); } const json = JSON.parse(data.toString()); @@ -112,6 +113,7 @@ export default async function unifiProxyHandler(req, res) { if (status !== 200) { logger.error("HTTP %d getting data from Unifi endpoint %s. Data: %s", status, url.href, data); + return res.status(status).json({error: {message: `HTTP Error ${status}`, url, data}}); } if (contentType) res.setHeader("Content-Type", contentType); From 1695fd5bee86bdc0c776a118e6c127f0161b5046 Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+shamoon@users.noreply.github.com> Date: Sat, 22 Oct 2022 23:11:08 -0700 Subject: [PATCH 3/8] Add field validation for some widgets --- src/widgets/jellyseerr/widget.js | 5 +++++ src/widgets/overseerr/widget.js | 5 +++++ src/widgets/pihole/widget.js | 5 +++++ src/widgets/radarr/widget.js | 3 +++ src/widgets/sabnzbd/widget.js | 3 +++ src/widgets/sonarr/widget.js | 9 +++++++++ src/widgets/speedtest/widget.js | 3 +++ src/widgets/strelaysrv/widget.js | 5 +++++ src/widgets/traefik/widget.js | 3 +++ 9 files changed, 41 insertions(+) diff --git a/src/widgets/jellyseerr/widget.js b/src/widgets/jellyseerr/widget.js index d752e3391..3895d2c5b 100644 --- a/src/widgets/jellyseerr/widget.js +++ b/src/widgets/jellyseerr/widget.js @@ -7,6 +7,11 @@ const widget = { mappings: { "request/count": { endpoint: "request/count", + validate: [ + "pending", + "approved", + "available" + ] }, }, }; diff --git a/src/widgets/overseerr/widget.js b/src/widgets/overseerr/widget.js index d752e3391..945af5edb 100644 --- a/src/widgets/overseerr/widget.js +++ b/src/widgets/overseerr/widget.js @@ -7,6 +7,11 @@ const widget = { mappings: { "request/count": { endpoint: "request/count", + validate: [ + "pending", + "approved", + "available", + ], }, }, }; diff --git a/src/widgets/pihole/widget.js b/src/widgets/pihole/widget.js index 2e20fe8a3..b392cdede 100644 --- a/src/widgets/pihole/widget.js +++ b/src/widgets/pihole/widget.js @@ -7,6 +7,11 @@ const widget = { mappings: { "api.php": { endpoint: "api.php", + validate: [ + "dns_queries_today", + "ads_blocked_today", + "domains_being_blocked" + ] }, }, }; diff --git a/src/widgets/radarr/widget.js b/src/widgets/radarr/widget.js index 5a457ea75..780542195 100644 --- a/src/widgets/radarr/widget.js +++ b/src/widgets/radarr/widget.js @@ -16,6 +16,9 @@ const widget = { }, "queue/status": { endpoint: "queue/status", + validate: [ + "totalCount" + ] }, }, }; diff --git a/src/widgets/sabnzbd/widget.js b/src/widgets/sabnzbd/widget.js index e30973768..0e7ea43a3 100644 --- a/src/widgets/sabnzbd/widget.js +++ b/src/widgets/sabnzbd/widget.js @@ -7,6 +7,9 @@ const widget = { mappings: { queue: { endpoint: "queue", + validate: [ + "queue" + ] }, }, }; diff --git a/src/widgets/sonarr/widget.js b/src/widgets/sonarr/widget.js index 32780bdae..2be11ffd4 100644 --- a/src/widgets/sonarr/widget.js +++ b/src/widgets/sonarr/widget.js @@ -11,12 +11,21 @@ const widget = { map: (data) => ({ total: asJson(data).length, }), + validate: [ + "total" + ] }, queue: { endpoint: "queue", + validate: [ + "totalRecords" + ] }, "wanted/missing": { endpoint: "wanted/missing", + validate: [ + "totalRecords" + ] }, }, }; diff --git a/src/widgets/speedtest/widget.js b/src/widgets/speedtest/widget.js index b227848a6..a5ba06347 100644 --- a/src/widgets/speedtest/widget.js +++ b/src/widgets/speedtest/widget.js @@ -7,6 +7,9 @@ const widget = { mappings: { "speedtest/latest": { endpoint: "speedtest/latest", + validate: [ + "data" + ] }, }, }; diff --git a/src/widgets/strelaysrv/widget.js b/src/widgets/strelaysrv/widget.js index 713f05b4d..2141e2e24 100644 --- a/src/widgets/strelaysrv/widget.js +++ b/src/widgets/strelaysrv/widget.js @@ -7,6 +7,11 @@ const widget = { mappings: { status: { endpoint: "status", + validate: [ + "numActiveSessions", + "numConnections", + "bytesProxied" + ] }, }, }; diff --git a/src/widgets/traefik/widget.js b/src/widgets/traefik/widget.js index aa92fa1e7..5811b1b9a 100644 --- a/src/widgets/traefik/widget.js +++ b/src/widgets/traefik/widget.js @@ -7,6 +7,9 @@ const widget = { mappings: { overview: { endpoint: "overview", + validate: [ + "http" + ] }, }, }; From 7c4d8a77cf3915c18dc18034a53998b0cf3020bd Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+shamoon@users.noreply.github.com> Date: Sun, 23 Oct 2022 01:48:46 -0700 Subject: [PATCH 4/8] Update error display styling --- public/locales/en/common.json | 2 +- src/components/services/widget/error.jsx | 57 +++++++++++++----------- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/public/locales/en/common.json b/public/locales/en/common.json index c4b41f057..0afa49421 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -13,8 +13,8 @@ "widget": { "missing_type": "Missing Widget Type: {{type}}", "api_error": "API Error", + "information": "Information", "status": "Status", - "debug_info": "Debug Information", "url": "URL", "raw_error": "Raw Error", "response_data": "Response Data" diff --git a/src/components/services/widget/error.jsx b/src/components/services/widget/error.jsx index a6d400e8d..d12aebf21 100644 --- a/src/components/services/widget/error.jsx +++ b/src/components/services/widget/error.jsx @@ -1,4 +1,6 @@ import { useTranslation } from "react-i18next"; +import { IoAlertCircle } from "react-icons/io5"; +import classNames from "classnames"; function displayError(error) { return JSON.stringify(error[1] ? error[1] : error, null, 4); @@ -16,33 +18,34 @@ export default function Error({ error }) { } return ( -
-
Something went wrong.
-
- {t("widget.debug_info")} -
-
    -
  • - {t("widget.api_error")}: {error.message} -
  • - {error.url &&
  • - {t("widget.url")}: {error.url} -
  • } - {error.rawError &&
  • - {t("widget.raw_error")}: -
    - {displayError(error.rawError)} -
    -
  • } - {error.data &&
  • - {t("widget.response_data")}: -
    - {displayData(error.data)} -
    -
  • } -
+
+ +
+ {t("widget.api_error")} {t("widget.information")}
-
-
+ +
+
    +
  • + {t("widget.api_error")}: {error.message} +
  • + {error.url &&
  • + {t("widget.url")}: {error.url} +
  • } + {error.rawError &&
  • + {t("widget.raw_error")}: +
    + {displayError(error.rawError)} +
    +
  • } + {error.data &&
  • + {t("widget.response_data")}: +
    + {displayData(error.data)} +
    +
  • } +
+
+
); } From f473d324dfd8e0afef16d9af1551398e8e513c5d Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+shamoon@users.noreply.github.com> Date: Sun, 23 Oct 2022 23:45:43 -0700 Subject: [PATCH 5/8] Fix docker status error display --- src/components/services/item.jsx | 2 +- src/components/services/widget/error.jsx | 15 +++++++-------- src/pages/api/docker/stats/[...service].js | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/components/services/item.jsx b/src/components/services/item.jsx index 56ed2b4b1..aa8e80962 100644 --- a/src/components/services/item.jsx +++ b/src/components/services/item.jsx @@ -85,7 +85,7 @@ export default function Item({ service }) { {service.container && service.server && (
diff --git a/src/components/services/widget/error.jsx b/src/components/services/widget/error.jsx index d12aebf21..cf5e13661 100644 --- a/src/components/services/widget/error.jsx +++ b/src/components/services/widget/error.jsx @@ -1,6 +1,5 @@ import { useTranslation } from "react-i18next"; import { IoAlertCircle } from "react-icons/io5"; -import classNames from "classnames"; function displayError(error) { return JSON.stringify(error[1] ? error[1] : error, null, 4); @@ -12,7 +11,7 @@ function displayData(data) { export default function Error({ error }) { const { t } = useTranslation(); - + if (error?.data?.error) { error = error.data.error; // eslint-disable-line no-param-reassign } @@ -21,24 +20,24 @@ export default function Error({ error }) {
- {t("widget.api_error")} {t("widget.information")} + {t("widget.api_error")} {error.message && t("widget.information")}
    -
  • + {error.message &&
  • {t("widget.api_error")}: {error.message} -
  • - {error.url &&
  • +
  • } + {error.url &&
  • {t("widget.url")}: {error.url}
  • } - {error.rawError &&
  • + {error.rawError &&
  • {t("widget.raw_error")}:
    {displayError(error.rawError)}
  • } - {error.data &&
  • + {error.data &&
  • {t("widget.response_data")}:
    {displayData(error.data)} diff --git a/src/pages/api/docker/stats/[...service].js b/src/pages/api/docker/stats/[...service].js index ca8c8bd3a..d214ffb28 100644 --- a/src/pages/api/docker/stats/[...service].js +++ b/src/pages/api/docker/stats/[...service].js @@ -46,7 +46,7 @@ export default async function handler(req, res) { }); } catch { res.status(500).send({ - error: "unknown error", + error: {message: "Unknown error"}, }); } } From 00163d2f44416eacb3f154810eaf94fc12755990 Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+shamoon@users.noreply.github.com> Date: Wed, 26 Oct 2022 08:35:38 -0700 Subject: [PATCH 6/8] Update homebridge, autobrr, truenas, tubearchivist, watchtower, pyload widgets --- src/widgets/autobrr/component.jsx | 5 +++-- src/widgets/autobrr/widget.js | 4 ++++ src/widgets/homebridge/component.jsx | 3 ++- src/widgets/pyload/proxy.js | 6 +++--- src/widgets/truenas/component.jsx | 5 +++-- src/widgets/truenas/widget.js | 4 ++++ src/widgets/tubearchivist/component.jsx | 5 +++-- src/widgets/tubearchivist/widget.js | 12 ++++++++++++ src/widgets/watchtower/component.jsx | 5 +++-- src/widgets/watchtower/proxy.js | 5 +++-- 10 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/widgets/autobrr/component.jsx b/src/widgets/autobrr/component.jsx index b78f48f61..d2769f0d4 100644 --- a/src/widgets/autobrr/component.jsx +++ b/src/widgets/autobrr/component.jsx @@ -13,8 +13,9 @@ export default function Component({ service }) { const { data: filtersData, error: filtersError } = useWidgetAPI(widget, "filters"); const { data: indexersData, error: indexersError } = useWidgetAPI(widget, "indexers"); - if (statsError || filtersError || indexersError) { - return ; + if (statsError || statsData?.error || filtersError || filtersData?.error || indexersError || indexersData?.error) { + const finalError = statsError ?? statsData?.error ?? filtersError ?? filtersData?.error ?? indexersError ?? indexersData?.error; + return ; } if (!statsData || !filtersData || !indexersData) { diff --git a/src/widgets/autobrr/widget.js b/src/widgets/autobrr/widget.js index 0254029ee..fb03f9d30 100644 --- a/src/widgets/autobrr/widget.js +++ b/src/widgets/autobrr/widget.js @@ -7,6 +7,10 @@ const widget = { mappings: { stats: { endpoint: "release/stats", + validate: [ + "push_approved_count", + "push_rejected_count" + ] }, filters: { endpoint: "filters", diff --git a/src/widgets/homebridge/component.jsx b/src/widgets/homebridge/component.jsx index 807cc49a8..3f1dc5dad 100644 --- a/src/widgets/homebridge/component.jsx +++ b/src/widgets/homebridge/component.jsx @@ -12,7 +12,8 @@ export default function Component({ service }) { const { data: homebridgeData, error: homebridgeError } = useWidgetAPI(widget, "info"); if (homebridgeError || homebridgeData?.error) { - return ; + const finalError = homebridgeError ?? homebridgeData.error; + return ; } if (!homebridgeData) { diff --git a/src/widgets/pyload/proxy.js b/src/widgets/pyload/proxy.js index 46b28684a..4a866d9cf 100644 --- a/src/widgets/pyload/proxy.js +++ b/src/widgets/pyload/proxy.js @@ -84,9 +84,9 @@ export default async function pyloadProxyHandler(req, res) { if (data?.error || status !== 200) { try { - return res.status(status).send(Buffer.from(data).toString()); + return res.status(status).send({error: {message: "HTTP error communicating with Plex API", data: Buffer.from(data).toString()}}); } catch (e) { - return res.status(status).send(data); + return res.status(status).send({error: {message: "HTTP error communicating with Plex API", data}}); } } @@ -95,7 +95,7 @@ export default async function pyloadProxyHandler(req, res) { } } catch (e) { logger.error(e); - return res.status(500).send(e.toString()); + return res.status(500).send({error: {message: `Error communicating with Plex API: ${e.toString()}`}}); } return res.status(400).json({ error: 'Invalid proxy service type' }); diff --git a/src/widgets/truenas/component.jsx b/src/widgets/truenas/component.jsx index 4244d8333..fd2fdbe79 100644 --- a/src/widgets/truenas/component.jsx +++ b/src/widgets/truenas/component.jsx @@ -41,8 +41,9 @@ export default function Component({ service }) { const { data: alertData, error: alertError } = useWidgetAPI(widget, "alerts"); const { data: statusData, error: statusError } = useWidgetAPI(widget, "status"); - if (alertError || statusError) { - return ; + if (alertError || alertData?.error || statusError || statusData?.error) { + const finalError = alertError ?? alertData?.error ?? statusError ?? statusData?.error; + return ; } if (!alertData || !statusData) { diff --git a/src/widgets/truenas/widget.js b/src/widgets/truenas/widget.js index 7269e36a1..4c479e9f3 100644 --- a/src/widgets/truenas/widget.js +++ b/src/widgets/truenas/widget.js @@ -14,6 +14,10 @@ const widget = { }, status: { endpoint: "system/info", + validate: [ + "loadavg", + "uptime_seconds" + ] }, }, }; diff --git a/src/widgets/tubearchivist/component.jsx b/src/widgets/tubearchivist/component.jsx index 5b5484436..503fb0faa 100644 --- a/src/widgets/tubearchivist/component.jsx +++ b/src/widgets/tubearchivist/component.jsx @@ -14,8 +14,9 @@ export default function Component({ service }) { const { data: channelsData, error: channelsError } = useWidgetAPI(widget, "channels"); const { data: playlistsData, error: playlistsError } = useWidgetAPI(widget, "playlists"); - if (downloadsError || videosError || channelsError || playlistsError) { - return ; + if (downloadsError || downloadsData?.error || videosError || videosData?.error || channelsError || channelsData?.error || playlistsError || playlistsData?.error) { + const finalError = downloadsError ?? downloadsData?.error ?? videosError ?? videosData?.error ?? channelsError ?? channelsData?.error ?? playlistsError ?? playlistsData?.error; + return ; } if (!downloadsData || !videosData || !channelsData || !playlistsData) { diff --git a/src/widgets/tubearchivist/widget.js b/src/widgets/tubearchivist/widget.js index c73070f00..6610bf123 100644 --- a/src/widgets/tubearchivist/widget.js +++ b/src/widgets/tubearchivist/widget.js @@ -7,15 +7,27 @@ const widget = { mappings: { downloads: { endpoint: "download", + validate: [ + "paginate", + ] }, videos: { endpoint: "video", + validate: [ + "paginate", + ] }, channels: { endpoint: "channel", + validate: [ + "paginate", + ] }, playlists: { endpoint: "playlist", + validate: [ + "paginate", + ] }, }, }; diff --git a/src/widgets/watchtower/component.jsx b/src/widgets/watchtower/component.jsx index 68c5531f6..2f683c1db 100644 --- a/src/widgets/watchtower/component.jsx +++ b/src/widgets/watchtower/component.jsx @@ -12,8 +12,9 @@ export default function Component({ service }) { const { data: watchData, error: watchError } = useWidgetAPI(widget, "watchtower"); - if (watchError || !watchData) { - return ; + if (watchError || watchData?.error) { + const finalError = watchError ?? watchData?.error; + return ; } if (!watchData) { diff --git a/src/widgets/watchtower/proxy.js b/src/widgets/watchtower/proxy.js index 2d54928c6..b37fc5f8b 100644 --- a/src/widgets/watchtower/proxy.js +++ b/src/widgets/watchtower/proxy.js @@ -33,15 +33,16 @@ export default async function watchtowerProxyHandler(req, res) { if (status !== 200 || !data) { logger.error("Error getting data from WatchTower: %d. Data: %s", status, data); + return res.status(status).json({error: {message: `HTTP Error ${status}`, url, data}}); } - const cleanData = data.toString().split("\n").filter(s => s.startsWith("watchtower")) + const cleanData = data.toString().split("\n").filter(s => s.startsWith("watchtower")); const jsonRes = {} cleanData.map(e => e.split(" ")).forEach(strArray => { const [key, value] = strArray jsonRes[key] = value - }) + }); if (contentType) res.setHeader("Content-Type", contentType); return res.status(status).send(jsonRes); From 8a783ba9f6c1e034f74ff1d4a71dedfe49e2f30b Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+shamoon@users.noreply.github.com> Date: Mon, 7 Nov 2022 09:24:15 -0800 Subject: [PATCH 7/8] Simplify error catching --- .../widgets/unifi_console/unifi_console.jsx | 2 +- src/utils/proxy/use-widget-api.js | 8 +++++++- src/widgets/adguard/component.jsx | 6 +++--- src/widgets/authentik/component.jsx | 4 ++-- src/widgets/autobrr/component.jsx | 4 ++-- src/widgets/bazarr/component.jsx | 4 ++-- src/widgets/changedetectionio/component.jsx | 5 ++--- src/widgets/coinmarketcap/component.jsx | 5 ++--- src/widgets/emby/component.jsx | 11 +++++------ src/widgets/gotify/component.jsx | 4 ++-- src/widgets/homebridge/component.jsx | 5 ++--- src/widgets/jackett/component.jsx | 5 ++--- src/widgets/jellyseerr/component.jsx | 5 ++--- src/widgets/lidarr/component.jsx | 4 ++-- src/widgets/mastodon/component.jsx | 5 ++--- src/widgets/navidrome/component.jsx | 4 ++-- src/widgets/nzbget/component.jsx | 5 ++--- src/widgets/ombi/component.jsx | 5 ++--- src/widgets/overseerr/component.jsx | 5 ++--- src/widgets/pihole/component.jsx | 5 ++--- src/widgets/plex/component.jsx | 10 ++++------ src/widgets/plex/proxy.js | 2 +- src/widgets/portainer/component.jsx | 5 ++--- src/widgets/prowlarr/component.jsx | 4 ++-- src/widgets/proxmox/component.jsx | 5 ++--- src/widgets/pyload/component.jsx | 4 ++-- src/widgets/qbittorrent/component.jsx | 5 ++--- src/widgets/radarr/component.jsx | 4 ++-- src/widgets/readarr/component.jsx | 4 ++-- src/widgets/rutorrent/component.jsx | 5 ++--- src/widgets/sabnzbd/component.jsx | 5 ++--- src/widgets/sonarr/component.jsx | 4 ++-- src/widgets/speedtest/component.jsx | 5 ++--- src/widgets/strelaysrv/component.jsx | 5 ++--- src/widgets/tautulli/component.jsx | 10 ++++------ src/widgets/traefik/component.jsx | 5 ++--- src/widgets/transmission/component.jsx | 5 ++--- src/widgets/truenas/component.jsx | 4 ++-- src/widgets/tubearchivist/component.jsx | 4 ++-- src/widgets/unifi/component.jsx | 5 ++--- src/widgets/watchtower/component.jsx | 5 ++--- 41 files changed, 93 insertions(+), 113 deletions(-) diff --git a/src/components/widgets/unifi_console/unifi_console.jsx b/src/components/widgets/unifi_console/unifi_console.jsx index 3e3e135e7..af8fd4586 100644 --- a/src/components/widgets/unifi_console/unifi_console.jsx +++ b/src/components/widgets/unifi_console/unifi_console.jsx @@ -12,7 +12,7 @@ export default function Widget({ options }) { options.type = "unifi_console"; const { data: statsData, error: statsError } = useWidgetAPI(options, "stat/sites", { index: options.index }); - if (statsError || statsData?.error) { + if (statsError) { return (
    diff --git a/src/utils/proxy/use-widget-api.js b/src/utils/proxy/use-widget-api.js index b196e62c4..05eb220ec 100644 --- a/src/utils/proxy/use-widget-api.js +++ b/src/utils/proxy/use-widget-api.js @@ -3,5 +3,11 @@ import useSWR from "swr"; import { formatProxyUrl } from "./api-helpers"; export default function useWidgetAPI(widget, ...options) { - return useSWR(formatProxyUrl(widget, ...options)); + const config = {}; + if (options?.refreshInterval) { + config.refreshInterval = options.refreshInterval; + } + const { data, error } = useSWR(formatProxyUrl(widget, ...options), config); + // make the data error the top-level error + return { data, error: data?.error ?? error } } diff --git a/src/widgets/adguard/component.jsx b/src/widgets/adguard/component.jsx index 3147ffcf8..bab969ad3 100644 --- a/src/widgets/adguard/component.jsx +++ b/src/widgets/adguard/component.jsx @@ -11,10 +11,10 @@ export default function Component({ service }) { const { data: adguardData, error: adguardError } = useWidgetAPI(widget, "stats"); - if (adguardError || adguardData?.error) { - const finalError = adguardError ?? adguardData.error; - return ; + if (adguardError) { + return ; } + if (!adguardData) { return ( diff --git a/src/widgets/authentik/component.jsx b/src/widgets/authentik/component.jsx index d4aa8dba7..84498db48 100644 --- a/src/widgets/authentik/component.jsx +++ b/src/widgets/authentik/component.jsx @@ -13,8 +13,8 @@ export default function Component({ service }) { const { data: loginsData, error: loginsError } = useWidgetAPI(widget, "login"); const { data: failedLoginsData, error: failedLoginsError } = useWidgetAPI(widget, "login_failed"); - if (usersError || usersData?.error || loginsError || loginsData?.error || failedLoginsError || failedLoginsData?.error) { - const finalError = usersError ?? usersData?.error ?? loginsError ?? loginsData?.error ?? failedLoginsError ?? failedLoginsData?.error; + if (usersError || loginsError || failedLoginsError) { + const finalError = usersError ?? loginsError ?? failedLoginsError; return ; } diff --git a/src/widgets/autobrr/component.jsx b/src/widgets/autobrr/component.jsx index d2769f0d4..3c1702436 100644 --- a/src/widgets/autobrr/component.jsx +++ b/src/widgets/autobrr/component.jsx @@ -13,8 +13,8 @@ export default function Component({ service }) { const { data: filtersData, error: filtersError } = useWidgetAPI(widget, "filters"); const { data: indexersData, error: indexersError } = useWidgetAPI(widget, "indexers"); - if (statsError || statsData?.error || filtersError || filtersData?.error || indexersError || indexersData?.error) { - const finalError = statsError ?? statsData?.error ?? filtersError ?? filtersData?.error ?? indexersError ?? indexersData?.error; + if (statsError || filtersError || indexersError) { + const finalError = statsError ?? filtersError ?? indexersError; return ; } diff --git a/src/widgets/bazarr/component.jsx b/src/widgets/bazarr/component.jsx index 070f82326..0537c180d 100644 --- a/src/widgets/bazarr/component.jsx +++ b/src/widgets/bazarr/component.jsx @@ -12,8 +12,8 @@ export default function Component({ service }) { const { data: episodesData, error: episodesError } = useWidgetAPI(widget, "episodes"); const { data: moviesData, error: moviesError } = useWidgetAPI(widget, "movies"); - if (moviesError || moviesData?.error || episodesError || episodesData?.error) { - const finalError = moviesError ?? moviesData?.error ?? episodesError ?? episodesData?.error; + if (moviesError || episodesError) { + const finalError = moviesError ?? episodesError; return ; } diff --git a/src/widgets/changedetectionio/component.jsx b/src/widgets/changedetectionio/component.jsx index ce29691f2..0a4ce8d2c 100644 --- a/src/widgets/changedetectionio/component.jsx +++ b/src/widgets/changedetectionio/component.jsx @@ -11,9 +11,8 @@ export default function Component({ service }) { const { data, error } = useWidgetAPI(widget, "info"); - if (error || data?.error) { - const finalError = error ?? data.error; - return ; + if (error) { + return ; } const totalObserved = Object.keys(data).length; let diffsDetected = 0; diff --git a/src/widgets/coinmarketcap/component.jsx b/src/widgets/coinmarketcap/component.jsx index fa5844bdf..90b91a5ea 100644 --- a/src/widgets/coinmarketcap/component.jsx +++ b/src/widgets/coinmarketcap/component.jsx @@ -36,9 +36,8 @@ export default function Component({ service }) { ); } - if (statsError || statsData?.error) { - const finalError = statsError ?? statsData.error; - return ; + if (statsError) { + return ; } if (!statsData || !dateRange) { diff --git a/src/widgets/emby/component.jsx b/src/widgets/emby/component.jsx index 26371c814..a61cd7aa7 100644 --- a/src/widgets/emby/component.jsx +++ b/src/widgets/emby/component.jsx @@ -1,10 +1,10 @@ -import useSWR from "swr"; import { useTranslation } from "next-i18next"; import { BsVolumeMuteFill, BsFillPlayFill, BsPauseFill, BsCpu, BsFillCpuFill } from "react-icons/bs"; import { MdOutlineSmartDisplay } from "react-icons/md"; -import { formatProxyUrl, formatProxyUrlWithSegments } from "utils/proxy/api-helpers"; import Container from "components/services/widget/container"; +import { formatProxyUrlWithSegments } from "utils/proxy/api-helpers"; +import useWidgetAPI from "utils/proxy/use-widget-api"; function ticksToTime(ticks) { const milliseconds = ticks / 10000; @@ -157,7 +157,7 @@ export default function Component({ service }) { data: sessionsData, error: sessionsError, mutate: sessionMutate, - } = useSWR(formatProxyUrl(widget, "Sessions"), { + } = useWidgetAPI(widget, "Sessions", { refreshInterval: 5000, }); @@ -171,9 +171,8 @@ export default function Component({ service }) { }); } - if (sessionsError || sessionsData?.error) { - const finalError = sessionsError ?? sessionsData.error; - return ; + if (sessionsError) { + return ; } if (!sessionsData) { diff --git a/src/widgets/gotify/component.jsx b/src/widgets/gotify/component.jsx index cedc3f84a..7cd5c1358 100644 --- a/src/widgets/gotify/component.jsx +++ b/src/widgets/gotify/component.jsx @@ -9,8 +9,8 @@ export default function Component({ service }) { const { data: messagesData, error: messagesError } = useWidgetAPI(widget, "message"); const { data: clientsData, error: clientsError } = useWidgetAPI(widget, "client"); - if (appsError || appsData?.error || messagesError || messagesData?.error || clientsError || clientsData?.error) { - const finalError = appsError ?? appsData?.error ?? messagesError ?? messagesData?.error ?? clientsError ?? clientsData?.error; + if (appsError || messagesError || clientsError) { + const finalError = appsError ?? messagesError ?? clientsError; return ; } diff --git a/src/widgets/homebridge/component.jsx b/src/widgets/homebridge/component.jsx index 3f1dc5dad..a1e2f2e1d 100644 --- a/src/widgets/homebridge/component.jsx +++ b/src/widgets/homebridge/component.jsx @@ -11,9 +11,8 @@ export default function Component({ service }) { const { data: homebridgeData, error: homebridgeError } = useWidgetAPI(widget, "info"); - if (homebridgeError || homebridgeData?.error) { - const finalError = homebridgeError ?? homebridgeData.error; - return ; + if (homebridgeError) { + return ; } if (!homebridgeData) { diff --git a/src/widgets/jackett/component.jsx b/src/widgets/jackett/component.jsx index e9fcd95a8..b70cbd954 100644 --- a/src/widgets/jackett/component.jsx +++ b/src/widgets/jackett/component.jsx @@ -11,9 +11,8 @@ export default function Component({ service }) { const { data: indexersData, error: indexersError } = useWidgetAPI(widget, "indexers"); - if (indexersError || indexersData?.error) { - const finalError = indexersError ?? indexersData.error; - return ; + if (indexersError) { + return ; } if (!indexersData) { diff --git a/src/widgets/jellyseerr/component.jsx b/src/widgets/jellyseerr/component.jsx index aece8560f..a129909eb 100644 --- a/src/widgets/jellyseerr/component.jsx +++ b/src/widgets/jellyseerr/component.jsx @@ -7,9 +7,8 @@ export default function Component({ service }) { const { data: statsData, error: statsError } = useWidgetAPI(widget, "request/count"); - if (statsError || statsData?.error) { - const finalError = statsError ?? statsData.error; - return ; + if (statsError) { + return ; } if (!statsData) { diff --git a/src/widgets/lidarr/component.jsx b/src/widgets/lidarr/component.jsx index 5951e094c..b612ae32e 100644 --- a/src/widgets/lidarr/component.jsx +++ b/src/widgets/lidarr/component.jsx @@ -13,8 +13,8 @@ export default function Component({ service }) { const { data: wantedData, error: wantedError } = useWidgetAPI(widget, "wanted/missing"); const { data: queueData, error: queueError } = useWidgetAPI(widget, "queue/status"); - if (albumsError || albumsData?.error || wantedError || wantedData?.error || queueError || queueData?.error) { - const finalError = albumsError ?? albumsData?.error ?? wantedError ?? wantedData?.error ?? queueError ?? queueData?.error; + if (albumsError || wantedError || queueError) { + const finalError = albumsError ?? wantedError ?? queueError; return ; } diff --git a/src/widgets/mastodon/component.jsx b/src/widgets/mastodon/component.jsx index 477d648ec..fd4f0ece6 100644 --- a/src/widgets/mastodon/component.jsx +++ b/src/widgets/mastodon/component.jsx @@ -11,9 +11,8 @@ export default function Component({ service }) { const { data: statsData, error: statsError } = useWidgetAPI(widget, "instance"); - if (statsError || statsData?.error) { - const finalError = statsError ?? statsData.error; - return ; + if (statsError) { + return ; } if (!statsData) { diff --git a/src/widgets/navidrome/component.jsx b/src/widgets/navidrome/component.jsx index f4f3e6721..e45ac6555 100644 --- a/src/widgets/navidrome/component.jsx +++ b/src/widgets/navidrome/component.jsx @@ -26,8 +26,8 @@ export default function Component({ service }) { const { data: navidromeData, error: navidromeError } = useWidgetAPI(widget, "getNowPlaying"); - if (navidromeError || navidromeData?.error || navidromeData?.["subsonic-response"]?.error) { - return ; + if (navidromeError || navidromeData?.["subsonic-response"]?.error) { + return ; } if (!navidromeData) { diff --git a/src/widgets/nzbget/component.jsx b/src/widgets/nzbget/component.jsx index 92408982a..b33e0830a 100644 --- a/src/widgets/nzbget/component.jsx +++ b/src/widgets/nzbget/component.jsx @@ -11,9 +11,8 @@ export default function Component({ service }) { const { data: statusData, error: statusError } = useWidgetAPI(widget, "status"); - if (statusError || statusData?.error) { - const finalError = statusError ?? statusData.error; - return ; + if (statusError) { + return ; } if (!statusData) { diff --git a/src/widgets/ombi/component.jsx b/src/widgets/ombi/component.jsx index a5a0a0b73..722d5c457 100644 --- a/src/widgets/ombi/component.jsx +++ b/src/widgets/ombi/component.jsx @@ -7,9 +7,8 @@ export default function Component({ service }) { const { data: statsData, error: statsError } = useWidgetAPI(widget, "Request/count"); - if (statsError || statsData?.error) { - const finalError = statsError ?? statsData.error; - return ; + if (statsError) { + return ; } if (!statsData) { diff --git a/src/widgets/overseerr/component.jsx b/src/widgets/overseerr/component.jsx index f0269b4f1..6f5ae8ffe 100644 --- a/src/widgets/overseerr/component.jsx +++ b/src/widgets/overseerr/component.jsx @@ -7,9 +7,8 @@ export default function Component({ service }) { const { data: statsData, error: statsError } = useWidgetAPI(widget, "request/count"); - if (statsError || statsData?.error) { - const finalError = statsError ?? statsData.error; - return ; + if (statsError) { + return ; } if (!statsData) { diff --git a/src/widgets/pihole/component.jsx b/src/widgets/pihole/component.jsx index cb77cc2b6..f213ac6db 100644 --- a/src/widgets/pihole/component.jsx +++ b/src/widgets/pihole/component.jsx @@ -11,9 +11,8 @@ export default function Component({ service }) { const { data: piholeData, error: piholeError } = useWidgetAPI(widget, "api.php"); - if (piholeError || piholeData?.error) { - const finalError = piholeError ?? piholeData.error; - return ; + if (piholeError) { + return ; } if (!piholeData) { diff --git a/src/widgets/plex/component.jsx b/src/widgets/plex/component.jsx index cf6d705cf..6fe15bd57 100644 --- a/src/widgets/plex/component.jsx +++ b/src/widgets/plex/component.jsx @@ -1,22 +1,20 @@ -import useSWR from "swr"; import { useTranslation } from "next-i18next"; import Block from "components/services/widget/block"; import Container from "components/services/widget/container"; -import { formatProxyUrl } from "utils/proxy/api-helpers"; +import useWidgetAPI from "utils/proxy/use-widget-api"; export default function Component({ service }) { const { t } = useTranslation(); const { widget } = service; - const { data: plexData, error: plexAPIError } = useSWR(formatProxyUrl(widget, "unified"), { + const { data: plexData, error: plexAPIError } = useWidgetAPI(widget, "unified", { refreshInterval: 5000, }); - if (plexAPIError || plexData?.error) { - const finalError = plexAPIError ?? plexData.error; - return ; + if (plexAPIError) { + return ; } if (!plexData) { diff --git a/src/widgets/plex/proxy.js b/src/widgets/plex/proxy.js index c016023dd..36b7a268e 100644 --- a/src/widgets/plex/proxy.js +++ b/src/widgets/plex/proxy.js @@ -67,7 +67,7 @@ export default async function plexProxyHandler(req, res) { let [status, apiData] = await fetchFromPlexAPI("/status/sessions", widget); if (status !== 200) { - return res.status(status).json({error: {message: "HTTP error communicating with Plex API", data: apiData}}); + return res.status(status).json({error: {message: "HTTP error communicating with Plex API", data: Buffer.from(apiData).toString()}}); } if (apiData && apiData.MediaContainer) { diff --git a/src/widgets/portainer/component.jsx b/src/widgets/portainer/component.jsx index 87c173dd8..ccc26b7f6 100644 --- a/src/widgets/portainer/component.jsx +++ b/src/widgets/portainer/component.jsx @@ -13,9 +13,8 @@ export default function Component({ service }) { all: 1, }); - if (containersError || containersData?.error) { - const finalError = containersError ?? containersData.error; - return ; + if (containersError) { + return ; } if (!containersData) { diff --git a/src/widgets/prowlarr/component.jsx b/src/widgets/prowlarr/component.jsx index 6649fdefd..890679e52 100644 --- a/src/widgets/prowlarr/component.jsx +++ b/src/widgets/prowlarr/component.jsx @@ -8,8 +8,8 @@ export default function Component({ service }) { const { data: indexersData, error: indexersError } = useWidgetAPI(widget, "indexer"); const { data: grabsData, error: grabsError } = useWidgetAPI(widget, "indexerstats"); - if (indexersError || indexersData?.error || grabsError || grabsData?.error) { - const finalError = indexersError ?? indexersData?.error ?? grabsError ?? grabsData?.error; + if (indexersError || grabsError) { + const finalError = indexersError ?? grabsError; return ; } diff --git a/src/widgets/proxmox/component.jsx b/src/widgets/proxmox/component.jsx index 79d2d8814..1d384b546 100644 --- a/src/widgets/proxmox/component.jsx +++ b/src/widgets/proxmox/component.jsx @@ -15,9 +15,8 @@ export default function Component({ service }) { const { data: clusterData, error: clusterError } = useWidgetAPI(widget, "cluster/resources"); - if (clusterError || clusterData?.error) { - const finalError = clusterError ?? clusterData.error; - return ; + if (clusterError) { + return ; } if (!clusterData || !clusterData.data) { diff --git a/src/widgets/pyload/component.jsx b/src/widgets/pyload/component.jsx index 958733c31..3bb4b0c45 100644 --- a/src/widgets/pyload/component.jsx +++ b/src/widgets/pyload/component.jsx @@ -9,8 +9,8 @@ export default function Component({ service }) { const { widget } = service; const { data: pyloadData, error: pyloadError } = useWidgetAPI(widget, "status"); - if (pyloadError || pyloadData?.error) { - return ; + if (pyloadError) { + return ; } if (!pyloadData) { diff --git a/src/widgets/qbittorrent/component.jsx b/src/widgets/qbittorrent/component.jsx index 541032a1f..3abc933da 100644 --- a/src/widgets/qbittorrent/component.jsx +++ b/src/widgets/qbittorrent/component.jsx @@ -11,9 +11,8 @@ export default function Component({ service }) { const { data: torrentData, error: torrentError } = useWidgetAPI(widget, "torrents/info"); - if (torrentError || torrentData?.error) { - const finalError = torrentError ?? torrentData.error; - return ; + if (torrentError) { + return ; } if (!torrentData) { diff --git a/src/widgets/radarr/component.jsx b/src/widgets/radarr/component.jsx index 679ce4fd0..f2620b789 100644 --- a/src/widgets/radarr/component.jsx +++ b/src/widgets/radarr/component.jsx @@ -8,8 +8,8 @@ export default function Component({ service }) { const { data: moviesData, error: moviesError } = useWidgetAPI(widget, "movie"); const { data: queuedData, error: queuedError } = useWidgetAPI(widget, "queue/status"); - if (moviesError || moviesData?.error || queuedError || queuedData?.error) { - const finalError = moviesError ?? moviesData?.error ?? queuedError ?? queuedData?.error; + if (moviesError || queuedError) { + const finalError = moviesError ?? queuedError; return ; } diff --git a/src/widgets/readarr/component.jsx b/src/widgets/readarr/component.jsx index ddf2dfd7c..4eee11f4f 100644 --- a/src/widgets/readarr/component.jsx +++ b/src/widgets/readarr/component.jsx @@ -13,8 +13,8 @@ export default function Component({ service }) { const { data: wantedData, error: wantedError } = useWidgetAPI(widget, "wanted/missing"); const { data: queueData, error: queueError } = useWidgetAPI(widget, "queue/status"); - if (booksError || booksData?.error || wantedError || wantedData?.error || queueError || queueData?.error) { - const finalError = booksError ?? booksData?.error ?? wantedError ?? wantedData?.error ?? queueError ?? queueData?.error; + if (booksError || wantedError || queueError) { + const finalError = booksError ?? wantedError ?? queueError; return ; } diff --git a/src/widgets/rutorrent/component.jsx b/src/widgets/rutorrent/component.jsx index 4f7340f58..ef3a278f1 100644 --- a/src/widgets/rutorrent/component.jsx +++ b/src/widgets/rutorrent/component.jsx @@ -11,9 +11,8 @@ export default function Component({ service }) { const { data: statusData, error: statusError } = useWidgetAPI(widget); - if (statusError || statusData?.error) { - const finalError = statusError ?? statusData.error; - return ; + if (statusError) { + return ; } if (!statusData) { diff --git a/src/widgets/sabnzbd/component.jsx b/src/widgets/sabnzbd/component.jsx index db49160b2..b0cdb9518 100644 --- a/src/widgets/sabnzbd/component.jsx +++ b/src/widgets/sabnzbd/component.jsx @@ -21,9 +21,8 @@ export default function Component({ service }) { const { data: queueData, error: queueError } = useWidgetAPI(widget, "queue"); - if (queueError || queueData?.error) { - const finalError = queueError ?? queueData.error; - return ; + if (queueError) { + return ; } if (!queueData) { diff --git a/src/widgets/sonarr/component.jsx b/src/widgets/sonarr/component.jsx index 28751eb66..14dd33287 100644 --- a/src/widgets/sonarr/component.jsx +++ b/src/widgets/sonarr/component.jsx @@ -9,8 +9,8 @@ export default function Component({ service }) { const { data: queuedData, error: queuedError } = useWidgetAPI(widget, "queue"); const { data: seriesData, error: seriesError } = useWidgetAPI(widget, "series"); - if (wantedError || wantedData?.error || queuedError || queuedData?.error || seriesError || seriesData?.error) { - const finalError = wantedError ?? wantedData?.error ?? queuedError ?? queuedData?.error ?? seriesError ?? seriesData?.error; + if (wantedError || queuedError || seriesError) { + const finalError = wantedError ?? queuedError ?? seriesError; return ; } diff --git a/src/widgets/speedtest/component.jsx b/src/widgets/speedtest/component.jsx index b8bc7fdd6..3067dbf3d 100644 --- a/src/widgets/speedtest/component.jsx +++ b/src/widgets/speedtest/component.jsx @@ -11,9 +11,8 @@ export default function Component({ service }) { const { data: speedtestData, error: speedtestError } = useWidgetAPI(widget, "speedtest/latest"); - if (speedtestError || speedtestData?.error) { - const finalError = speedtestError ?? speedtestData.error; - return ; + if (speedtestError) { + return ; } if (!speedtestData) { diff --git a/src/widgets/strelaysrv/component.jsx b/src/widgets/strelaysrv/component.jsx index 3366ffc29..f58f8830a 100644 --- a/src/widgets/strelaysrv/component.jsx +++ b/src/widgets/strelaysrv/component.jsx @@ -11,9 +11,8 @@ export default function Component({ service }) { const { data: statsData, error: statsError } = useWidgetAPI(widget, "status"); - if (statsError || statsData?.error) { - const finalError = statsError ?? statsData.error; - return ; + if (statsError) { + return ; } if (!statsData) { diff --git a/src/widgets/tautulli/component.jsx b/src/widgets/tautulli/component.jsx index d77f2b1e4..44b1eb6db 100644 --- a/src/widgets/tautulli/component.jsx +++ b/src/widgets/tautulli/component.jsx @@ -1,11 +1,10 @@ /* eslint-disable camelcase */ -import useSWR from "swr"; import { useTranslation } from "next-i18next"; import { BsFillPlayFill, BsPauseFill, BsCpu, BsFillCpuFill } from "react-icons/bs"; import { MdOutlineSmartDisplay, MdSmartDisplay } from "react-icons/md"; -import { formatProxyUrl } from "utils/proxy/api-helpers"; import Container from "components/services/widget/container"; +import useWidgetAPI from "utils/proxy/use-widget-api"; function millisecondsToTime(milliseconds) { const seconds = Math.floor((milliseconds / 1000) % 60); @@ -119,13 +118,12 @@ export default function Component({ service }) { const { widget } = service; - const { data: activityData, error: activityError } = useSWR(formatProxyUrl(widget, "get_activity"), { + const { data: activityData, error: activityError } = useWidgetAPI(widget, "get_activity", { refreshInterval: 5000, }); - if (activityError || activityData?.error) { - const finalError = activityError ?? activityData.error; - return ; + if (activityError) { + return ; } if (!activityData) { diff --git a/src/widgets/traefik/component.jsx b/src/widgets/traefik/component.jsx index bee59c290..7739e62a1 100644 --- a/src/widgets/traefik/component.jsx +++ b/src/widgets/traefik/component.jsx @@ -7,9 +7,8 @@ export default function Component({ service }) { const { data: traefikData, error: traefikError } = useWidgetAPI(widget, "overview"); - if (traefikError || traefikData?.error) { - const finalError = traefikError ?? traefikData.error; - return ; + if (traefikError) { + return ; } if (!traefikData) { diff --git a/src/widgets/transmission/component.jsx b/src/widgets/transmission/component.jsx index 6cc8efe6e..7de276c28 100644 --- a/src/widgets/transmission/component.jsx +++ b/src/widgets/transmission/component.jsx @@ -11,9 +11,8 @@ export default function Component({ service }) { const { data: torrentData, error: torrentError } = useWidgetAPI(widget); - if (torrentError || torrentData?.error) { - const finalError = torrentError ?? torrentData.error; - return ; + if (torrentError) { + return ; } if (!torrentData) { diff --git a/src/widgets/truenas/component.jsx b/src/widgets/truenas/component.jsx index fd2fdbe79..70f917ab4 100644 --- a/src/widgets/truenas/component.jsx +++ b/src/widgets/truenas/component.jsx @@ -41,8 +41,8 @@ export default function Component({ service }) { const { data: alertData, error: alertError } = useWidgetAPI(widget, "alerts"); const { data: statusData, error: statusError } = useWidgetAPI(widget, "status"); - if (alertError || alertData?.error || statusError || statusData?.error) { - const finalError = alertError ?? alertData?.error ?? statusError ?? statusData?.error; + if (alertError || statusError) { + const finalError = alertError ?? statusError; return ; } diff --git a/src/widgets/tubearchivist/component.jsx b/src/widgets/tubearchivist/component.jsx index 503fb0faa..b1b310d77 100644 --- a/src/widgets/tubearchivist/component.jsx +++ b/src/widgets/tubearchivist/component.jsx @@ -14,8 +14,8 @@ export default function Component({ service }) { const { data: channelsData, error: channelsError } = useWidgetAPI(widget, "channels"); const { data: playlistsData, error: playlistsError } = useWidgetAPI(widget, "playlists"); - if (downloadsError || downloadsData?.error || videosError || videosData?.error || channelsError || channelsData?.error || playlistsError || playlistsData?.error) { - const finalError = downloadsError ?? downloadsData?.error ?? videosError ?? videosData?.error ?? channelsError ?? channelsData?.error ?? playlistsError ?? playlistsData?.error; + if (downloadsError || videosError || channelsError || playlistsError) { + const finalError = downloadsError ?? videosError ?? channelsError ?? playlistsError; return ; } diff --git a/src/widgets/unifi/component.jsx b/src/widgets/unifi/component.jsx index ea161d674..6df43dfdb 100644 --- a/src/widgets/unifi/component.jsx +++ b/src/widgets/unifi/component.jsx @@ -11,9 +11,8 @@ export default function Component({ service }) { const { data: statsData, error: statsError } = useWidgetAPI(widget, "stat/sites"); - if (statsError || statsData?.error) { - const finalError = statsError ?? statsData.error; - return ; + if (statsError) { + return ; } const defaultSite = statsData?.data?.find(s => s.name === "default"); diff --git a/src/widgets/watchtower/component.jsx b/src/widgets/watchtower/component.jsx index 2f683c1db..3550e3f43 100644 --- a/src/widgets/watchtower/component.jsx +++ b/src/widgets/watchtower/component.jsx @@ -12,9 +12,8 @@ export default function Component({ service }) { const { data: watchData, error: watchError } = useWidgetAPI(widget, "watchtower"); - if (watchError || watchData?.error) { - const finalError = watchError ?? watchData?.error; - return ; + if (watchError) { + return ; } if (!watchData) { From 1215808dd49addfa44e8446ab0995b0956741277 Mon Sep 17 00:00:00 2001 From: Michael Shamoon <4887959+shamoon@users.noreply.github.com> Date: Fri, 18 Nov 2022 09:11:25 -0800 Subject: [PATCH 8/8] Add validation, error display for gluetun --- src/widgets/gluetun/component.jsx | 6 +----- src/widgets/gluetun/widget.js | 5 +++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/widgets/gluetun/component.jsx b/src/widgets/gluetun/component.jsx index f20c400b9..59e490ce7 100644 --- a/src/widgets/gluetun/component.jsx +++ b/src/widgets/gluetun/component.jsx @@ -1,18 +1,14 @@ -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: gluetunData, error: gluetunError } = useWidgetAPI(widget, "ip"); if (gluetunError) { - return ; + return ; } if (!gluetunData) { diff --git a/src/widgets/gluetun/widget.js b/src/widgets/gluetun/widget.js index 59aa39efe..009adbf31 100644 --- a/src/widgets/gluetun/widget.js +++ b/src/widgets/gluetun/widget.js @@ -7,6 +7,11 @@ const widget = { mappings: { ip: { endpoint: "publicip/ip", + validate: [ + "public_ip", + "region", + "country" + ] }, }, };