From e05165f1362e54d7d9c6fac8a951fbc4ae8a8db0 Mon Sep 17 00:00:00 2001 From: grabbit Date: Thu, 19 Feb 2026 15:44:17 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20backend=20=E2=80=94=20web=20auth,=20not?= =?UTF-8?q?ifications,=20scheduled=20warmup,=20nas=5Foffline/all=5Fsynced,?= =?UTF-8?q?=20reconnect=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- src/config.rs | 5 +++++ src/daemon.rs | 3 +++ src/supervisor.rs | 17 +++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/src/config.rs b/src/config.rs index 7121415..2355b43 100644 --- a/src/config.rs +++ b/src/config.rs @@ -701,6 +701,11 @@ impl Config { } } + // Validate notification thresholds + if self.notifications.cache_threshold_pct > 100 { + anyhow::bail!("notifications.cache_threshold_pct must be 0–100, got {}", self.notifications.cache_threshold_pct); + } + // Validate SMB auth if self.smb_auth.enabled { if self.smb_auth.username.is_none() { diff --git a/src/daemon.rs b/src/daemon.rs index 88cbf49..fa5c36b 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -70,6 +70,8 @@ pub struct DaemonStatus { pub nas_offline_notified: bool, /// Cache warning level already notified (0=none, 3=writeback depth). pub cache_notified_level: u8, + /// Whether we've already sent the cache-threshold notification (reset when usage drops). + pub cache_threshold_notified: bool, } impl DaemonStatus { @@ -108,6 +110,7 @@ impl DaemonStatus { nas_offline_since: None, nas_offline_notified: false, cache_notified_level: 0, + cache_threshold_notified: false, } } diff --git a/src/supervisor.rs b/src/supervisor.rs index 201ce49..0ebb7ec 100644 --- a/src/supervisor.rs +++ b/src/supervisor.rs @@ -910,6 +910,23 @@ fn supervise( status.nas_offline_notified = false; } + // Cache usage % notification + if notif.cache_threshold_pct > 0 { + let total_cache: u64 = status.shares.iter().map(|s| s.cache_bytes).sum(); + if let Some(max_bytes) = parse_size_bytes(&config.cache.max_size) { + let pct = (total_cache as f64 / max_bytes as f64 * 100.0) as u8; + if pct >= notif.cache_threshold_pct && !status.cache_threshold_notified { + send_webhook_notification(&url, &format!( + "\u{26a0}\u{fe0f} Warpgate: cache usage {}% — consider cleaning", pct + )); + status.cache_threshold_notified = true; + } else if pct < notif.cache_threshold_pct.saturating_sub(5) { + // Hysteresis: reset when usage drops 5% below threshold + status.cache_threshold_notified = false; + } + } + } + // Write-back depth notification if total_dirty >= notif.writeback_depth { if status.cache_notified_level < 3 {