diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index 150afbd90..50e796f58 100755
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -354,6 +354,16 @@
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
+ "healthchecks": {
+ "new": "New",
+ "up": "Online",
+ "grace": "In Grace Period",
+ "down": "Offline",
+ "paused": "Paused",
+ "status": "Status",
+ "last_ping": "Last Ping",
+ "never": "No pings yet"
+ },
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
diff --git a/src/widgets/components.js b/src/widgets/components.js
index fb64e2b02..cfd4d01a6 100644
--- a/src/widgets/components.js
+++ b/src/widgets/components.js
@@ -21,6 +21,7 @@ const components = {
grafana: dynamic(() => import("./grafana/component")),
hdhomerun: dynamic(() => import("./hdhomerun/component")),
homebridge: dynamic(() => import("./homebridge/component")),
+ healthchecks: dynamic(() => import("./healthchecks/component")),
jackett: dynamic(() => import("./jackett/component")),
jellyfin: dynamic(() => import("./emby/component")),
jellyseerr: dynamic(() => import("./jellyseerr/component")),
diff --git a/src/widgets/healthchecks/component.jsx b/src/widgets/healthchecks/component.jsx
new file mode 100644
index 000000000..810cec0d2
--- /dev/null
+++ b/src/widgets/healthchecks/component.jsx
@@ -0,0 +1,56 @@
+import { useTranslation } from "next-i18next";
+
+import Block from "components/services/widget/block";
+import Container from "components/services/widget/container";
+import useWidgetAPI from "utils/proxy/use-widget-api";
+
+function formatDate(dateString) {
+ const date = new Date(dateString);
+ const now = new Date();
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
+ const diff = (now - date) / 1000;
+
+ if (date > today && diff < 86400) {
+ return date.toLocaleTimeString([], {
+ hour: "numeric",
+ minute: "numeric",
+ });
+ }
+
+ return date.toLocaleDateString([], {
+ month: "short",
+ day: "numeric",
+ hour: "numeric",
+ minute: "numeric",
+ });
+}
+
+export default function Component({ service }) {
+ const { t } = useTranslation();
+ const { widget } = service;
+
+ const { data, error } = useWidgetAPI(widget, "checks");
+
+ if (error) {
+ return ;
+ }
+
+ if (!data) {
+ return (
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+ );
+}
diff --git a/src/widgets/healthchecks/widget.js b/src/widgets/healthchecks/widget.js
new file mode 100644
index 000000000..3f7569c90
--- /dev/null
+++ b/src/widgets/healthchecks/widget.js
@@ -0,0 +1,18 @@
+import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
+
+const widget = {
+ api: "https://healthchecks.io/api/v2/{endpoint}/{uuid}",
+ proxyHandler: credentialedProxyHandler,
+
+ mappings: {
+ checks: {
+ endpoint: "checks",
+ validate: [
+ "status",
+ "last_ping",
+ ]
+ },
+ },
+};
+
+export default widget;
diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js
index c9f299cba..7df127764 100644
--- a/src/widgets/widgets.js
+++ b/src/widgets/widgets.js
@@ -16,6 +16,7 @@ import gotify from "./gotify/widget";
import grafana from "./grafana/widget";
import hdhomerun from "./hdhomerun/widget";
import homebridge from "./homebridge/widget";
+import healthchecks from "./healthchecks/widget";
import jackett from "./jackett/widget";
import jellyseerr from "./jellyseerr/widget";
import komga from "./komga/widget";
@@ -87,6 +88,7 @@ const widgets = {
grafana,
hdhomerun,
homebridge,
+ healthchecks,
jackett,
jellyfin: emby,
jellyseerr,