diff --git a/docs/widgets/services/trilium.md b/docs/widgets/services/trilium.md
new file mode 100644
index 000000000..003157d20
--- /dev/null
+++ b/docs/widgets/services/trilium.md
@@ -0,0 +1,17 @@
+---
+title: Trilium
+description: Trilium Widget Configuration
+---
+
+Learn more about [Trilium](https://github.com/TriliumNext/Notes).
+
+This widget is compatible with [TriliumNext](https://github.com/TriliumNext/Notes) versions >= [v0.94.0](https://github.com/TriliumNext/Notes/releases/tag/v0.94.0).
+
+Find (or create) your ETAPI key under `Options > ETAPI > Create new ETAPI token`.
+
+```yaml
+widget:
+ type: trilium
+ url: https://trilium.host.or.ip
+ key: etapi_token
+```
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index 87895e6e0..43378ec1f 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -359,6 +359,12 @@
"services": "Services",
"middleware": "Middleware"
},
+ "trilium": {
+ "version": "Version",
+ "notesCount": "Notes",
+ "dbSize": "Database Size",
+ "unknown": "Unknown"
+ },
"navidrome": {
"nothing_streaming": "No Active Streams",
"please_wait": "Please Wait"
diff --git a/src/utils/proxy/handlers/credentialed.js b/src/utils/proxy/handlers/credentialed.js
index 6eb4f9a95..712273bd6 100644
--- a/src/utils/proxy/handlers/credentialed.js
+++ b/src/utils/proxy/handlers/credentialed.js
@@ -106,6 +106,8 @@ export default async function credentialedProxyHandler(req, res, map) {
} else {
headers.Authorization = widget.password;
}
+ } else if (widget.type === "trilium") {
+ headers.Authorization = widget.key;
} else if (widget.type === "gitlab") {
headers["PRIVATE-TOKEN"] = widget.key;
} else if (widget.type === "speedtest") {
diff --git a/src/widgets/components.js b/src/widgets/components.js
index 71c7f095a..92e057dc9 100644
--- a/src/widgets/components.js
+++ b/src/widgets/components.js
@@ -132,6 +132,7 @@ const components = {
tdarr: dynamic(() => import("./tdarr/component")),
traefik: dynamic(() => import("./traefik/component")),
transmission: dynamic(() => import("./transmission/component")),
+ trilium: dynamic(() => import("./trilium/component")),
tubearchivist: dynamic(() => import("./tubearchivist/component")),
truenas: dynamic(() => import("./truenas/component")),
unifi: dynamic(() => import("./unifi/component")),
diff --git a/src/widgets/trilium/component.jsx b/src/widgets/trilium/component.jsx
new file mode 100644
index 000000000..132c9a073
--- /dev/null
+++ b/src/widgets/trilium/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: metricsData, error: metricsError } = useWidgetAPI(widget, "metrics");
+
+ if (metricsError) {
+ return ;
+ }
+
+ if (!metricsData) {
+ return (
+
+
+
+
+
+ );
+ }
+
+ const version = metricsData.version?.app;
+ const notesCount = metricsData.database?.activeNotes || 0;
+ const databaseSizeBytes = metricsData.statistics?.databaseSizeBytes || 0;
+
+ return (
+
+
+
+
+
+ );
+}
diff --git a/src/widgets/trilium/widget.js b/src/widgets/trilium/widget.js
new file mode 100644
index 000000000..f3bbaccba
--- /dev/null
+++ b/src/widgets/trilium/widget.js
@@ -0,0 +1,15 @@
+import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
+
+const widget = {
+ api: "{url}/etapi/{endpoint}",
+ proxyHandler: credentialedProxyHandler,
+
+ mappings: {
+ metrics: {
+ endpoint: "metrics?format=json",
+ validate: ["version", "database"],
+ },
+ },
+};
+
+export default widget;
diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js
index b36ee5025..2a2df8c23 100644
--- a/src/widgets/widgets.js
+++ b/src/widgets/widgets.js
@@ -123,6 +123,7 @@ import tdarr from "./tdarr/widget";
import technitium from "./technitium/widget";
import traefik from "./traefik/widget";
import transmission from "./transmission/widget";
+import trilium from "./trilium/widget";
import truenas from "./truenas/widget";
import tubearchivist from "./tubearchivist/widget";
import unifi from "./unifi/widget";
@@ -266,6 +267,7 @@ const widgets = {
tdarr,
traefik,
transmission,
+ trilium,
tubearchivist,
truenas,
unifi,