diff --git a/docs/widgets/services/filebrowser.md b/docs/widgets/services/filebrowser.md
new file mode 100644
index 000000000..24fe629fd
--- /dev/null
+++ b/docs/widgets/services/filebrowser.md
@@ -0,0 +1,19 @@
+---
+title: Filebrowser
+description: Filebrowser Widget Configuration
+---
+
+Learn more about [Filebrowser](https://filebrowser.org).
+
+If you are using [Proxy header authentication](https://filebrowser.org/configuration/authentication-method#proxy-header) you have to set `authHeader` and `username`.
+
+Allowed fields: `["available", "used", "total"]`.
+
+```yaml
+widget:
+ type: filebrowser
+ url: http://filebrowserhostorip:port
+ username: username
+ password: password
+ authHeader: X-My-Header # If using Proxy header authentication
+```
diff --git a/docs/widgets/services/index.md b/docs/widgets/services/index.md
index 80ff72ba0..0d0db2030 100644
--- a/docs/widgets/services/index.md
+++ b/docs/widgets/services/index.md
@@ -33,6 +33,7 @@ You can also find a list of all available service widgets in the sidebar navigat
- [Emby](emby.md)
- [ESPHome](esphome.md)
- [EVCC](evcc.md)
+- [Filebrowser](filebrowser.md)
- [Fileflows](fileflows.md)
- [Firefly III](firefly.md)
- [Flood](flood.md)
diff --git a/mkdocs.yml b/mkdocs.yml
index 8bb19e435..adde9180d 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -56,6 +56,7 @@ nav:
- widgets/services/emby.md
- widgets/services/esphome.md
- widgets/services/evcc.md
+ - widgets/services/filebrowser.md
- widgets/services/fileflows.md
- widgets/services/firefly.md
- widgets/services/flood.md
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index a4bc77210..f37329e46 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -1072,6 +1072,11 @@
"stacks": "Stacks",
"containers": "Containers"
},
+ "filebrowser": {
+ "available": "Available",
+ "used": "Used",
+ "total": "Total"
+ },
"wallos": {
"activeSubscriptions": "Subscriptions",
"thisMonthlyCost": "This Month",
diff --git a/src/widgets/components.js b/src/widgets/components.js
index b67ebc7ea..9cb02c2af 100644
--- a/src/widgets/components.js
+++ b/src/widgets/components.js
@@ -31,6 +31,7 @@ const components = {
emby: dynamic(() => import("./emby/component")),
esphome: dynamic(() => import("./esphome/component")),
evcc: dynamic(() => import("./evcc/component")),
+ filebrowser: dynamic(() => import("./filebrowser/component")),
fileflows: dynamic(() => import("./fileflows/component")),
firefly: dynamic(() => import("./firefly/component")),
flood: dynamic(() => import("./flood/component")),
diff --git a/src/widgets/filebrowser/component.jsx b/src/widgets/filebrowser/component.jsx
new file mode 100644
index 000000000..cf5a28000
--- /dev/null
+++ b/src/widgets/filebrowser/component.jsx
@@ -0,0 +1,38 @@
+import Block from "components/services/widget/block";
+import Container from "components/services/widget/container";
+import { useTranslation } from "next-i18next";
+
+import useWidgetAPI from "utils/proxy/use-widget-api";
+
+export default function Component({ service }) {
+ const { t } = useTranslation();
+
+ const { widget } = service;
+
+ const { data: usage, error: usageError } = useWidgetAPI(widget, "usage");
+
+ if (usageError) {
+ return ;
+ }
+
+ if (!usage) {
+ return (
+
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+ );
+}
diff --git a/src/widgets/filebrowser/proxy.js b/src/widgets/filebrowser/proxy.js
new file mode 100644
index 000000000..f416c2873
--- /dev/null
+++ b/src/widgets/filebrowser/proxy.js
@@ -0,0 +1,80 @@
+import getServiceWidget from "utils/config/service-helpers";
+import createLogger from "utils/logger";
+import { formatApiCall } from "utils/proxy/api-helpers";
+import { httpProxy } from "utils/proxy/http";
+import widgets from "widgets/widgets";
+
+const proxyName = "filebrowserProxyHandler";
+const logger = createLogger(proxyName);
+
+async function login(widget, service) {
+ const url = formatApiCall(widgets[widget.type].api, { ...widget, endpoint: "login" });
+ const headers = {};
+ if (widget.authHeader) {
+ headers[widget.authHeader] = widget.username;
+ }
+ const [status, , data] = await httpProxy(url, {
+ method: "POST",
+ headers,
+ body: JSON.stringify({
+ username: widget.username,
+ password: widget.password,
+ }),
+ });
+
+ switch (status) {
+ case 200:
+ return data;
+ case 401:
+ logger.error("Unauthorized access to Filebrowser API for service '%s'. Check credentials.", service);
+ break;
+ default:
+ logger.error("Unexpected status code %d when logging in to Filebrowser API for service '%s'", status, service);
+ break;
+ }
+}
+
+export default async function filebrowserProxyHandler(req, res) {
+ const { group, service, endpoint, index } = req.query;
+
+ if (!group || !service) {
+ logger.error("Invalid or missing service '%s' or group '%s'", service, group);
+ return res.status(400).json({ error: "Invalid proxy service type" });
+ }
+
+ const widget = await getServiceWidget(group, service, index);
+ if (!widget || !widgets[widget.type].api) {
+ logger.error("Invalid or missing widget for service '%s' in group '%s'", service, group);
+ return res.status(400).json({ error: "Invalid widget configuration" });
+ }
+
+ const token = await login(widget, service);
+ if (!token) {
+ return res.status(500).json({ error: "Failed to authenticate with Filebrowser" });
+ }
+
+ const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget }));
+
+ try {
+ const params = {
+ method: "GET",
+ headers: {
+ "X-AUTH": token,
+ },
+ };
+
+ logger.debug("Calling Filebrowser API endpoint: %s", endpoint);
+
+ const [status, , data] = await httpProxy(url, params);
+
+ if (status !== 200) {
+ logger.error("Error calling Filebrowser API: %d. Data: %s", status, data);
+ return res.status(status).json({ error: "Filebrowser API Error", data });
+ }
+
+ return res.status(status).send(data);
+ } catch (error) {
+ logger.error("Exception calling Filebrowser API: %s", error.message);
+ return res.status(500).json({ error: "Filebrowser API Error", message: error.message });
+ }
+}
diff --git a/src/widgets/filebrowser/widget.js b/src/widgets/filebrowser/widget.js
new file mode 100644
index 000000000..8ab0d9b13
--- /dev/null
+++ b/src/widgets/filebrowser/widget.js
@@ -0,0 +1,14 @@
+import filebrowserProxyHandler from "./proxy";
+
+const widget = {
+ api: "{url}/api/{endpoint}",
+ proxyHandler: filebrowserProxyHandler,
+
+ mappings: {
+ usage: {
+ endpoint: "usage",
+ },
+ },
+};
+
+export default widget;
diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js
index c1865fce3..cfd28c590 100644
--- a/src/widgets/widgets.js
+++ b/src/widgets/widgets.js
@@ -25,6 +25,7 @@ import downloadstation from "./downloadstation/widget";
import emby from "./emby/widget";
import esphome from "./esphome/widget";
import evcc from "./evcc/widget";
+import filebrowser from "./filebrowser/widget";
import fileflows from "./fileflows/widget";
import firefly from "./firefly/widget";
import flood from "./flood/widget";
@@ -167,6 +168,7 @@ const widgets = {
emby,
esphome,
evcc,
+ filebrowser,
fileflows,
firefly,
flood,