diff --git a/src/components/services/widget.jsx b/src/components/services/widget.jsx index 926f1156f..ca313588c 100644 --- a/src/components/services/widget.jsx +++ b/src/components/services/widget.jsx @@ -11,6 +11,7 @@ import Jellyfin from "./widgets/service/jellyfin"; import Speedtest from "./widgets/service/speedtest"; import Traefik from "./widgets/service/traefik"; import Jellyseerr from "./widgets/service/jellyseerr"; +import Npm from "./widgets/service/npm"; const widgetMappings = { docker: Docker, @@ -25,7 +26,8 @@ const widgetMappings = { rutorrent: Rutorrent, speedtest: Speedtest, traefik: Traefik, - jellyseerr: Jellyseerr + jellyseerr: Jellyseerr, + npm: Npm, }; export default function Widget({ service }) { diff --git a/src/components/services/widgets/service/npm.jsx b/src/components/services/widgets/service/npm.jsx new file mode 100644 index 000000000..222bf1bdf --- /dev/null +++ b/src/components/services/widgets/service/npm.jsx @@ -0,0 +1,65 @@ +import useSWR from "swr"; + +import Widget from "../widget"; +import Block from "../block"; + +export default function Npm({ service }) { + const config = service.widget; + const { url } = config; + + const fetcher = async (reqUrl) => { + const { url, username, password } = config; + const loginUrl = `${url}/api/tokens`; + const body = { identity: username, secret: password }; + + const res = await fetch(loginUrl, { + method: "POST", + body: JSON.stringify(body), + headers: { + "Content-Type": "application/json", + }, + }) + .then((response) => response.json()) + .then( + async (data) => + await fetch(reqUrl, { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + data.token, + }, + }) + ); + return res.json(); + }; + + const { data: infoData, error: infoError } = useSWR(`${url}/api/nginx/proxy-hosts`, fetcher); + + console.log(infoData); + + if (infoError) { + return ; + } + + if (!infoData) { + return ( + + + + + + ); + } + + const enabled = infoData.filter((c) => c.enabled === 1).length; + const disabled = infoData.filter((c) => c.enabled === 0).length; + const total = infoData.length; + + return ( + + + + + + ); +} diff --git a/src/components/widget.jsx b/src/components/widget.jsx index 72032b441..dc5bee1b0 100644 --- a/src/components/widget.jsx +++ b/src/components/widget.jsx @@ -1,12 +1,14 @@ import WeatherApi from "components/widgets/weather/weather"; import OpenWeatherMap from "components/widgets/openweathermap/weather"; import Resources from "components/widgets/resources/resources"; +import Search from "components/widgets/search/search"; const widgetMappings = { weather: WeatherApi, // This key will be deprecated in the future weatherapi: WeatherApi, openweathermap: OpenWeatherMap, resources: Resources, + search: Search, }; export default function Widget({ widget }) { diff --git a/src/components/widgets/resources/resources.jsx b/src/components/widgets/resources/resources.jsx index d5c5b37ae..5c7ef229c 100644 --- a/src/components/widgets/resources/resources.jsx +++ b/src/components/widgets/resources/resources.jsx @@ -5,7 +5,7 @@ import Memory from "./memory"; export default function Resources({ options }) { return ( <> -
+
{options.disk && } {options.cpu && } diff --git a/src/components/widgets/search/search.jsx b/src/components/widgets/search/search.jsx new file mode 100644 index 000000000..5ac41efd0 --- /dev/null +++ b/src/components/widgets/search/search.jsx @@ -0,0 +1,73 @@ +import { useState } from "react"; +import { FiSearch } from "react-icons/fi"; +import { FcGoogle } from "react-icons/fc"; +import { SiDuckduckgo } from "react-icons/si"; +import { SiMicrosoftbing } from "react-icons/si"; + +export default function Search({ options, classN }) { + const providers = ["google", "bing", "duckduckgo", "custom"]; + const targets = ["_blank", "_parent", "_top"]; + + const [query, setQuery] = useState(""); + + function search() { + if (!providers.includes(options.provider)) { + return; + } else { + if (options.provider === "custom") { + if (targets.includes(options.target)) { + window.open(options.customdata.url + query, options.target); + } else window.open(options.customdata.url + query, "_self"); + } else { + if (targets.includes(options.target)) { + window.open(`https://www.${options.provider}.com/search?q=` + query, `${options.target}`); + } else window.open(`https://www.${options.provider}.com/search?q=` + query, "_self"); + } + } + + setQuery(""); + } + + if (!options || (options.provider === "custom" && !options.customdata)) { + return <>; + } + + return ( +
+
+ {options.provider == "google" ? ( + + ) : options.provider == "duckduckgo" ? ( + + ) : options.provider == "bing" ? ( + + ) : options.provider == "custom" ? ( + options.customdata.abbr.length > 2 ? ( + options.customdata.abbr.substring(0, 2) + ) : ( + options.customdata.abbr + ) + ) : ( + "" + )} +
+ 1 ? "pl-12" : "pl-10" + } w-full text-sm text-gray-900 bg-gray-50 rounded-full border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 h-full`} + placeholder="Search..." + onChange={(s) => setQuery(s.currentTarget.value)} + required + /> + +
+ ); +} diff --git a/src/components/widgets/weather/weather.jsx b/src/components/widgets/weather/weather.jsx index 72565872e..f1c2d9018 100644 --- a/src/components/widgets/weather/weather.jsx +++ b/src/components/widgets/weather/weather.jsx @@ -33,7 +33,7 @@ export default function WeatherApi({ options }) { } return ( -
+
diff --git a/src/pages/index.js b/src/pages/index.js index d994b2d8f..99c1e695c 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -8,6 +8,7 @@ import ServicesGroup from "components/services/group"; import BookmarksGroup from "components/bookmarks/group"; import Widget from "components/widget"; import { ColorProvider } from "utils/color-context"; +import Search from "components/widgets/search/search"; const ThemeToggle = dynamic(() => import("components/theme-toggle"), { ssr: false, @@ -17,7 +18,7 @@ const ColorToggle = dynamic(() => import("components/color-toggle"), { ssr: false, }); -const rightAlignedWidgets = ["weatherapi", "openweathermap", "weather"]; +const rightAlignedWidgets = ["weatherapi", "openweathermap", "weather", "search"]; export default function Home() { const { data: services, error: servicesError } = useSWR("/api/services"); @@ -31,7 +32,7 @@ export default function Home() { Welcome
-
+
{widgets && ( <> {widgets @@ -39,12 +40,21 @@ export default function Home() { .map((widget, i) => ( ))} -
+ {widgets + .filter((widget) => widget.type === "search") + .map( + (widget, i) => + ??
+ )} {widgets .filter((widget) => rightAlignedWidgets.includes(widget.type)) - .map((widget, i) => ( - - ))} + .map((widget, i) => + widget.type === "search" ? ( + + ) : ( + + ) + )} )}
diff --git a/src/skeleton/services.yaml b/src/skeleton/services.yaml index d6a010ab4..bf721153e 100644 --- a/src/skeleton/services.yaml +++ b/src/skeleton/services.yaml @@ -5,6 +5,11 @@ - My First Service: href: http://localhost/ description: Homepage is awesome + # widget: + # type: npm # npm for NGINX Proxy Manager + # url: http://localhost # no slash at the end + # username: email@example.com # your email + # password: secretpassword # your password - My Second Group: - My Second Service: diff --git a/src/skeleton/widgets.yaml b/src/skeleton/widgets.yaml index 93cbb9e7a..676166a49 100644 --- a/src/skeleton/widgets.yaml +++ b/src/skeleton/widgets.yaml @@ -5,3 +5,9 @@ cpu: true memory: true disk: / +# - search: # Searchbar in widgets area +# provider: custom # Can be google, duckduckgo, bing or custom. +# target: _blank # Can be _blank, _top, _self or _parent. +# customdata: +# url: https://startpage.com/search?q= # Required for custom provider. Remember to add the q param as per your provider. +# abbr: G # Can be omitted. Only the first 2 characters will be considered.