+ {{-- Up/down counts change moderately; refresh on the 30s "data only" cadence, not every 5s. --}}
+ {!! Form::hidden('config[dataonly]', '1') !!}
+
diff --git a/Healthchecks/livestats.blade.php b/Healthchecks/livestats.blade.php
new file mode 100644
index 0000000000..109206a02a
--- /dev/null
+++ b/Healthchecks/livestats.blade.php
@@ -0,0 +1,10 @@
+
+
+ Up
+ {!! $up ?? 0 !!}
+
+
+ Down
+ {!! $down ?? 0 !!}
+
+
From bb35076745f8c365234567818b7c3d550be0e896 Mon Sep 17 00:00:00 2001
From: KodeStar
Date: Fri, 11 Sep 2026 14:53:35 +0100
Subject: [PATCH 2/2] Guard Healthchecks livestats against error responses
The Management API returns 401 with a { "error": ... } body when the
API key is missing or revoked, and execute() is configured with
http_errors => false, so that body reaches livestats() as a stdClass
with no "checks" property. Reading it directly raised an undefined
property warning, which Laravel promotes to an ErrorException, so
get_stats returned a 500 and the tile broke instead of falling back to
zeroed counts.
Coalesce the property to null so a malformed or error response leaves
the tile inactive, and flip the status to active only when a real
checks array came back - matching the KuvaszUptime pattern this app
follows.
---
Healthchecks/Healthchecks.php | 43 ++++++++++++++++++-----------------
1 file changed, 22 insertions(+), 21 deletions(-)
diff --git a/Healthchecks/Healthchecks.php b/Healthchecks/Healthchecks.php
index 2997da432f..fed0a889d9 100644
--- a/Healthchecks/Healthchecks.php
+++ b/Healthchecks/Healthchecks.php
@@ -18,6 +18,7 @@ public function test()
public function livestats()
{
+ $status = 'inactive';
$data = [
'up' => 0,
'down' => 0,
@@ -26,30 +27,30 @@ public function livestats()
// Single authenticated GET. execute() returns null on a failed
// connection (it never throws), so guard before reading the body.
$res = parent::execute($this->url('api/v3/checks/'), $this->getAttrs());
- if ($res === null) {
- return parent::getLiveStats('inactive', $data);
- }
-
- // Management API returns { "checks": [ ... ] }; each check has
- // status of new, up, grace, down, or paused.
- $body = json_decode($res->getBody());
- $checks = is_object($body) ? $body->checks : [];
- if (!is_array($checks)) {
- return parent::getLiveStats('inactive', $data);
- }
-
- foreach ($checks as $check) {
- if (!isset($check->status)) {
- continue;
- }
- if ($check->status === 'up') {
- $data['up']++;
- } elseif ($check->status === 'down') {
- $data['down']++;
+ if ($res !== null) {
+ // Management API returns { "checks": [ ... ] }; each check has a
+ // status of new, up, grace, down, or paused. Errors (a bad API
+ // key returns 401 with { "error": ... }) carry no "checks" key,
+ // so coalesce to null rather than reading the property directly
+ // and leave the tile inactive in that case.
+ $body = json_decode($res->getBody());
+ $checks = $body->checks ?? null;
+ if (is_array($checks)) {
+ $status = 'active';
+ foreach ($checks as $check) {
+ if (!isset($check->status)) {
+ continue;
+ }
+ if ($check->status === 'up') {
+ $data['up']++;
+ } elseif ($check->status === 'down') {
+ $data['down']++;
+ }
+ }
}
}
- return parent::getLiveStats('inactive', $data);
+ return parent::getLiveStats($status, $data);
}
private function getAttrs()