diff --git a/agent/src/collectors/service.rs b/agent/src/collectors/service.rs index 1e04e74..ddb2bdd 100644 --- a/agent/src/collectors/service.rs +++ b/agent/src/collectors/service.rs @@ -435,16 +435,17 @@ impl ServiceCollector { "sshd" | "ssh" => self.get_ssh_active_users().await.map(|s| vec![s]), "nginx" => self.get_nginx_description().await.map(|s| vec![s]), "apache2" | "httpd" => self.get_web_server_connections().await.map(|s| vec![s]), - "docker" | "docker-registry" => self.get_docker_containers().await.map(|s| vec![s]), + "docker-registry" => self.get_docker_registry_info().await.map(|s| vec![s]), + "docker" => self.get_docker_containers().await.map(|s| vec![s]), "postgresql" | "postgres" => self.get_postgres_connections().await.map(|s| vec![s]), "mysql" | "mariadb" => self.get_mysql_connections().await.map(|s| vec![s]), "redis" | "redis-immich" => self.get_redis_info().await.map(|s| vec![s]), - "mongodb" | "mongod" => self.get_mongodb_info().await.map(|s| vec![s]), "gitea" => self.get_gitea_info().await.map(|s| vec![s]), - "immich-server" | "immich" => self.get_immich_info().await.map(|s| vec![s]), + "immich-server" => self.get_immich_info().await.map(|s| vec![s]), "vaultwarden" => self.get_vaultwarden_info().await.map(|s| vec![s]), "unifi" => self.get_unifi_info().await.map(|s| vec![s]), "mosquitto" => self.get_mosquitto_info().await.map(|s| vec![s]), + "haasp-webgrid" => self.get_haasp_webgrid_info().await.map(|s| vec![s]), _ => None, }; @@ -506,9 +507,9 @@ impl ServiceCollector { } } - async fn get_docker_containers(&self) -> Option { - let output = Command::new("docker") - .args(["ps", "--format", "table {{.Names}}"]) + async fn get_docker_containers(&self) -> Option> { + let output = Command::new("/run/current-system/sw/bin/docker") + .args(["ps", "--format", "{{.Names}}"]) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .output() @@ -520,12 +521,16 @@ impl ServiceCollector { } let stdout = String::from_utf8_lossy(&output.stdout); - let container_count = stdout.lines().count().saturating_sub(1); // Subtract header line + let containers: Vec = stdout + .lines() + .filter(|line| !line.trim().is_empty()) + .map(|line| line.trim().to_string()) + .collect(); - if container_count > 0 { - Some(format!("{} running containers", container_count)) + if containers.is_empty() { + None } else { - Some("no containers running".to_string()) + Some(containers) } } @@ -573,7 +578,24 @@ impl ServiceCollector { } } - // Fallback: check MySQL connections on port 3306 + // Fallback: check MySQL unix socket connections (more common than TCP) + let output = Command::new("/run/current-system/sw/bin/ss") + .args(["-x", "state", "connected", "src", "*mysql*"]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .output() + .await + .ok()?; + + if output.status.success() { + let stdout = String::from_utf8_lossy(&output.stdout); + let connection_count = stdout.lines().count().saturating_sub(1); + if connection_count > 0 { + return Some(format!("{} connections", connection_count)); + } + } + + // Also try TCP port 3306 as final fallback let output = Command::new("/run/current-system/sw/bin/ss") .args(["-tn", "state", "established", "dport", "= :3306"]) .stdout(Stdio::piped()) @@ -911,9 +933,9 @@ impl ServiceCollector { } async fn get_immich_info(&self) -> Option { - // Check HTTP connections - Immich typically runs on port 2283 or behind nginx + // Check HTTP connections - Immich runs on port 8084 (from nginx proxy config) let output = Command::new("/run/current-system/sw/bin/ss") - .args(["-tn", "state", "established", "dport", "= :2283"]) + .args(["-tn", "state", "established", "dport", "= :8084"]) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .output() @@ -932,34 +954,9 @@ impl ServiceCollector { } async fn get_vaultwarden_info(&self) -> Option { - // Try common vaultwarden ports: 8080, 8084, 8000 - let ports = ["8080", "8084", "8000"]; - - for port in &ports { - let output = Command::new("/run/current-system/sw/bin/ss") - .args(["-tn", "state", "established", "dport", &format!("= :{}", port)]) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .output() - .await - .ok()?; - - if output.status.success() { - let stdout = String::from_utf8_lossy(&output.stdout); - let connection_count = stdout.lines().count().saturating_sub(1); - if connection_count > 0 { - return Some(format!("{} connections", connection_count)); - } - } - } - - None - } - - async fn get_unifi_info(&self) -> Option { - // Check for device count via UniFi API (if accessible) - let output = Command::new("curl") - .args(["-s", "-f", "--insecure", "https://localhost:8443/api/self/sites"]) + // Check vaultwarden connections on port 8222 (from nginx proxy config) + let output = Command::new("/run/current-system/sw/bin/ss") + .args(["-tn", "state", "established", "dport", "= :8222"]) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .output() @@ -967,12 +964,32 @@ impl ServiceCollector { .ok()?; if output.status.success() { - return Some("Controller active".to_string()); + let stdout = String::from_utf8_lossy(&output.stdout); + let connection_count = stdout.lines().count().saturating_sub(1); + if connection_count > 0 { + return Some(format!("{} connections", connection_count)); + } } - // Fallback: check data directory - if tokio::fs::metadata("/var/lib/unifi/data").await.is_ok() { - return Some("Data directory exists".to_string()); + None + } + + async fn get_unifi_info(&self) -> Option { + // Check UniFi connections on port 8080 (TCP) + let output = Command::new("/run/current-system/sw/bin/ss") + .args(["-tn", "state", "established", "dport", "= :8080"]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .output() + .await + .ok()?; + + if output.status.success() { + let stdout = String::from_utf8_lossy(&output.stdout); + let connection_count = stdout.lines().count().saturating_sub(1); + if connection_count > 0 { + return Some(format!("{} connections", connection_count)); + } } None @@ -999,10 +1016,10 @@ impl ServiceCollector { None } - async fn get_mongodb_info(&self) -> Option { - // Check MongoDB connections on port 27017 + async fn get_docker_registry_info(&self) -> Option { + // Check Docker registry connections on port 5000 (from nginx proxy config) let output = Command::new("/run/current-system/sw/bin/ss") - .args(["-tn", "state", "established", "dport", "= :27017"]) + .args(["-tn", "state", "established", "dport", "= :5000"]) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .output() @@ -1017,9 +1034,13 @@ impl ServiceCollector { } } - // Fallback: check data directory size - let output = Command::new("sudo") - .args(["/run/current-system/sw/bin/du", "-sh", "/var/lib/mongodb"]) + None + } + + async fn get_haasp_webgrid_info(&self) -> Option { + // Check HAASP webgrid connections on port 8081 + let output = Command::new("/run/current-system/sw/bin/ss") + .args(["-tn", "state", "established", "dport", "= :8081"]) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .output() @@ -1028,8 +1049,9 @@ impl ServiceCollector { if output.status.success() { let stdout = String::from_utf8_lossy(&output.stdout); - if let Some(size) = stdout.split_whitespace().next() { - return Some(format!("Data: {}", size)); + let connection_count = stdout.lines().count().saturating_sub(1); + if connection_count > 0 { + return Some(format!("{} connections", connection_count)); } } @@ -1101,6 +1123,31 @@ impl Collector for ServiceCollector { healthy += 1; } } + } + // Handle docker specially - create sub-services for containers + else if service == "docker" && matches!(service_data.status, ServiceStatus::Running) { + // Clear docker description - containers will become individual sub-services + let mut docker_service = service_data; + docker_service.description = None; + services.push(docker_service); + + // Add docker containers as individual sub-services + if let Some(containers) = self.get_docker_containers().await { + for container in containers.iter() { + services.push(ServiceData { + name: container.clone(), + status: ServiceStatus::Running, // Assume containers are running if docker is running + memory_used_mb: 0.0, + memory_quota_mb: 0.0, + cpu_percent: 0.0, + sandbox_limit: None, + disk_used_gb: 0.0, + description: None, + sub_service: Some("docker".to_string()), + }); + healthy += 1; + } + } } else { services.push(service_data); } diff --git a/agent/src/discovery.rs b/agent/src/discovery.rs index 163f876..4afc915 100644 --- a/agent/src/discovery.rs +++ b/agent/src/discovery.rs @@ -238,6 +238,9 @@ impl AutoDiscovery { "rclone", // Container runtimes "docker", + // CI/CD services + "gitea-actions", + "actions-runner", // Network services "sshd", "dnsmasq",