From 4eabdc1e5ea0533fc6f8c1028434427d5f8a3fde Mon Sep 17 00:00:00 2001 From: Jason Fischer Date: Tue, 29 Nov 2022 20:11:50 -0800 Subject: [PATCH] Add Synology DiskStation widget closes #523 --- public/locales/en/common.json | 6 +++ src/widgets/components.js | 1 + src/widgets/diskstation/component.jsx | 41 ++++++++++++++++ src/widgets/diskstation/proxy.js | 70 +++++++++++++++++++++++++++ src/widgets/diskstation/widget.js | 14 ++++++ src/widgets/widgets.js | 2 + 6 files changed, 134 insertions(+) create mode 100644 src/widgets/diskstation/component.jsx create mode 100644 src/widgets/diskstation/proxy.js create mode 100644 src/widgets/diskstation/widget.js diff --git a/public/locales/en/common.json b/public/locales/en/common.json index dbef3ed34..4273fff86 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -118,6 +118,12 @@ "leech": "Leech", "seed": "Seed" }, + "diskstation": { + "download": "Download", + "upload": "Upload", + "leech": "Leech", + "seed": "Seed" + }, "sonarr": { "wanted": "Wanted", "queued": "Queued", diff --git a/src/widgets/components.js b/src/widgets/components.js index 68f114765..606d82913 100644 --- a/src/widgets/components.js +++ b/src/widgets/components.js @@ -8,6 +8,7 @@ const components = { changedetectionio: dynamic(() => import("./changedetectionio/component")), coinmarketcap: dynamic(() => import("./coinmarketcap/component")), deluge: dynamic(() => import("./deluge/component")), + diskstation: dynamic(() => import("./diskstation/component")), docker: dynamic(() => import("./docker/component")), emby: dynamic(() => import("./emby/component")), gluetun: dynamic(() => import("./gluetun/component")), diff --git a/src/widgets/diskstation/component.jsx b/src/widgets/diskstation/component.jsx new file mode 100644 index 000000000..3a87eebc3 --- /dev/null +++ b/src/widgets/diskstation/component.jsx @@ -0,0 +1,41 @@ +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: listData, error: listError } = useWidgetAPI(widget, "list"); + + if (listError) { + return ; + } + + const tasks = listData?.data?.tasks; + if (!tasks) { + return ( + + + + + + + ); + } + + const rateDl = tasks.reduce((acc, task) => acc + (task?.additional?.transfer?.speed_download ?? 0), 0); + const rateUl = tasks.reduce((acc, task) => acc + (task?.additional?.transfer?.speed_upload ?? 0), 0); + const completed = tasks.filter((task) => task?.additional?.transfer?.size_downloaded === task?.size)?.length || 0; + const leech = tasks.length - completed || 0; + + return ( + + + + + + + ); +} diff --git a/src/widgets/diskstation/proxy.js b/src/widgets/diskstation/proxy.js new file mode 100644 index 000000000..af7678152 --- /dev/null +++ b/src/widgets/diskstation/proxy.js @@ -0,0 +1,70 @@ +import { formatApiCall } from "utils/proxy/api-helpers"; +import { httpProxy } from "utils/proxy/http"; +import createLogger from "utils/logger"; +import widgets from "widgets/widgets"; +import getServiceWidget from "utils/config/service-helpers"; + +const logger = createLogger("diskstationProxyHandler"); +const authApi = "{url}/webapi/auth.cgi?api=SYNO.API.Auth&version=2&method=login&account={username}&passwd={password}&session=DownloadStation&format=cookie" + +async function login(widget) { + const loginUrl = formatApiCall(authApi, widget); + const [status, contentType, data] = await httpProxy(loginUrl); + if (status !== 200) { + return [status, contentType, data]; + } + + const json = JSON.parse(data.toString()); + if (json?.success !== true) { + // from https://global.download.synology.com/download/Document/Software/DeveloperGuide/Package/DownloadStation/All/enu/Synology_Download_Station_Web_API.pdf + /* + Code Description + 400 No such account or incorrect password + 401 Account disabled + 402 Permission denied + 403 2-step verification code required + 404 Failed to authenticate 2-step verification code + */ + let message = "Authentication failed."; + if (json?.error?.code >= 403) message += " 2FA enabled."; + logger.warn("Unable to login. Code: %d", json?.error?.code); + return [401, "application/json", JSON.stringify({ code: json?.error?.code, message })]; + } + + return [status, contentType, data]; +} + +export default async function diskstationProxyHandler(req, res) { + const { group, service, endpoint } = req.query; + + if (!group || !service) { + return res.status(400).json({ error: "Invalid proxy service type" }); + } + + const widget = await getServiceWidget(group, service); + const api = widgets?.[widget.type]?.api; + if (!api) { + return res.status(403).json({ error: "Service does not support API calls" }); + } + + const url = formatApiCall(api, { endpoint, ...widget }); + let [status, contentType, data] = await httpProxy(url); + if (status !== 200) { + logger.debug("Error %d calling endpoint %s", status, url); + return res.status(status, data); + } + + const json = JSON.parse(data.toString()); + if (json?.success !== true) { + logger.debug("Logging in to DiskStation"); + [status, contentType, data] = await login(widget); + if (status !== 200) { + return res.status(status).end(data) + } + + [status, contentType, data] = await httpProxy(url); + } + + if (contentType) res.setHeader("Content-Type", contentType); + return res.status(status).send(data); +} diff --git a/src/widgets/diskstation/widget.js b/src/widgets/diskstation/widget.js new file mode 100644 index 000000000..71187425d --- /dev/null +++ b/src/widgets/diskstation/widget.js @@ -0,0 +1,14 @@ +import diskstationProxyHandler from "./proxy"; + +const widget = { + api: "{url}/webapi/DownloadStation/task.cgi?api=SYNO.DownloadStation.Task&version=1&method={endpoint}", + proxyHandler: diskstationProxyHandler, + + mappings: { + "list": { + endpoint: "list&additional=transfer", + }, + }, +}; + +export default widget; diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js index 72417b77a..24dd6e5f9 100644 --- a/src/widgets/widgets.js +++ b/src/widgets/widgets.js @@ -5,6 +5,7 @@ import bazarr from "./bazarr/widget"; import changedetectionio from "./changedetectionio/widget"; import coinmarketcap from "./coinmarketcap/widget"; import deluge from "./deluge/widget"; +import diskstation from "./diskstation/widget"; import emby from "./emby/widget"; import gluetun from "./gluetun/widget"; import gotify from "./gotify/widget"; @@ -51,6 +52,7 @@ const widgets = { changedetectionio, coinmarketcap, deluge, + diskstation, emby, gluetun, gotify,