diff --git a/projects/msp-tools/guru-rmm/agent/src/main.rs b/projects/msp-tools/guru-rmm/agent/src/main.rs index 120d5cd..765cce5 100644 --- a/projects/msp-tools/guru-rmm/agent/src/main.rs +++ b/projects/msp-tools/guru-rmm/agent/src/main.rs @@ -224,8 +224,11 @@ async fn install_service( #[cfg(target_os = "macos")] { let _ = (server_url, api_key, skip_legacy_check); // Suppress unused warnings - info!("Installing GuruRMM Agent as launchd service..."); - todo!("macOS launchd service installation not yet implemented"); + return Err(anyhow::anyhow!( + "macOS launchd service installation is not yet implemented.\n\ + For now, you can run the agent manually or create a launchd plist.\n\ + See: https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html" + )); } } @@ -504,7 +507,10 @@ async fn uninstall_service() -> Result<()> { #[cfg(target_os = "macos")] { - todo!("macOS service uninstallation not yet implemented"); + return Err(anyhow::anyhow!( + "macOS launchd service uninstallation is not yet implemented.\n\ + If you created a launchd plist manually, remove it from ~/Library/LaunchAgents/ or /Library/LaunchDaemons/" + )); } } @@ -593,7 +599,10 @@ async fn start_service() -> Result<()> { #[cfg(target_os = "macos")] { - todo!("macOS service start not yet implemented"); + return Err(anyhow::anyhow!( + "macOS launchd service start is not yet implemented.\n\ + If you created a launchd plist manually, use: launchctl load " + )); } } @@ -626,7 +635,10 @@ async fn stop_service() -> Result<()> { #[cfg(target_os = "macos")] { - todo!("macOS service stop not yet implemented"); + return Err(anyhow::anyhow!( + "macOS launchd service stop is not yet implemented.\n\ + If you created a launchd plist manually, use: launchctl unload " + )); } } diff --git a/projects/msp-tools/guru-rmm/agent/src/service.rs b/projects/msp-tools/guru-rmm/agent/src/service.rs index 37aeb1d..c32b107 100644 --- a/projects/msp-tools/guru-rmm/agent/src/service.rs +++ b/projects/msp-tools/guru-rmm/agent/src/service.rs @@ -90,7 +90,7 @@ pub mod windows { .context("Failed to set StartPending status")?; // Determine config path - let config_path = PathBuf::from(format!(r"{}\\agent.toml", CONFIG_DIR)); + let config_path = PathBuf::from(CONFIG_DIR).join("agent.toml"); // Create the tokio runtime for the agent let runtime = tokio::runtime::Runtime::new().context("Failed to create tokio runtime")?; @@ -331,8 +331,8 @@ pub mod windows { let current_exe = std::env::current_exe().context("Failed to get current executable path")?; - let binary_dest = PathBuf::from(format!(r"{}\\gururmm-agent.exe", INSTALL_DIR)); - let config_dest = PathBuf::from(format!(r"{}\\agent.toml", CONFIG_DIR)); + let binary_dest = PathBuf::from(INSTALL_DIR).join("gururmm-agent.exe"); + let config_dest = PathBuf::from(CONFIG_DIR).join("agent.toml"); // Create directories info!("Creating directories..."); @@ -490,7 +490,7 @@ pub mod windows { pub fn uninstall() -> Result<()> { info!("Uninstalling GuruRMM Agent..."); - let binary_path = PathBuf::from(format!(r"{}\\gururmm-agent.exe", INSTALL_DIR)); + let binary_path = PathBuf::from(INSTALL_DIR).join("gururmm-agent.exe"); // Open the service manager let manager = ServiceManager::local_computer( @@ -685,8 +685,8 @@ pub mod windows { // Get the current executable path let current_exe = std::env::current_exe()?; - let binary_dest = PathBuf::from(format!(r"{}\\gururmm-agent.exe", INSTALL_DIR)); - let config_dest = PathBuf::from(format!(r"{}\\agent.toml", CONFIG_DIR)); + let binary_dest = PathBuf::from(INSTALL_DIR).join("gururmm-agent.exe"); + let config_dest = PathBuf::from(CONFIG_DIR).join("agent.toml"); // Create directories std::fs::create_dir_all(INSTALL_DIR)?; @@ -727,7 +727,7 @@ pub mod windows { pub fn uninstall() -> Result<()> { use std::path::PathBuf; - let binary_path = PathBuf::from(format!(r"{}\\gururmm-agent.exe", INSTALL_DIR)); + let binary_path = PathBuf::from(INSTALL_DIR).join("gururmm-agent.exe"); println!("** To uninstall legacy service, use NSSM:"); println!(" nssm stop {}", SERVICE_NAME); diff --git a/projects/msp-tools/guru-rmm/dashboard/src/pages/Agents.tsx b/projects/msp-tools/guru-rmm/dashboard/src/pages/Agents.tsx index 157cf49..edd05ed 100644 --- a/projects/msp-tools/guru-rmm/dashboard/src/pages/Agents.tsx +++ b/projects/msp-tools/guru-rmm/dashboard/src/pages/Agents.tsx @@ -101,6 +101,10 @@ export function Agents() { queryClient.invalidateQueries({ queryKey: ["agents"] }); setDeleteConfirm(null); }, + onError: (error: Error) => { + console.error("Failed to delete agent:", error); + alert(`Failed to delete agent: ${error.message}`); + }, }); const moveMutation = useMutation({ @@ -111,6 +115,10 @@ export function Agents() { queryClient.invalidateQueries({ queryKey: ["sites"] }); setMovingAgent(null); }, + onError: (error: Error) => { + console.error("Failed to move agent:", error); + alert(`Failed to move agent: ${error.message}`); + }, }); // Get unique clients from agents @@ -124,7 +132,17 @@ export function Agents() { (agent.client_name && agent.client_name.toLowerCase().includes(search.toLowerCase())) || (agent.site_name && agent.site_name.toLowerCase().includes(search.toLowerCase())); - const matchesClient = !filterClient || agent.client_name === filterClient; + // Handle special __unassigned__ filter value + let matchesClient: boolean; + if (filterClient === "__unassigned__") { + // Show only agents without a site/client assignment + matchesClient = !agent.site_id; + } else if (filterClient) { + matchesClient = agent.client_name === filterClient; + } else { + matchesClient = true; + } + const matchesSite = !filterSite || agent.site_id === filterSite; return matchesSearch && matchesClient && matchesSite; @@ -173,7 +191,7 @@ export function Agents() { className="px-3 py-2 rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--background))] text-sm" > - + {clients.map((client) => (