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 (
+
+ );
+}
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.