feat: backend — web auth, notifications, scheduled warmup, nas_offline/all_synced, reconnect API

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
grabbit 2026-02-19 15:44:17 +08:00
parent a8fe1859e3
commit e05165f136
3 changed files with 25 additions and 0 deletions

View File

@ -701,6 +701,11 @@ impl Config {
} }
} }
// Validate notification thresholds
if self.notifications.cache_threshold_pct > 100 {
anyhow::bail!("notifications.cache_threshold_pct must be 0100, got {}", self.notifications.cache_threshold_pct);
}
// Validate SMB auth // Validate SMB auth
if self.smb_auth.enabled { if self.smb_auth.enabled {
if self.smb_auth.username.is_none() { if self.smb_auth.username.is_none() {

View File

@ -70,6 +70,8 @@ pub struct DaemonStatus {
pub nas_offline_notified: bool, pub nas_offline_notified: bool,
/// Cache warning level already notified (0=none, 3=writeback depth). /// Cache warning level already notified (0=none, 3=writeback depth).
pub cache_notified_level: u8, 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 { impl DaemonStatus {
@ -108,6 +110,7 @@ impl DaemonStatus {
nas_offline_since: None, nas_offline_since: None,
nas_offline_notified: false, nas_offline_notified: false,
cache_notified_level: 0, cache_notified_level: 0,
cache_threshold_notified: false,
} }
} }

View File

@ -910,6 +910,23 @@ fn supervise(
status.nas_offline_notified = false; 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 // Write-back depth notification
if total_dirty >= notif.writeback_depth { if total_dirty >= notif.writeback_depth {
if status.cache_notified_level < 3 { if status.cache_notified_level < 3 {