From c0f12ce93138e05d91c805e2b7129e4f9c668436 Mon Sep 17 00:00:00 2001 From: jwed Date: Sun, 24 May 2026 22:07:58 -0400 Subject: [PATCH] fix(reboot): suppress webhook alerts during a fleet reboot, regardless of cache driver Existing Cache::has('unifi:planned_reboot:{mac}') per-MAC suppression relies on the cache driver being shared across the scheduler and the snapshot-capture containers. In environments where the cache is backed by something process-local (or where the keys expire before a slow reboot completes), webhook alerts fire even though the dashboard itself initiated the reboots. RebootAllAps now also stamps a single Setting (unifi.reboot_suppression_until) at the start of a fleet reboot, covering a 20-minute window. WebhookCheckService checks this Setting in addition to the per-MAC cache key, in checkDeviceTransition and checkReboot. Setting is database-backed so it's always visible across containers regardless of cache configuration. v1.11.1. Co-Authored-By: Claude Opus 4.7 (1M context) --- composer.json | 2 +- src/Services/WebhookCheckService.php | 26 +++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index e278210..0879a2d 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "dashboard/unifi", "description": "UniFi network management, WiFi stats, and captive portal authentication for the Dashboard platform", - "version": "1.11.0", + "version": "1.11.1", "type": "library", "license": "MIT", "autoload": { diff --git a/src/Services/WebhookCheckService.php b/src/Services/WebhookCheckService.php index ef23178..3198031 100644 --- a/src/Services/WebhookCheckService.php +++ b/src/Services/WebhookCheckService.php @@ -181,7 +181,11 @@ class WebhookCheckService $prev = DeviceState::where('device_mac', $mac)->first(); if (! $prev) continue; - // Skip planned reboots — these are intentional, not alerts + // Skip planned reboots — these are intentional, not alerts. + // Two layers: a global suppression window set by RebootAllAps + // (Setting, survives any cache driver), plus the per-MAC + // cache keys for finer granularity. + if ($this->inGlobalRebootSuppression()) continue; if (Cache::has('unifi:planned_reboot:' . strtolower($mac))) continue; if ($comingOnline) { @@ -509,6 +513,8 @@ class WebhookCheckService private function checkReboot($aps, array $filter): array { $alerts = []; + if ($this->inGlobalRebootSuppression()) return $alerts; + foreach ($aps as $ap) { if (! empty($filter) && ! in_array($ap['mac'], $filter)) continue; if (Cache::has('unifi:planned_reboot:' . strtolower($ap['mac']))) continue; @@ -584,6 +590,24 @@ class WebhookCheckService return self::buildPlatformPayload($url, $message, $fullPayload); } + /** + * Is a fleet reboot in progress right now? RebootAllAps stamps a + * suppression-until timestamp as a Setting; while that timestamp + * is in the future, we skip all device-offline / device-online / + * unexpected-reboot alerts to avoid flooding webhooks during the + * known maintenance window. + */ + private function inGlobalRebootSuppression(): bool + { + $until = \App\Models\Setting::get('unifi.reboot_suppression_until'); + if (! $until) return false; + try { + return \Carbon\Carbon::parse($until)->isFuture(); + } catch (\Throwable) { + return false; + } + } + /** * Public/static helper so the test-webhook endpoint produces the * same per-platform payload shape that real events do.