feat: add alerting system with configurable rules and UI (MON-02, MON-03)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -436,6 +436,10 @@ impl RpcHandler {
|
||||
"monitoring.current" => self.handle_monitoring_current().await,
|
||||
"monitoring.history" => self.handle_monitoring_history(params).await,
|
||||
"monitoring.containers" => self.handle_monitoring_containers().await,
|
||||
"monitoring.alerts" => self.handle_monitoring_alerts(params).await,
|
||||
"monitoring.alert-rules" => self.handle_monitoring_alert_rules().await,
|
||||
"monitoring.configure-alert" => self.handle_monitoring_configure_alert(params).await,
|
||||
"monitoring.acknowledge-alert" => self.handle_monitoring_acknowledge_alert(params).await,
|
||||
|
||||
// System updates
|
||||
"update.check" => self.handle_update_check().await,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::RpcHandler;
|
||||
use crate::monitoring::AlertRuleKind;
|
||||
use anyhow::Result;
|
||||
use tracing::debug;
|
||||
|
||||
@@ -59,4 +60,76 @@ impl RpcHandler {
|
||||
None => Ok(serde_json::json!({ "containers": [] })),
|
||||
}
|
||||
}
|
||||
|
||||
/// monitoring.alerts — get fired alert history
|
||||
pub(super) async fn handle_monitoring_alerts(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<serde_json::Value> {
|
||||
debug!("Getting alert history");
|
||||
|
||||
let count = params
|
||||
.as_ref()
|
||||
.and_then(|p| p.get("count"))
|
||||
.and_then(|v| v.as_u64())
|
||||
.unwrap_or(50) as usize;
|
||||
|
||||
let alerts = self.metrics_store.get_fired_alerts(count).await;
|
||||
|
||||
Ok(serde_json::json!({
|
||||
"count": alerts.len(),
|
||||
"alerts": alerts,
|
||||
}))
|
||||
}
|
||||
|
||||
/// monitoring.alert-rules — get current alert rules
|
||||
pub(super) async fn handle_monitoring_alert_rules(&self) -> Result<serde_json::Value> {
|
||||
debug!("Getting alert rules");
|
||||
|
||||
let rules = self.metrics_store.get_alert_rules().await;
|
||||
Ok(serde_json::json!({ "rules": rules }))
|
||||
}
|
||||
|
||||
/// monitoring.configure-alert — update an alert rule
|
||||
pub(super) async fn handle_monitoring_configure_alert(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<serde_json::Value> {
|
||||
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
|
||||
|
||||
let kind_str = params
|
||||
.get("kind")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing 'kind' parameter"))?;
|
||||
|
||||
let kind: AlertRuleKind = serde_json::from_value(serde_json::json!(kind_str))
|
||||
.map_err(|_| anyhow::anyhow!("Invalid alert kind: {}", kind_str))?;
|
||||
|
||||
let enabled = params.get("enabled").and_then(|v| v.as_bool());
|
||||
let threshold = params.get("threshold").and_then(|v| v.as_f64());
|
||||
|
||||
self.metrics_store
|
||||
.update_alert_rule(&kind, enabled, threshold)
|
||||
.await;
|
||||
|
||||
debug!("Updated alert rule: {:?}", kind);
|
||||
Ok(serde_json::json!({ "updated": true, "kind": kind_str }))
|
||||
}
|
||||
|
||||
/// monitoring.acknowledge-alert — acknowledge a fired alert
|
||||
pub(super) async fn handle_monitoring_acknowledge_alert(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<serde_json::Value> {
|
||||
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
|
||||
|
||||
let alert_id = params
|
||||
.get("id")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing 'id' parameter"))?;
|
||||
|
||||
let found = self.metrics_store.acknowledge_alert(alert_id).await;
|
||||
|
||||
Ok(serde_json::json!({ "acknowledged": found, "id": alert_id }))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user