use anyhow::Result;
use tokio::sync::mpsc;
use crate::event::{AppEvent, DetectedService};
use crate::ssh::probe::{generate_quick_scan_script, ProbeOutput};
use crate::ssh::services::ServiceRegistry;
use crate::ssh::session::SshSession;
pub async fn quick_scan(
session: &SshSession,
host_id: String,
tx: mpsc::Sender<AppEvent>,
) -> Result<()> {
tracing::debug!(host = %host_id, "starting quick scan");
let probe_script = generate_quick_scan_script();
let output = session.run_command(probe_script).await?;
let probe_output = ProbeOutput::parse(&output)?;
let registry = ServiceRegistry::new();
let detected_kinds = registry.detect_services(&probe_output);
tracing::debug!(
host = %host_id,
services = ?detected_kinds,
"quick scan detected {} services",
detected_kinds.len()
);
let mut services = Vec::new();
for kind in detected_kinds {
let quick_metrics = if let Some(provider) = registry.get_provider(&kind) {
provider.quick_metrics(&probe_output)
} else {
Vec::new()
};
services.push(DetectedService {
kind: kind.clone(),
version: None,
status: crate::event::ServiceStatus::Unknown,
metrics: quick_metrics,
alerts: Vec::new(),
suggested_snippets: Vec::new(),
});
}
if let Some(os_info) = probe_output.parse_os_info() {
use crate::event::Metrics;
use std::time::Instant;
let metrics = Metrics {
cpu_percent: None,
ram_percent: None,
disk_percent: None,
uptime: None,
load_avg: None,
os_info: Some(os_info),
last_updated: Instant::now(),
};
tx.send(AppEvent::MetricsUpdate(host_id.clone(), metrics))
.await
.ok(); }
tx.send(AppEvent::DiscoveryQuickScanDone(host_id, services))
.await
.map_err(|_| anyhow::anyhow!("event channel closed"))?;
Ok(())
}
pub async fn deep_probe(
session: &SshSession,
host_id: String,
probe_output: &ProbeOutput,
tx: mpsc::Sender<AppEvent>,
) -> Result<()> {
tracing::debug!(host = %host_id, "starting deep probe");
let registry = ServiceRegistry::new();
let services = registry.collect_all_metrics(session, probe_output).await;
tracing::debug!(
host = %host_id,
services = services.len(),
"deep probe collected metrics for {} services",
services.len()
);
for service in &services {
for alert in &service.alerts {
tx.send(AppEvent::AlertNew(host_id.clone(), alert.clone()))
.await
.ok(); }
}
tx.send(AppEvent::DiscoveryDeepProbeDone(host_id, services))
.await
.map_err(|_| anyhow::anyhow!("event channel closed"))?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_quick_scan_script_generation() {
let script = generate_quick_scan_script();
assert!(script.contains("===OMNYSSH:OS==="));
assert!(script.contains("===OMNYSSH:DOCKER==="));
assert!(script.contains("===OMNYSSH:SERVICES==="));
}
}