From 0fc662ba4d60f757da1e940c2d34f3552082ef8a Mon Sep 17 00:00:00 2001 From: Heng-Yi Wu <2316687+henry40408@users.noreply.github.com> Date: Tue, 18 Apr 2023 22:33:04 +0800 Subject: [PATCH] feat: add FreshRSS widget --- public/locales/en/common.json | 4 +++ src/utils/proxy/handlers/credentialed.js | 23 +++++++++++---- src/widgets/components.js | 1 + src/widgets/freshrss/component.jsx | 37 ++++++++++++++++++++++++ src/widgets/freshrss/widget.js | 24 +++++++++++++++ src/widgets/widgets.js | 2 ++ 6 files changed, 86 insertions(+), 5 deletions(-) create mode 100644 src/widgets/freshrss/component.jsx create mode 100644 src/widgets/freshrss/widget.js diff --git a/public/locales/en/common.json b/public/locales/en/common.json index c46e6c38..adcda8ff 100755 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -98,6 +98,10 @@ "leech": "Leech", "seed": "Seed" }, + "freshrss": { + "subscriptions": "Subscriptions", + "unread": "Unread" + }, "changedetectionio": { "totalObserved": "Total Observed", "diffsDetected": "Diffs Detected" diff --git a/src/utils/proxy/handlers/credentialed.js b/src/utils/proxy/handlers/credentialed.js index 93cdb995..ae942a26 100644 --- a/src/utils/proxy/handlers/credentialed.js +++ b/src/utils/proxy/handlers/credentialed.js @@ -34,9 +34,8 @@ export default async function credentialedProxyHandler(req, res, map) { "ghostfolio", "truenas", "pterodactyl", - ].includes(widget.type)) - { - headers.Authorization = `Bearer ${widget.key}`; + ].includes(widget.type)) { + headers.Authorization = `Bearer ${widget.key}`; } else if (widget.type === "proxmox") { headers.Authorization = `PVEAPIToken=${widget.username}=${widget.password}`; } else if (widget.type === "proxmoxbackupserver") { @@ -54,6 +53,20 @@ export default async function credentialedProxyHandler(req, res, map) { } else { headers.Authorization = `Basic ${Buffer.from(`${widget.username}:${widget.password}`).toString("base64")}`; } + } else if (widget.type === "freshrss") { + const resp = await fetch(`${widget.url}/api/greader.php/accounts/ClientLogin`, { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded" + }, + body: new URLSearchParams({ + Email: widget.username, + Passwd: widget.password, + }) + }) + const text = await resp.text() + const [, token] = text.split('\n').find(line => line.startsWith('Auth=')).split('=') + headers.Authorization = `GoogleLogin auth=${token}` } else { headers["X-API-Key"] = `${widget.key}`; } @@ -78,10 +91,10 @@ export default async function credentialedProxyHandler(req, res, map) { if (status >= 400) { logger.error("HTTP Error %d calling %s", status, url.toString()); } - + if (status === 200) { if (!validateWidgetData(widget, endpoint, resultData)) { - return res.status(500).json({error: {message: "Invalid data", url: sanitizeErrorURL(url), data: resultData}}); + return res.status(500).json({ error: { message: "Invalid data", url: sanitizeErrorURL(url), data: resultData } }); } if (map) resultData = map(resultData); } diff --git a/src/widgets/components.js b/src/widgets/components.js index ec7be376..6ca5722e 100644 --- a/src/widgets/components.js +++ b/src/widgets/components.js @@ -17,6 +17,7 @@ const components = { emby: dynamic(() => import("./emby/component")), fileflows: dynamic(() => import("./fileflows/component")), flood: dynamic(() => import("./flood/component")), + freshrss: dynamic(() => import("./freshrss/component")), ghostfolio: dynamic(() => import("./ghostfolio/component")), gluetun: dynamic(() => import("./gluetun/component")), gotify: dynamic(() => import("./gotify/component")), diff --git a/src/widgets/freshrss/component.jsx b/src/widgets/freshrss/component.jsx new file mode 100644 index 00000000..ee4daf18 --- /dev/null +++ b/src/widgets/freshrss/component.jsx @@ -0,0 +1,37 @@ +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: subscriptionsData, error: subscriptionsError } = useWidgetAPI(widget, "subscriptions"); + const { data: unreadData, error: unreadError } = useWidgetAPI(widget, "unread"); + + if (subscriptionsError) { + return ; + } + if (unreadError) { + return ; + } + + if (!subscriptionsData || !unreadData) { + return ( + + + + + ); + } + + return ( + + + + + ); +} diff --git a/src/widgets/freshrss/widget.js b/src/widgets/freshrss/widget.js new file mode 100644 index 00000000..ea87fa38 --- /dev/null +++ b/src/widgets/freshrss/widget.js @@ -0,0 +1,24 @@ +import { asJson } from "utils/proxy/api-helpers"; +import credentialedProxyHandler from "utils/proxy/handlers/credentialed"; + +const widget = { + api: "{url}/api/greader.php/{endpoint}?output=json", + proxyHandler: credentialedProxyHandler, + + mappings: { + subscriptions: { + endpoint: "reader/api/0/subscription/list", + map: (data) => ({ + count: asJson(data).subscriptions.length + }), + }, + unread: { + endpoint: "reader/api/0/unread-count", + map: (data) => ({ + count: asJson(data).max + }), + } + } +}; + +export default widget; diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js index a8ce5282..dca03173 100644 --- a/src/widgets/widgets.js +++ b/src/widgets/widgets.js @@ -12,6 +12,7 @@ import downloadstation from "./downloadstation/widget"; import emby from "./emby/widget"; import fileflows from "./fileflows/widget"; import flood from "./flood/widget"; +import freshrss from "./freshrss/widget"; import ghostfolio from "./ghostfolio/widget" import gluetun from "./gluetun/widget"; import gotify from "./gotify/widget"; @@ -90,6 +91,7 @@ const widgets = { emby, fileflows, flood, + freshrss, ghostfolio, gluetun, gotify,