patches on sxsw ai working api key working container hardened plus many more

This commit is contained in:
Dorian
2026-03-12 22:19:04 +00:00
parent 73e0a1b74d
commit 5e6aaa74aa
14 changed files with 625 additions and 46 deletions
+68 -1
View File
@@ -2,11 +2,14 @@ pub mod collector;
use serde::{Deserialize, Serialize};
use std::collections::VecDeque;
use std::path::PathBuf;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
use tokio::sync::RwLock;
use tracing::{debug, info, warn};
const ALERT_RULES_FILE: &str = "alert-rules.json";
/// Maximum entries at 1-minute resolution (24 hours = 1440 minutes)
const MAX_1MIN_ENTRIES: usize = 1440;
@@ -132,6 +135,7 @@ pub struct MetricsStore {
ws_connections: AtomicU32,
alert_rules: RwLock<Vec<AlertRule>>,
fired_alerts: RwLock<VecDeque<FiredAlert>>,
data_dir: Option<PathBuf>,
}
impl MetricsStore {
@@ -144,6 +148,22 @@ impl MetricsStore {
ws_connections: AtomicU32::new(0),
alert_rules: RwLock::new(AlertRule::default_rules()),
fired_alerts: RwLock::new(VecDeque::with_capacity(MAX_ALERT_HISTORY)),
data_dir: None,
}
}
/// Create a MetricsStore that persists alert rules to disk.
pub fn with_data_dir(data_dir: PathBuf) -> Self {
let rules = load_alert_rules_sync(&data_dir);
Self {
minute_data: RwLock::new(VecDeque::with_capacity(MAX_1MIN_ENTRIES)),
quarter_hour_data: RwLock::new(VecDeque::with_capacity(MAX_15MIN_ENTRIES)),
minute_count: RwLock::new(0),
rpc_latency: RwLock::new((0.0, 0)),
ws_connections: AtomicU32::new(0),
alert_rules: RwLock::new(rules),
fired_alerts: RwLock::new(VecDeque::with_capacity(MAX_ALERT_HISTORY)),
data_dir: Some(data_dir),
}
}
@@ -229,7 +249,7 @@ impl MetricsStore {
self.alert_rules.read().await.clone()
}
/// Update an alert rule by kind.
/// Update an alert rule by kind and persist to disk.
pub async fn update_alert_rule(&self, kind: &AlertRuleKind, enabled: Option<bool>, threshold: Option<f64>) {
let mut rules = self.alert_rules.write().await;
if let Some(rule) = rules.iter_mut().find(|r| &r.kind == kind) {
@@ -240,6 +260,12 @@ impl MetricsStore {
rule.threshold = t;
}
}
// Persist to disk so changes survive restarts
if let Some(ref dir) = self.data_dir {
if let Err(e) = save_alert_rules(dir, &rules).await {
warn!("Failed to persist alert rules: {}", e);
}
}
}
/// Get fired alert history.
@@ -409,6 +435,47 @@ impl MetricsStore {
}
}
/// Load alert rules from disk, falling back to defaults if file missing or corrupt.
fn load_alert_rules_sync(data_dir: &std::path::Path) -> Vec<AlertRule> {
let path = data_dir.join(ALERT_RULES_FILE);
match std::fs::read_to_string(&path) {
Ok(content) => match serde_json::from_str::<Vec<AlertRule>>(&content) {
Ok(saved) => {
// Merge with defaults: use saved enabled/threshold, add any new rule kinds
let defaults = AlertRule::default_rules();
let mut merged = Vec::new();
for default in &defaults {
if let Some(saved_rule) = saved.iter().find(|r| r.kind == default.kind) {
merged.push(AlertRule {
kind: default.kind.clone(),
threshold: saved_rule.threshold,
enabled: saved_rule.enabled,
description: default.description.clone(),
});
} else {
merged.push(default.clone());
}
}
info!("Loaded alert rules from {}", path.display());
merged
}
Err(e) => {
warn!("Failed to parse alert rules ({}), using defaults", e);
AlertRule::default_rules()
}
},
Err(_) => AlertRule::default_rules(),
}
}
/// Save alert rules to disk.
async fn save_alert_rules(data_dir: &std::path::Path, rules: &[AlertRule]) -> anyhow::Result<()> {
tokio::fs::create_dir_all(data_dir).await?;
let content = serde_json::to_string_pretty(rules)?;
tokio::fs::write(data_dir.join(ALERT_RULES_FILE), content).await?;
Ok(())
}
/// Spawn the background metrics collector (runs every 60 seconds).
/// Also evaluates alert rules on each snapshot and pushes notifications.
pub fn spawn_metrics_collector(