diff --git a/agent/src/collectors/service.rs b/agent/src/collectors/service.rs index 81458a8..4fa1ba0 100644 --- a/agent/src/collectors/service.rs +++ b/agent/src/collectors/service.rs @@ -732,38 +732,25 @@ impl ServiceCollector { fn parse_nginx_config(&self, config: &str) -> Option> { let mut sites = Vec::new(); - let mut server_name_lines = 0; + let lines: Vec<&str> = config.lines().collect(); + let mut i = 0; - for line in config.lines() { - let trimmed = line.trim(); - if trimmed.starts_with("server_name") { - server_name_lines += 1; - tracing::debug!("Found server_name line: {}", trimmed); - - // Extract server names from "server_name example.com www.example.com;" - if let Some(names_part) = trimmed.strip_prefix("server_name") { - let names_clean = names_part.trim().trim_end_matches(';'); - tracing::debug!("Extracted names part: '{}'", names_clean); - - for name in names_clean.split_whitespace() { - // Skip default catch-all server names - if name != "_" && !name.is_empty() && name.contains('.') && !name.starts_with('$') { - tracing::debug!("Adding site: {}", name); - sites.push(name.to_string()); - } else { - tracing::debug!("Skipping invalid site: {}", name); - } - } + while i < lines.len() { + let trimmed = lines[i].trim(); + + // Look for server blocks + if trimmed == "server {" { + if let Some(site_info) = self.parse_server_block(&lines, &mut i) { + sites.push(site_info); } } + i += 1; } - tracing::info!("Found {} server_name lines, extracted {} sites", server_name_lines, sites.len()); + tracing::info!("Extracted {} sites with routing info", sites.len()); - // Remove duplicates and limit to reasonable number - sites.sort(); - sites.dedup(); - sites.truncate(15); // Show max 15 sites to avoid overwhelming the UI + // Limit to reasonable number + sites.truncate(15); tracing::info!("Final nginx sites list: {:?}", sites); @@ -774,6 +761,80 @@ impl ServiceCollector { Some(sites) } } + + fn parse_server_block(&self, lines: &[&str], start_index: &mut usize) -> Option { + let mut server_names = Vec::new(); + let mut destinations = Vec::new(); + let mut i = *start_index + 1; + let mut brace_count = 1; + + // Parse until we close the server block + while i < lines.len() && brace_count > 0 { + let trimmed = lines[i].trim(); + + // Track braces + brace_count += trimmed.matches('{').count(); + brace_count -= trimmed.matches('}').count(); + + // Extract server_name + if trimmed.starts_with("server_name") { + if let Some(names_part) = trimmed.strip_prefix("server_name") { + let names_clean = names_part.trim().trim_end_matches(';'); + for name in names_clean.split_whitespace() { + if name != "_" && !name.is_empty() && name.contains('.') && !name.starts_with('$') { + server_names.push(name.to_string()); + } + } + } + } + + // Extract routing information + if trimmed.starts_with("return") && trimmed.contains("301") { + // Redirect: return 301 https://example.com$request_uri; + if let Some(url_start) = trimmed.find("https://") { + let url_part = &trimmed[url_start..]; + if let Some(url_end) = url_part.find('$') { + let redirect_url = &url_part[..url_end]; + destinations.push(format!("→ {}", redirect_url)); + } + } + } else if trimmed.starts_with("proxy_pass") { + // Proxy: proxy_pass http://localhost:8080; + if let Some(url_start) = trimmed.find("http") { + let url_part = &trimmed[url_start..]; + if let Some(url_end) = url_part.find(';') { + let proxy_url = &url_part[..url_end]; + destinations.push(format!("← {}", proxy_url)); + } + } + } else if trimmed.starts_with("root") { + // Static files: root /var/www/example; + if let Some(path_start) = trimmed.find('/') { + let path_part = &trimmed[path_start..]; + if let Some(path_end) = path_part.find(';') { + let root_path = &path_part[..path_end]; + destinations.push(format!("📁 {}", root_path)); + } + } + } + + i += 1; + } + + *start_index = i - 1; + + // Build site info string + if !server_names.is_empty() { + let primary_name = &server_names[0]; + if destinations.is_empty() { + Some(primary_name.clone()) + } else { + Some(format!("{} {}", primary_name, destinations[0])) + } + } else { + None + } + } } #[async_trait]