mirror of
https://github.com/karl0ss/homepage.git
synced 2025-04-29 12:03:41 +01:00
starting of widget refactoring
This commit is contained in:
parent
d6f6ea9dba
commit
562235f828
@ -2,6 +2,12 @@
|
|||||||
"extends": ["airbnb", "next/core-web-vitals", "prettier"],
|
"extends": ["airbnb", "next/core-web-vitals", "prettier"],
|
||||||
"plugins": ["prettier"],
|
"plugins": ["prettier"],
|
||||||
"rules": {
|
"rules": {
|
||||||
|
"import/no-cycle": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"maxDepth": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
"import/order": [
|
"import/order": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
|
@ -1,72 +1,11 @@
|
|||||||
import dynamic from "next/dynamic";
|
|
||||||
import { useTranslation } from "next-i18next";
|
import { useTranslation } from "next-i18next";
|
||||||
|
|
||||||
const Sonarr = dynamic(() => import("./widgets/service/sonarr"));
|
import components from "widgets/components";
|
||||||
const Radarr = dynamic(() => import("./widgets/service/radarr"));
|
|
||||||
const Lidarr = dynamic(() => import("./widgets/service/lidarr"));
|
|
||||||
const Readarr = dynamic(() => import("./widgets/service/readarr"));
|
|
||||||
const Bazarr = dynamic(() => import("./widgets/service/bazarr"));
|
|
||||||
const Ombi = dynamic(() => import("./widgets/service/ombi"));
|
|
||||||
const Portainer = dynamic(() => import("./widgets/service/portainer"));
|
|
||||||
const Emby = dynamic(() => import("./widgets/service/emby"));
|
|
||||||
const Nzbget = dynamic(() => import("./widgets/service/nzbget"));
|
|
||||||
const SABnzbd = dynamic(() => import("./widgets/service/sabnzbd"));
|
|
||||||
const Transmission = dynamic(() => import("./widgets/service/transmission"));
|
|
||||||
const QBittorrent = dynamic(() => import("./widgets/service/qbittorrent"));
|
|
||||||
const Docker = dynamic(() => import("./widgets/service/docker"));
|
|
||||||
const Pihole = dynamic(() => import("./widgets/service/pihole"));
|
|
||||||
const Rutorrent = dynamic(() => import("./widgets/service/rutorrent"));
|
|
||||||
const Jellyfin = dynamic(() => import("./widgets/service/jellyfin"));
|
|
||||||
const Speedtest = dynamic(() => import("./widgets/service/speedtest"));
|
|
||||||
const Traefik = dynamic(() => import("./widgets/service/traefik"));
|
|
||||||
const Jellyseerr = dynamic(() => import("./widgets/service/jellyseerr"));
|
|
||||||
const Overseerr = dynamic(() => import("./widgets/service/overseerr"));
|
|
||||||
const Npm = dynamic(() => import("./widgets/service/npm"));
|
|
||||||
const Tautulli = dynamic(() => import("./widgets/service/tautulli"));
|
|
||||||
const CoinMarketCap = dynamic(() => import("./widgets/service/coinmarketcap"));
|
|
||||||
const Gotify = dynamic(() => import("./widgets/service/gotify"));
|
|
||||||
const Prowlarr = dynamic(() => import("./widgets/service/prowlarr"));
|
|
||||||
const Jackett = dynamic(() => import("./widgets/service/jackett"));
|
|
||||||
const AdGuard = dynamic(() => import("./widgets/service/adguard"));
|
|
||||||
const StRelaySrv = dynamic(() => import("./widgets/service/strelaysrv"));
|
|
||||||
const Mastodon = dynamic(() => import("./widgets/service/mastodon"));
|
|
||||||
|
|
||||||
const widgetMappings = {
|
|
||||||
docker: Docker,
|
|
||||||
sonarr: Sonarr,
|
|
||||||
radarr: Radarr,
|
|
||||||
lidarr: Lidarr,
|
|
||||||
readarr: Readarr,
|
|
||||||
bazarr: Bazarr,
|
|
||||||
ombi: Ombi,
|
|
||||||
portainer: Portainer,
|
|
||||||
emby: Emby,
|
|
||||||
jellyfin: Jellyfin,
|
|
||||||
nzbget: Nzbget,
|
|
||||||
sabnzbd: SABnzbd,
|
|
||||||
transmission: Transmission,
|
|
||||||
qbittorrent: QBittorrent,
|
|
||||||
pihole: Pihole,
|
|
||||||
rutorrent: Rutorrent,
|
|
||||||
speedtest: Speedtest,
|
|
||||||
traefik: Traefik,
|
|
||||||
jellyseerr: Jellyseerr,
|
|
||||||
overseerr: Overseerr,
|
|
||||||
coinmarketcap: CoinMarketCap,
|
|
||||||
npm: Npm,
|
|
||||||
tautulli: Tautulli,
|
|
||||||
gotify: Gotify,
|
|
||||||
prowlarr: Prowlarr,
|
|
||||||
jackett: Jackett,
|
|
||||||
adguard: AdGuard,
|
|
||||||
strelaysrv: StRelaySrv,
|
|
||||||
mastodon: Mastodon,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function Widget({ service }) {
|
export default function Widget({ service }) {
|
||||||
const { t } = useTranslation("common");
|
const { t } = useTranslation("common");
|
||||||
|
|
||||||
const ServiceWidget = widgetMappings[service.widget.type];
|
const ServiceWidget = components[service.widget.type];
|
||||||
|
|
||||||
if (ServiceWidget) {
|
if (ServiceWidget) {
|
||||||
return <ServiceWidget service={service} />;
|
return <ServiceWidget service={service} />;
|
||||||
|
@ -4,14 +4,14 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function AdGuard({ service }) {
|
export default function AdGuard({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: adguardData, error: adguardError } = useSWR(formatApiUrl(config, "stats"));
|
const { data: adguardData, error: adguardError } = useSWR(formatProxyUrl(config, "stats"));
|
||||||
|
|
||||||
if (adguardError) {
|
if (adguardError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
@ -4,15 +4,15 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function Bazarr({ service }) {
|
export default function Bazarr({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: episodesData, error: episodesError } = useSWR(formatApiUrl(config, "episodes"));
|
const { data: episodesData, error: episodesError } = useSWR(formatProxyUrl(config, "episodes"));
|
||||||
const { data: moviesData, error: moviesError } = useSWR(formatApiUrl(config, "movies"));
|
const { data: moviesData, error: moviesError } = useSWR(formatProxyUrl(config, "movies"));
|
||||||
|
|
||||||
if (episodesError || moviesError) {
|
if (episodesError || moviesError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
@ -7,7 +7,7 @@ import Widget from "../widget";
|
|||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import Dropdown from "components/services/dropdown";
|
import Dropdown from "components/services/dropdown";
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function CoinMarketCap({ service }) {
|
export default function CoinMarketCap({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -26,7 +26,7 @@ export default function CoinMarketCap({ service }) {
|
|||||||
const { symbols } = config;
|
const { symbols } = config;
|
||||||
|
|
||||||
const { data: statsData, error: statsError } = useSWR(
|
const { data: statsData, error: statsError } = useSWR(
|
||||||
formatApiUrl(config, `v1/cryptocurrency/quotes/latest?symbol=${symbols.join(",")}&convert=${currencyCode}`)
|
formatProxyUrl(config, `v1/cryptocurrency/quotes/latest?symbol=${symbols.join(",")}&convert=${currencyCode}`)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!symbols || symbols.length === 0) {
|
if (!symbols || symbols.length === 0) {
|
||||||
|
@ -5,7 +5,7 @@ import { MdOutlineSmartDisplay } from "react-icons/md";
|
|||||||
|
|
||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
function ticksToTime(ticks) {
|
function ticksToTime(ticks) {
|
||||||
const milliseconds = ticks / 10000;
|
const milliseconds = ticks / 10000;
|
||||||
@ -158,12 +158,12 @@ export default function Emby({ service }) {
|
|||||||
data: sessionsData,
|
data: sessionsData,
|
||||||
error: sessionsError,
|
error: sessionsError,
|
||||||
mutate: sessionMutate,
|
mutate: sessionMutate,
|
||||||
} = useSWR(formatApiUrl(config, "Sessions"), {
|
} = useSWR(formatProxyUrl(config, "Sessions"), {
|
||||||
refreshInterval: 5000,
|
refreshInterval: 5000,
|
||||||
});
|
});
|
||||||
|
|
||||||
async function handlePlayCommand(session, command) {
|
async function handlePlayCommand(session, command) {
|
||||||
const url = formatApiUrl(config, `Sessions/${session.Id}/Playing/${command}`);
|
const url = formatProxyUrl(config, `Sessions/${session.Id}/Playing/${command}`);
|
||||||
await fetch(url, {
|
await fetch(url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
|
@ -4,16 +4,16 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function Gotify({ service }) {
|
export default function Gotify({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: appsData, error: appsError } = useSWR(formatApiUrl(config, `application`));
|
const { data: appsData, error: appsError } = useSWR(formatProxyUrl(config, `application`));
|
||||||
const { data: messagesData, error: messagesError } = useSWR(formatApiUrl(config, `message`));
|
const { data: messagesData, error: messagesError } = useSWR(formatProxyUrl(config, `message`));
|
||||||
const { data: clientsData, error: clientsError } = useSWR(formatApiUrl(config, `client`));
|
const { data: clientsData, error: clientsError } = useSWR(formatProxyUrl(config, `client`));
|
||||||
|
|
||||||
if (appsError || messagesError || clientsError) {
|
if (appsError || messagesError || clientsError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
@ -4,14 +4,14 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function Jackett({ service }) {
|
export default function Jackett({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: indexersData, error: indexersError } = useSWR(formatApiUrl(config, "indexers"));
|
const { data: indexersData, error: indexersError } = useSWR(formatProxyUrl(config, "indexers"));
|
||||||
|
|
||||||
if (indexersError) {
|
if (indexersError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
@ -4,14 +4,14 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function Jellyseerr({ service }) {
|
export default function Jellyseerr({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: statsData, error: statsError } = useSWR(formatApiUrl(config, `request/count`));
|
const { data: statsData, error: statsError } = useSWR(formatProxyUrl(config, `request/count`));
|
||||||
|
|
||||||
if (statsError) {
|
if (statsError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
@ -4,16 +4,16 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function Lidarr({ service }) {
|
export default function Lidarr({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: albumsData, error: albumsError } = useSWR(formatApiUrl(config, "album"));
|
const { data: albumsData, error: albumsError } = useSWR(formatProxyUrl(config, "album"));
|
||||||
const { data: wantedData, error: wantedError } = useSWR(formatApiUrl(config, "wanted/missing"));
|
const { data: wantedData, error: wantedError } = useSWR(formatProxyUrl(config, "wanted/missing"));
|
||||||
const { data: queueData, error: queueError } = useSWR(formatApiUrl(config, "queue/status"));
|
const { data: queueData, error: queueError } = useSWR(formatProxyUrl(config, "queue/status"));
|
||||||
|
|
||||||
if (albumsError || wantedError || queueError) {
|
if (albumsError || wantedError || queueError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
@ -4,14 +4,14 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function Mastodon({ service }) {
|
export default function Mastodon({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: statsData, error: statsError } = useSWR(formatApiUrl(config, `instance`));
|
const { data: statsData, error: statsError } = useSWR(formatProxyUrl(config, `instance`));
|
||||||
|
|
||||||
if (statsError) {
|
if (statsError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
@ -29,7 +29,7 @@ export default function Mastodon({ service }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Widget>
|
<Widget>
|
||||||
<Block label={t("mastodon.user_count")} value={t("common.number", { value: statsData.stats.user_count })} />
|
<Block label={t("mastodon.user_count")} value={t("common.number", { value: statsData.stats.user_count })} />
|
||||||
<Block label={t("mastodon.status_count")} value={t("common.number", { value: statsData.stats.status_count })} />
|
<Block label={t("mastodon.status_count")} value={t("common.number", { value: statsData.stats.status_count })} />
|
||||||
<Block label={t("mastodon.domain_count")} value={t("common.number", { value: statsData.stats.domain_count })} />
|
<Block label={t("mastodon.domain_count")} value={t("common.number", { value: statsData.stats.domain_count })} />
|
||||||
</Widget>
|
</Widget>
|
||||||
|
@ -4,14 +4,14 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function Npm({ service }) {
|
export default function Npm({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: infoData, error: infoError } = useSWR(formatApiUrl(config, "nginx/proxy-hosts"));
|
const { data: infoData, error: infoError } = useSWR(formatProxyUrl(config, "nginx/proxy-hosts"));
|
||||||
|
|
||||||
if (infoError) {
|
if (infoError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
@ -4,14 +4,14 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function Nzbget({ service }) {
|
export default function Nzbget({ service }) {
|
||||||
const { t } = useTranslation("common");
|
const { t } = useTranslation("common");
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: statusData, error: statusError } = useSWR(formatApiUrl(config, "status"));
|
const { data: statusData, error: statusError } = useSWR(formatProxyUrl(config, "status"));
|
||||||
|
|
||||||
if (statusError) {
|
if (statusError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
@ -4,14 +4,14 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function Ombi({ service }) {
|
export default function Ombi({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: statsData, error: statsError } = useSWR(formatApiUrl(config, `Request/count`));
|
const { data: statsData, error: statsError } = useSWR(formatProxyUrl(config, `Request/count`));
|
||||||
|
|
||||||
if (statsError) {
|
if (statsError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
@ -4,14 +4,14 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function Overseerr({ service }) {
|
export default function Overseerr({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: statsData, error: statsError } = useSWR(formatApiUrl(config, `request/count`));
|
const { data: statsData, error: statsError } = useSWR(formatProxyUrl(config, `request/count`));
|
||||||
|
|
||||||
if (statsError) {
|
if (statsError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
@ -4,14 +4,14 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function Pihole({ service }) {
|
export default function Pihole({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: piholeData, error: piholeError } = useSWR(formatApiUrl(config, "api.php"));
|
const { data: piholeData, error: piholeError } = useSWR(formatProxyUrl(config, "api.php"));
|
||||||
|
|
||||||
if (piholeError) {
|
if (piholeError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
@ -4,14 +4,16 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function Portainer({ service }) {
|
export default function Portainer({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: containersData, error: containersError } = useSWR(formatApiUrl(config, `docker/containers/json?all=1`));
|
const { data: containersData, error: containersError } = useSWR(
|
||||||
|
formatProxyUrl(config, `docker/containers/json?all=1`)
|
||||||
|
);
|
||||||
|
|
||||||
if (containersError) {
|
if (containersError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
@ -4,15 +4,15 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function Prowlarr({ service }) {
|
export default function Prowlarr({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: indexersData, error: indexersError } = useSWR(formatApiUrl(config, "indexer"));
|
const { data: indexersData, error: indexersError } = useSWR(formatProxyUrl(config, "indexer"));
|
||||||
const { data: grabsData, error: grabsError } = useSWR(formatApiUrl(config, "indexerstats"));
|
const { data: grabsData, error: grabsError } = useSWR(formatProxyUrl(config, "indexerstats"));
|
||||||
|
|
||||||
if (indexersError || grabsError) {
|
if (indexersError || grabsError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
@ -32,11 +32,11 @@ export default function Prowlarr({ service }) {
|
|||||||
|
|
||||||
const indexers = indexersData?.filter((indexer) => indexer.enable === true);
|
const indexers = indexersData?.filter((indexer) => indexer.enable === true);
|
||||||
|
|
||||||
let numberOfGrabs = 0
|
let numberOfGrabs = 0;
|
||||||
let numberOfQueries = 0
|
let numberOfQueries = 0;
|
||||||
let numberOfFailedGrabs = 0
|
let numberOfFailedGrabs = 0;
|
||||||
let numberOfFailedQueries = 0
|
let numberOfFailedQueries = 0;
|
||||||
grabsData?.indexers?.forEach(element => {
|
grabsData?.indexers?.forEach((element) => {
|
||||||
numberOfGrabs += element.numberOfGrabs;
|
numberOfGrabs += element.numberOfGrabs;
|
||||||
numberOfQueries += element.numberOfQueries;
|
numberOfQueries += element.numberOfQueries;
|
||||||
numberOfFailedGrabs += numberOfFailedGrabs + element.numberOfFailedGrabs;
|
numberOfFailedGrabs += numberOfFailedGrabs + element.numberOfFailedGrabs;
|
||||||
|
@ -4,14 +4,14 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function QBittorrent ({ service }) {
|
export default function QBittorrent({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: torrentData, error: torrentError } = useSWR(formatApiUrl(config, "torrents/info"));
|
const { data: torrentData, error: torrentError } = useSWR(formatProxyUrl(config, "torrents/info"));
|
||||||
|
|
||||||
if (torrentError) {
|
if (torrentError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
@ -4,14 +4,15 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function Radarr({ service }) {
|
export default function Radarr({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: moviesData, error: moviesError } = useSWR(formatApiUrl(config, "movie"));
|
const { data: moviesData, error: moviesError } = useSWR(formatProxyUrl(config, "movie"));
|
||||||
const { data: queuedData, error: queuedError } = useSWR(formatApiUrl(config, "queue/status"));
|
const { data: queuedData, error: queuedError } = useSWR(formatProxyUrl(config, "queue/status"));
|
||||||
|
|
||||||
if (moviesError || queuedError) {
|
if (moviesError || queuedError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
@ -4,16 +4,16 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function Readarr({ service }) {
|
export default function Readarr({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: booksData, error: booksError } = useSWR(formatApiUrl(config, "book"));
|
const { data: booksData, error: booksError } = useSWR(formatProxyUrl(config, "book"));
|
||||||
const { data: wantedData, error: wantedError } = useSWR(formatApiUrl(config, "wanted/missing"));
|
const { data: wantedData, error: wantedError } = useSWR(formatProxyUrl(config, "wanted/missing"));
|
||||||
const { data: queueData, error: queueError } = useSWR(formatApiUrl(config, "queue/status"));
|
const { data: queueData, error: queueError } = useSWR(formatProxyUrl(config, "queue/status"));
|
||||||
|
|
||||||
if (booksError || wantedError || queueError) {
|
if (booksError || wantedError || queueError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
@ -4,14 +4,14 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function Rutorrent({ service }) {
|
export default function Rutorrent({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: statusData, error: statusError } = useSWR(formatApiUrl(config));
|
const { data: statusData, error: statusError } = useSWR(formatProxyUrl(config));
|
||||||
|
|
||||||
if (statusError) {
|
if (statusError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
@ -4,14 +4,14 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function SABnzbd({ service }) {
|
export default function SABnzbd({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: queueData, error: queueError } = useSWR(formatApiUrl(config, "queue"));
|
const { data: queueData, error: queueError } = useSWR(formatProxyUrl(config, "queue"));
|
||||||
|
|
||||||
if (queueError) {
|
if (queueError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
@ -4,16 +4,16 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function Sonarr({ service }) {
|
export default function Sonarr({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: wantedData, error: wantedError } = useSWR(formatApiUrl(config, "wanted/missing"));
|
const { data: wantedData, error: wantedError } = useSWR(formatProxyUrl(config, "wanted/missing"));
|
||||||
const { data: queuedData, error: queuedError } = useSWR(formatApiUrl(config, "queue"));
|
const { data: queuedData, error: queuedError } = useSWR(formatProxyUrl(config, "queue"));
|
||||||
const { data: seriesData, error: seriesError } = useSWR(formatApiUrl(config, "series"));
|
const { data: seriesData, error: seriesError } = useSWR(formatProxyUrl(config, "series"));
|
||||||
|
|
||||||
if (wantedError || queuedError || seriesError) {
|
if (wantedError || queuedError || seriesError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
@ -4,14 +4,14 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function Speedtest({ service }) {
|
export default function Speedtest({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: speedtestData, error: speedtestError } = useSWR(formatApiUrl(config, "speedtest/latest"));
|
const { data: speedtestData, error: speedtestError } = useSWR(formatProxyUrl(config, "speedtest/latest"));
|
||||||
|
|
||||||
if (speedtestError) {
|
if (speedtestError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
@ -4,14 +4,14 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function StRelaySrv({ service }) {
|
export default function StRelaySrv({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: statsData, error: statsError } = useSWR(formatApiUrl(config, `status`));
|
const { data: statsData, error: statsError } = useSWR(formatProxyUrl(config, `status`));
|
||||||
|
|
||||||
if (statsError) {
|
if (statsError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
@ -29,10 +29,16 @@ export default function StRelaySrv({ service }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Widget>
|
<Widget>
|
||||||
<Block label={t("strelaysrv.numActiveSessions")} value={t("common.number", { value: statsData.numActiveSessions })} />
|
<Block
|
||||||
<Block label={t("strelaysrv.numConnections")} value={t("common.number", { value: statsData.numConnections })} />
|
label={t("strelaysrv.numActiveSessions")}
|
||||||
<Block label={t("strelaysrv.dataRelayed")} value={t("common.bytes", { value: statsData.bytesProxied })} />
|
value={t("common.number", { value: statsData.numActiveSessions })}
|
||||||
<Block label={t("strelaysrv.transferRate")} value={t("common.bitrate",{ value: statsData.kbps10s1m5m15m30m60m[5] })} />
|
/>
|
||||||
|
<Block label={t("strelaysrv.numConnections")} value={t("common.number", { value: statsData.numConnections })} />
|
||||||
|
<Block label={t("strelaysrv.dataRelayed")} value={t("common.bytes", { value: statsData.bytesProxied })} />
|
||||||
|
<Block
|
||||||
|
label={t("strelaysrv.transferRate")}
|
||||||
|
value={t("common.bitrate", { value: statsData.kbps10s1m5m15m30m60m[5] })}
|
||||||
|
/>
|
||||||
</Widget>
|
</Widget>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import { MdOutlineSmartDisplay, MdSmartDisplay } from "react-icons/md";
|
|||||||
|
|
||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
function millisecondsToTime(milliseconds) {
|
function millisecondsToTime(milliseconds) {
|
||||||
const seconds = Math.floor((milliseconds / 1000) % 60);
|
const seconds = Math.floor((milliseconds / 1000) % 60);
|
||||||
@ -120,7 +120,7 @@ export default function Tautulli({ service }) {
|
|||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: activityData, error: activityError } = useSWR(formatApiUrl(config, "get_activity"), {
|
const { data: activityData, error: activityError } = useSWR(formatProxyUrl(config, "get_activity"), {
|
||||||
refreshInterval: 5000,
|
refreshInterval: 5000,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -4,14 +4,14 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function Traefik({ service }) {
|
export default function Traefik({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: traefikData, error: traefikError } = useSWR(formatApiUrl(config, "overview"));
|
const { data: traefikData, error: traefikError } = useSWR(formatProxyUrl(config, "overview"));
|
||||||
|
|
||||||
if (traefikError) {
|
if (traefikError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
@ -4,14 +4,14 @@ import { useTranslation } from "next-i18next";
|
|||||||
import Widget from "../widget";
|
import Widget from "../widget";
|
||||||
import Block from "../block";
|
import Block from "../block";
|
||||||
|
|
||||||
import { formatApiUrl } from "utils/api-helpers";
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
export default function Transmission({ service }) {
|
export default function Transmission({ service }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const config = service.widget;
|
const config = service.widget;
|
||||||
|
|
||||||
const { data: torrentData, error: torrentError } = useSWR(formatApiUrl(config));
|
const { data: torrentData, error: torrentError } = useSWR(formatProxyUrl(config));
|
||||||
|
|
||||||
if (torrentError) {
|
if (torrentError) {
|
||||||
return <Widget error={t("widget.api_error")} />;
|
return <Widget error={t("widget.api_error")} />;
|
||||||
@ -37,7 +37,7 @@ export default function Transmission({ service }) {
|
|||||||
const torrent = torrents[i];
|
const torrent = torrents[i];
|
||||||
rateDl += torrent.rateDownload;
|
rateDl += torrent.rateDownload;
|
||||||
rateUl += torrent.rateUpload;
|
rateUl += torrent.rateUpload;
|
||||||
if (torrent.percentDone === 1) {
|
if (torrent.percentDone === 1) {
|
||||||
completed += 1;
|
completed += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,128 +1,45 @@
|
|||||||
import createLogger from "utils/logger";
|
import createLogger from "utils/logger";
|
||||||
import genericProxyHandler from "utils/proxies/generic";
|
import genericProxyHandler from "utils/proxies/generic";
|
||||||
import credentialedProxyHandler from "utils/proxies/credentialed";
|
import widgets from "widgets/widgets";
|
||||||
import rutorrentProxyHandler from "utils/proxies/rutorrent";
|
|
||||||
import nzbgetProxyHandler from "utils/proxies/nzbget";
|
|
||||||
import npmProxyHandler from "utils/proxies/npm";
|
|
||||||
import transmissionProxyHandler from "utils/proxies/transmission";
|
|
||||||
import qbittorrentProxyHandler from "utils/proxies/qbittorrent";
|
|
||||||
|
|
||||||
const logger = createLogger('servicesProxy');
|
const logger = createLogger("servicesProxy");
|
||||||
|
|
||||||
function asJson(data) {
|
|
||||||
if (data?.length > 0) {
|
|
||||||
const json = JSON.parse(data.toString());
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
function jsonArrayTransform(data, transform) {
|
|
||||||
const json = asJson(data);
|
|
||||||
if (json instanceof Array) {
|
|
||||||
return transform(json);
|
|
||||||
}
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
function jsonArrayFilter(data, filter) {
|
|
||||||
return jsonArrayTransform(data, (items) => items.filter(filter));
|
|
||||||
}
|
|
||||||
|
|
||||||
const serviceProxyHandlers = {
|
|
||||||
// uses query param auth
|
|
||||||
emby: genericProxyHandler,
|
|
||||||
jellyfin: genericProxyHandler,
|
|
||||||
pihole: genericProxyHandler,
|
|
||||||
radarr: {
|
|
||||||
proxy: genericProxyHandler,
|
|
||||||
maps: {
|
|
||||||
movie: (data) => ({
|
|
||||||
wanted: jsonArrayFilter(data, (item) => item.isAvailable === false).length,
|
|
||||||
have: jsonArrayFilter(data, (item) => item.isAvailable === true).length,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
sonarr: {
|
|
||||||
proxy: genericProxyHandler,
|
|
||||||
maps: {
|
|
||||||
series: (data) => ({
|
|
||||||
total: asJson(data).length,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
lidarr: {
|
|
||||||
proxy: genericProxyHandler,
|
|
||||||
maps: {
|
|
||||||
album: (data) => ({
|
|
||||||
have: jsonArrayFilter(data, (item) => item?.statistics?.percentOfTracks === 100).length,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
readarr: {
|
|
||||||
proxy: genericProxyHandler,
|
|
||||||
maps: {
|
|
||||||
book: (data) => ({
|
|
||||||
have: jsonArrayFilter(data, (item) => item?.statistics?.bookFileCount > 0).length,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
bazarr: {
|
|
||||||
proxy: genericProxyHandler,
|
|
||||||
maps: {
|
|
||||||
movies: (data) => ({
|
|
||||||
total: asJson(data).total,
|
|
||||||
}),
|
|
||||||
episodes: (data) => ({
|
|
||||||
total: asJson(data).total,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
speedtest: genericProxyHandler,
|
|
||||||
tautulli: genericProxyHandler,
|
|
||||||
traefik: genericProxyHandler,
|
|
||||||
sabnzbd: genericProxyHandler,
|
|
||||||
jackett: genericProxyHandler,
|
|
||||||
adguard: genericProxyHandler,
|
|
||||||
strelaysrv: genericProxyHandler,
|
|
||||||
mastodon: genericProxyHandler,
|
|
||||||
// uses X-API-Key (or similar) header auth
|
|
||||||
gotify: credentialedProxyHandler,
|
|
||||||
portainer: credentialedProxyHandler,
|
|
||||||
jellyseerr: credentialedProxyHandler,
|
|
||||||
overseerr: credentialedProxyHandler,
|
|
||||||
ombi: credentialedProxyHandler,
|
|
||||||
coinmarketcap: credentialedProxyHandler,
|
|
||||||
prowlarr: credentialedProxyHandler,
|
|
||||||
// super specific handlers
|
|
||||||
rutorrent: rutorrentProxyHandler,
|
|
||||||
nzbget: nzbgetProxyHandler,
|
|
||||||
npm: npmProxyHandler,
|
|
||||||
transmission: transmissionProxyHandler,
|
|
||||||
qbittorrent: qbittorrentProxyHandler,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default async function handler(req, res) {
|
export default async function handler(req, res) {
|
||||||
try {
|
try {
|
||||||
const { type } = req.query;
|
const { type } = req.query;
|
||||||
|
const widget = widgets[type];
|
||||||
|
|
||||||
const serviceProxyHandler = serviceProxyHandlers[type];
|
if (!widget) {
|
||||||
|
logger.debug("Unknown proxy service type: %s", type);
|
||||||
|
return res.status(403).json({ error: "Unkown proxy service type" });
|
||||||
|
}
|
||||||
|
|
||||||
if (serviceProxyHandler) {
|
const serviceProxyHandler = widget.proxyHandler || genericProxyHandler;
|
||||||
if (serviceProxyHandler instanceof Function) {
|
|
||||||
return serviceProxyHandler(req, res);
|
if (serviceProxyHandler instanceof Function) {
|
||||||
|
// map opaque endpoints to their actual endpoint
|
||||||
|
const mapping = widget?.mappings?.[req.query.endpoint];
|
||||||
|
const map = mapping?.map;
|
||||||
|
const endpoint = mapping?.endpoint;
|
||||||
|
const endpointProxy = mapping?.proxyHandler;
|
||||||
|
|
||||||
|
if (!endpoint) {
|
||||||
|
logger.debug("Unsupported service endpoint: %s", type);
|
||||||
|
return res.status(403).json({ error: "Unsupported service endpoint" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { proxy, maps } = serviceProxyHandler;
|
req.query.endpoint = endpoint;
|
||||||
if (proxy) {
|
|
||||||
return proxy(req, res, maps);
|
if (endpointProxy instanceof Function) {
|
||||||
|
return endpointProxy(req, res, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return serviceProxyHandler(req, res, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("Unknown proxy service type: %s", type);
|
logger.debug("Unknown proxy service type: %s", type);
|
||||||
return res.status(403).json({ error: "Unkown proxy service type" });
|
return res.status(403).json({ error: "Unkown proxy service type" });
|
||||||
}
|
} catch (ex) {
|
||||||
catch (ex) {
|
|
||||||
logger.error(ex);
|
logger.error(ex);
|
||||||
return res.status(500).send({ error: "Unexpected error" });
|
return res.status(500).send({ error: "Unexpected error" });
|
||||||
}
|
}
|
||||||
|
@ -1,44 +1,44 @@
|
|||||||
const formats = {
|
// const formats = {
|
||||||
emby: `{url}/emby/{endpoint}?api_key={key}`,
|
// emby: `{url}/emby/{endpoint}?api_key={key}`,
|
||||||
jellyfin: `{url}/emby/{endpoint}?api_key={key}`,
|
// jellyfin: `{url}/emby/{endpoint}?api_key={key}`,
|
||||||
pihole: `{url}/admin/{endpoint}`,
|
// pihole: `{url}/admin/{endpoint}`,
|
||||||
radarr: `{url}/api/v3/{endpoint}?apikey={key}`,
|
// radarr: `{url}/api/v3/{endpoint}?apikey={key}`,
|
||||||
sonarr: `{url}/api/v3/{endpoint}?apikey={key}`,
|
// sonarr: `{url}/api/v3/{endpoint}?apikey={key}`,
|
||||||
speedtest: `{url}/api/{endpoint}`,
|
// speedtest: `{url}/api/{endpoint}`,
|
||||||
tautulli: `{url}/api/v2?apikey={key}&cmd={endpoint}`,
|
// tautulli: `{url}/api/v2?apikey={key}&cmd={endpoint}`,
|
||||||
traefik: `{url}/api/{endpoint}`,
|
// traefik: `{url}/api/{endpoint}`,
|
||||||
portainer: `{url}/api/endpoints/{env}/{endpoint}`,
|
// portainer: `{url}/api/endpoints/{env}/{endpoint}`,
|
||||||
rutorrent: `{url}/plugins/httprpc/action.php`,
|
// rutorrent: `{url}/plugins/httprpc/action.php`,
|
||||||
transmission: `{url}/transmission/rpc`,
|
// transmission: `{url}/transmission/rpc`,
|
||||||
qbittorrent: `{url}/api/v2/{endpoint}`,
|
// qbittorrent: `{url}/api/v2/{endpoint}`,
|
||||||
jellyseerr: `{url}/api/v1/{endpoint}`,
|
// jellyseerr: `{url}/api/v1/{endpoint}`,
|
||||||
overseerr: `{url}/api/v1/{endpoint}`,
|
// overseerr: `{url}/api/v1/{endpoint}`,
|
||||||
ombi: `{url}/api/v1/{endpoint}`,
|
// ombi: `{url}/api/v1/{endpoint}`,
|
||||||
npm: `{url}/api/{endpoint}`,
|
// npm: `{url}/api/{endpoint}`,
|
||||||
lidarr: `{url}/api/v1/{endpoint}?apikey={key}`,
|
// lidarr: `{url}/api/v1/{endpoint}?apikey={key}`,
|
||||||
readarr: `{url}/api/v1/{endpoint}?apikey={key}`,
|
// readarr: `{url}/api/v1/{endpoint}?apikey={key}`,
|
||||||
bazarr: `{url}/api/{endpoint}/wanted?apikey={key}`,
|
// bazarr: `{url}/api/{endpoint}/wanted?apikey={key}`,
|
||||||
sabnzbd: `{url}/api/?apikey={key}&output=json&mode={endpoint}`,
|
// sabnzbd: `{url}/api/?apikey={key}&output=json&mode={endpoint}`,
|
||||||
coinmarketcap: `https://pro-api.coinmarketcap.com/{endpoint}`,
|
// coinmarketcap: `https://pro-api.coinmarketcap.com/{endpoint}`,
|
||||||
gotify: `{url}/{endpoint}`,
|
// gotify: `{url}/{endpoint}`,
|
||||||
prowlarr: `{url}/api/v1/{endpoint}`,
|
// prowlarr: `{url}/api/v1/{endpoint}`,
|
||||||
jackett: `{url}/api/v2.0/{endpoint}?apikey={key}&configured=true`,
|
// jackett: `{url}/api/v2.0/{endpoint}?apikey={key}&configured=true`,
|
||||||
adguard: `{url}/control/{endpoint}`,
|
// adguard: `{url}/control/{endpoint}`,
|
||||||
strelaysrv: `{url}/{endpoint}`,
|
// strelaysrv: `{url}/{endpoint}`,
|
||||||
mastodon: `{url}/api/v1/{endpoint}`,
|
// mastodon: `{url}/api/v1/{endpoint}`,
|
||||||
};
|
// };
|
||||||
|
|
||||||
export function formatApiCall(api, args) {
|
export function formatApiCall(url, args) {
|
||||||
const find = /\{.*?\}/g;
|
const find = /\{.*?\}/g;
|
||||||
const replace = (match) => {
|
const replace = (match) => {
|
||||||
const key = match.replace(/\{|\}/g, "");
|
const key = match.replace(/\{|\}/g, "");
|
||||||
return args[key];
|
return args[key];
|
||||||
};
|
};
|
||||||
|
|
||||||
return formats[api].replace(find, replace);
|
return url.replace(find, replace);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatApiUrl(widget, endpoint) {
|
export function formatProxyUrl(widget, endpoint) {
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
type: widget.type,
|
type: widget.type,
|
||||||
group: widget.service_group,
|
group: widget.service_group,
|
||||||
@ -47,3 +47,23 @@ export function formatApiUrl(widget, endpoint) {
|
|||||||
});
|
});
|
||||||
return `/api/services/proxy?${params.toString()}`;
|
return `/api/services/proxy?${params.toString()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function asJson(data) {
|
||||||
|
if (data?.length > 0) {
|
||||||
|
const json = JSON.parse(data.toString());
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function jsonArrayTransform(data, transform) {
|
||||||
|
const json = asJson(data);
|
||||||
|
if (json instanceof Array) {
|
||||||
|
return transform(json);
|
||||||
|
}
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function jsonArrayFilter(data, filter) {
|
||||||
|
return jsonArrayTransform(data, (items) => items.filter(filter));
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import getServiceWidget from "utils/service-helpers";
|
import getServiceWidget from "utils/service-helpers";
|
||||||
import { formatApiCall } from "utils/api-helpers";
|
import { formatApiCall } from "utils/api-helpers";
|
||||||
import { httpProxy } from "utils/http";
|
import { httpProxy } from "utils/http";
|
||||||
|
import widgets from "widgets/widgets";
|
||||||
|
|
||||||
export default async function credentialedProxyHandler(req, res) {
|
export default async function credentialedProxyHandler(req, res) {
|
||||||
const { group, service, endpoint } = req.query;
|
const { group, service, endpoint } = req.query;
|
||||||
@ -8,8 +9,12 @@ export default async function credentialedProxyHandler(req, res) {
|
|||||||
if (group && service) {
|
if (group && service) {
|
||||||
const widget = await getServiceWidget(group, service);
|
const widget = await getServiceWidget(group, service);
|
||||||
|
|
||||||
|
if (!widgets?.[widget.type]?.api) {
|
||||||
|
return res.status(403).json({ error: "Service does not support API calls" });
|
||||||
|
}
|
||||||
|
|
||||||
if (widget) {
|
if (widget) {
|
||||||
const url = new URL(formatApiCall(widget.type, { endpoint, ...widget }));
|
const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget }));
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
@ -2,17 +2,22 @@ import getServiceWidget from "utils/service-helpers";
|
|||||||
import { formatApiCall } from "utils/api-helpers";
|
import { formatApiCall } from "utils/api-helpers";
|
||||||
import { httpProxy } from "utils/http";
|
import { httpProxy } from "utils/http";
|
||||||
import createLogger from "utils/logger";
|
import createLogger from "utils/logger";
|
||||||
|
import widgets from "widgets/widgets";
|
||||||
|
|
||||||
const logger = createLogger('genericProxyHandler');
|
const logger = createLogger("genericProxyHandler");
|
||||||
|
|
||||||
export default async function genericProxyHandler(req, res, maps) {
|
export default async function genericProxyHandler(req, res, map) {
|
||||||
const { group, service, endpoint } = req.query;
|
const { group, service, endpoint } = req.query;
|
||||||
|
|
||||||
if (group && service) {
|
if (group && service) {
|
||||||
const widget = await getServiceWidget(group, service);
|
const widget = await getServiceWidget(group, service);
|
||||||
|
|
||||||
|
if (!widgets?.[widget.type]?.api) {
|
||||||
|
return res.status(403).json({ error: "Service does not support API calls" });
|
||||||
|
}
|
||||||
|
|
||||||
if (widget) {
|
if (widget) {
|
||||||
const url = new URL(formatApiCall(widget.type, { endpoint, ...widget }));
|
const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget }));
|
||||||
|
|
||||||
let headers;
|
let headers;
|
||||||
if (widget.username && widget.password) {
|
if (widget.username && widget.password) {
|
||||||
@ -27,8 +32,8 @@ export default async function genericProxyHandler(req, res, maps) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let resultData = data;
|
let resultData = data;
|
||||||
if ((status === 200) && (maps?.[endpoint])) {
|
if (status === 200 && map) {
|
||||||
resultData = maps[endpoint](data);
|
resultData = map(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contentType) res.setHeader("Content-Type", contentType);
|
if (contentType) res.setHeader("Content-Type", contentType);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import getServiceWidget from "utils/service-helpers";
|
import getServiceWidget from "utils/service-helpers";
|
||||||
import { formatApiCall } from "utils/api-helpers";
|
import { formatApiCall } from "utils/api-helpers";
|
||||||
|
import widgets from "widgets/widgets";
|
||||||
|
|
||||||
export default async function npmProxyHandler(req, res) {
|
export default async function npmProxyHandler(req, res) {
|
||||||
const { group, service, endpoint } = req.query;
|
const { group, service, endpoint } = req.query;
|
||||||
@ -7,8 +8,12 @@ export default async function npmProxyHandler(req, res) {
|
|||||||
if (group && service) {
|
if (group && service) {
|
||||||
const widget = await getServiceWidget(group, service);
|
const widget = await getServiceWidget(group, service);
|
||||||
|
|
||||||
|
if (!widgets?.[widget.type]?.api) {
|
||||||
|
return res.status(403).json({ error: "Service does not support API calls" });
|
||||||
|
}
|
||||||
|
|
||||||
if (widget) {
|
if (widget) {
|
||||||
const url = new URL(formatApiCall(widget.type, { endpoint, ...widget }));
|
const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget }));
|
||||||
|
|
||||||
const loginUrl = `${widget.url}/api/tokens`;
|
const loginUrl = `${widget.url}/api/tokens`;
|
||||||
const body = { identity: widget.username, secret: widget.password };
|
const body = { identity: widget.username, secret: widget.password };
|
||||||
|
@ -12,15 +12,15 @@ async function login(widget, params) {
|
|||||||
return fetch(loginUrl, {
|
return fetch(loginUrl, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||||
body: loginBody
|
body: loginBody,
|
||||||
})
|
})
|
||||||
.then(async response => {
|
.then(async (response) => {
|
||||||
addCookieToJar(loginUrl, response.headers);
|
addCookieToJar(loginUrl, response.headers);
|
||||||
setCookieHeader(loginUrl, params);
|
setCookieHeader(loginUrl, params);
|
||||||
const data = await response.text();
|
const data = await response.text();
|
||||||
return ([response.status, data]);
|
return [response.status, data];
|
||||||
})
|
})
|
||||||
.catch(err => ([500, err]));
|
.catch((err) => [500, err]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function qbittorrentProxyHandler(req, res) {
|
export default async function qbittorrentProxyHandler(req, res) {
|
||||||
@ -46,7 +46,7 @@ export default async function qbittorrentProxyHandler(req, res) {
|
|||||||
if (status !== 200) {
|
if (status !== 200) {
|
||||||
return res.status(status).end(data);
|
return res.status(status).end(data);
|
||||||
}
|
}
|
||||||
if (data.toString() !== 'Ok.') {
|
if (data.toString() !== "Ok.") {
|
||||||
return res.status(401).end(data);
|
return res.status(401).end(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
8
src/widgets/components.js
Normal file
8
src/widgets/components.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import dynamic from "next/dynamic";
|
||||||
|
|
||||||
|
const components = {
|
||||||
|
overseerr: dynamic(() => import("./overseerr/component")),
|
||||||
|
radarr: dynamic(() => import("./radarr/component")),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default components;
|
36
src/widgets/overseerr/component.jsx
Normal file
36
src/widgets/overseerr/component.jsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import useSWR from "swr";
|
||||||
|
import { useTranslation } from "next-i18next";
|
||||||
|
|
||||||
|
import Widget from "components/services/widgets/widget";
|
||||||
|
import Block from "components/services/widgets/block";
|
||||||
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
|
export default function Component({ service }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const config = service.widget;
|
||||||
|
|
||||||
|
const { data: statsData, error: statsError } = useSWR(formatProxyUrl(config, "request/count"));
|
||||||
|
|
||||||
|
if (statsError) {
|
||||||
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!statsData) {
|
||||||
|
return (
|
||||||
|
<Widget>
|
||||||
|
<Block label={t("overseerr.pending")} />
|
||||||
|
<Block label={t("overseerr.approved")} />
|
||||||
|
<Block label={t("overseerr.available")} />
|
||||||
|
</Widget>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Widget>
|
||||||
|
<Block label={t("overseerr.pending")} value={statsData.pending} />
|
||||||
|
<Block label={t("overseerr.approved")} value={statsData.approved} />
|
||||||
|
<Block label={t("overseerr.available")} value={statsData.available} />
|
||||||
|
</Widget>
|
||||||
|
);
|
||||||
|
}
|
14
src/widgets/overseerr/widget.js
Normal file
14
src/widgets/overseerr/widget.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import credentialedProxyHandler from "utils/proxies/credentialed";
|
||||||
|
|
||||||
|
const widget = {
|
||||||
|
api: "{url}/api/v1/{endpoint}",
|
||||||
|
proxyHandler: credentialedProxyHandler,
|
||||||
|
|
||||||
|
mappings: {
|
||||||
|
"request/count": {
|
||||||
|
endpoint: "request/count",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default widget;
|
37
src/widgets/radarr/component.jsx
Normal file
37
src/widgets/radarr/component.jsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import useSWR from "swr";
|
||||||
|
import { useTranslation } from "next-i18next";
|
||||||
|
|
||||||
|
import Widget from "components/services/widgets/widget";
|
||||||
|
import Block from "components/services/widgets/block";
|
||||||
|
import { formatProxyUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
|
export default function Component({ service }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const config = service.widget;
|
||||||
|
|
||||||
|
const { data: moviesData, error: moviesError } = useSWR(formatProxyUrl(config, "movie"));
|
||||||
|
const { data: queuedData, error: queuedError } = useSWR(formatProxyUrl(config, "queue/status"));
|
||||||
|
|
||||||
|
if (moviesError || queuedError) {
|
||||||
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!moviesData || !queuedData) {
|
||||||
|
return (
|
||||||
|
<Widget>
|
||||||
|
<Block label={t("radarr.wanted")} />
|
||||||
|
<Block label={t("radarr.queued")} />
|
||||||
|
<Block label={t("radarr.movies")} />
|
||||||
|
</Widget>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Widget>
|
||||||
|
<Block label={t("radarr.wanted")} value={moviesData.wanted} />
|
||||||
|
<Block label={t("radarr.queued")} value={queuedData.totalCount} />
|
||||||
|
<Block label={t("radarr.movies")} value={moviesData.have} />
|
||||||
|
</Widget>
|
||||||
|
);
|
||||||
|
}
|
22
src/widgets/radarr/widget.js
Normal file
22
src/widgets/radarr/widget.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import genericProxyHandler from "utils/proxies/generic";
|
||||||
|
import { jsonArrayFilter } from "utils/api-helpers";
|
||||||
|
|
||||||
|
const widget = {
|
||||||
|
api: "{url}/api/v3/{endpoint}?apikey={key}",
|
||||||
|
proxyHandler: genericProxyHandler,
|
||||||
|
|
||||||
|
mappings: {
|
||||||
|
movie: {
|
||||||
|
endpoint: "movie",
|
||||||
|
map: (data) => ({
|
||||||
|
wanted: jsonArrayFilter(data, (item) => item.isAvailable === false).length,
|
||||||
|
have: jsonArrayFilter(data, (item) => item.isAvailable === true).length,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
"queue/status": {
|
||||||
|
endpoint: "queue/status",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default widget;
|
9
src/widgets/widgets.js
Normal file
9
src/widgets/widgets.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import overseerr from "./overseerr/widget";
|
||||||
|
import radarr from "./radarr/widget";
|
||||||
|
|
||||||
|
const widgets = {
|
||||||
|
overseerr,
|
||||||
|
radarr,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default widgets;
|
@ -4,7 +4,11 @@ const tailwindScrollbars = require("tailwind-scrollbar");
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
darkMode: "class",
|
darkMode: "class",
|
||||||
content: ["./src/pages/**/*.{js,ts,jsx,tsx}", "./src/components/**/*.{js,ts,jsx,tsx}"],
|
content: [
|
||||||
|
"./src/pages/**/*.{js,ts,jsx,tsx}",
|
||||||
|
"./src/components/**/*.{js,ts,jsx,tsx}",
|
||||||
|
"./src/widgets/**/*.{js,ts,jsx,tsx}",
|
||||||
|
],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user