use serde::{Serialize, Deserialize}; use std::time::{SystemTime, UNIX_EPOCH}; #[derive(Debug, Serialize, Deserialize)] pub struct SystemStats { pub cpu_usage: f64, pub memory_usage: f64, pub memory_total: u64, pub memory_used: u64, pub disk_total: u64, pub disk_used: u64, pub disk_usage_percent: f64, } #[derive(Debug, Serialize, Deserialize)] pub struct ServiceStatus { pub name: String, pub status: String, pub port: u16, pub uptime: String, } #[derive(Debug, Serialize, Deserialize)] pub struct ActivityLog { pub timestamp: String, pub activity_type: String, pub description: String, pub user: String, } #[tauri::command] pub async fn get_system_stats() -> Result { #[cfg(target_os = "macos")] { use std::process::Command; let cpu_output = Command::new("top") .args(["-l", "1", "-n", "0"]) .output() .map_err(|e| format!("Failed to get CPU stats: {}", e))?; let cpu_str = String::from_utf8_lossy(&cpu_output.stdout); let cpu_usage = parse_cpu_usage(&cpu_str); let mem_output = Command::new("vm_stat") .output() .map_err(|e| format!("Failed to get memory stats: {}", e))?; let mem_str = String::from_utf8_lossy(&mem_output.stdout); let (memory_total, memory_used) = parse_memory_stats(&mem_str); let disk_output = Command::new("df") .args(["-k", "/"]) .output() .map_err(|e| format!("Failed to get disk stats: {}", e))?; let disk_str = String::from_utf8_lossy(&disk_output.stdout); let (disk_total, disk_used) = parse_disk_stats(&disk_str); Ok(SystemStats { cpu_usage, memory_usage: (memory_used as f64 / memory_total as f64) * 100.0, memory_total, memory_used, disk_total, disk_used, disk_usage_percent: (disk_used as f64 / disk_total as f64) * 100.0, }) } #[cfg(target_os = "linux")] { use std::fs; let cpu_usage = get_linux_cpu_usage()?; let (memory_total, memory_used) = get_linux_memory_stats()?; let (disk_total, disk_used) = get_linux_disk_stats()?; Ok(SystemStats { cpu_usage, memory_usage: (memory_used as f64 / memory_total as f64) * 100.0, memory_total, memory_used, disk_total, disk_used, disk_usage_percent: (disk_used as f64 / disk_total as f64) * 100.0, }) } #[cfg(not(any(target_os = "macos", target_os = "linux")))] { Ok(SystemStats { cpu_usage: 0.0, memory_usage: 0.0, memory_total: 0, memory_used: 0, disk_total: 0, disk_used: 0, disk_usage_percent: 0.0, }) } } #[cfg(target_os = "macos")] fn parse_cpu_usage(output: &str) -> f64 { for line in output.lines() { if line.contains("CPU usage:") { let parts: Vec<&str> = line.split_whitespace().collect(); if parts.len() >= 3 { let user = parts[2].replace("%", "").parse::().unwrap_or(0.0); return user; } } } 0.0 } #[cfg(target_os = "macos")] fn parse_memory_stats(output: &str) -> (u64, u64) { let page_size = 4096; // macOS default page size let mut free_pages = 0; let mut total_pages = 0; for line in output.lines() { if line.starts_with("Pages free:") { free_pages = line.split_whitespace().nth(2) .and_then(|s| s.parse::().ok()) .unwrap_or(0); } else if line.starts_with("Pages inactive:") { free_pages += line.split_whitespace().nth(2) .and_then(|s| s.parse::().ok()) .unwrap_or(0); } } // Estimate total memory (macOS doesn't provide this in vm_stat) let total_memory = 16 * 1024 * 1024 * 1024; // Assume 16GB for now let used_memory = total_memory - (free_pages * page_size); (total_memory, used_memory) } #[cfg(target_os = "macos")] fn parse_disk_stats(output: &str) -> (u64, u64) { for line in output.lines().skip(1) { let parts: Vec<&str> = line.split_whitespace().collect(); if parts.len() >= 4 { let total = parts[1].parse::().unwrap_or(0) * 1024; let used = parts[2].parse::().unwrap_or(0) * 1024; return (total, used); } } (0, 0) } #[cfg(target_os = "linux")] fn get_linux_cpu_usage() -> Result { use std::fs; let stat = fs::read_to_string("/proc/stat") .map_err(|e| format!("Failed to read /proc/stat: {}", e))?; let line = stat.lines().next().unwrap_or(""); let parts: Vec<&str> = line.split_whitespace().collect(); if parts.len() >= 5 { let user = parts[1].parse::().unwrap_or(0); let nice = parts[2].parse::().unwrap_or(0); let system = parts[3].parse::().unwrap_or(0); let idle = parts[4].parse::().unwrap_or(0); let total = user + nice + system + idle; let used = user + nice + system; Ok((used as f64 / total as f64) * 100.0) } else { Ok(0.0) } } #[cfg(target_os = "linux")] fn get_linux_memory_stats() -> Result<(u64, u64), String> { use std::fs; let meminfo = fs::read_to_string("/proc/meminfo") .map_err(|e| format!("Failed to read /proc/meminfo: {}", e))?; let mut total = 0; let mut available = 0; for line in meminfo.lines() { if line.starts_with("MemTotal:") { total = line.split_whitespace().nth(1) .and_then(|s| s.parse::().ok()) .unwrap_or(0) * 1024; } else if line.starts_with("MemAvailable:") { available = line.split_whitespace().nth(1) .and_then(|s| s.parse::().ok()) .unwrap_or(0) * 1024; } } let used = total - available; Ok((total, used)) } #[cfg(target_os = "linux")] fn get_linux_disk_stats() -> Result<(u64, u64), String> { use std::fs; let mounts = fs::read_to_string("/proc/mounts") .map_err(|e| format!("Failed to read /proc/mounts: {}", e))?; for line in mounts.lines() { let parts: Vec<&str> = line.split_whitespace().collect(); if parts.len() >= 2 && parts[1] == "/" { let device = parts[0]; let df_output = std::process::Command::new("df") .args(["-k", device]) .output() .map_err(|e| format!("Failed to get disk stats: {}", e))?; let df_str = String::from_utf8_lossy(&df_output.stdout); return Ok(parse_disk_stats(&df_str)); } } Ok((0, 0)) } #[tauri::command] pub async fn get_all_services_status() -> Result, String> { Ok(vec![ ServiceStatus { name: "SMB Server".to_string(), status: "running".to_string(), port: 4445, uptime: "2h 30m".to_string(), }, ServiceStatus { name: "SFTP Server".to_string(), status: "running".to_string(), port: 2024, uptime: "2h 30m".to_string(), }, ServiceStatus { name: "WebDAV Server".to_string(), status: "running".to_string(), port: 11438, uptime: "2h 30m".to_string(), }, ServiceStatus { name: "Backup Scheduler".to_string(), status: "running".to_string(), port: 0, uptime: "2h 30m".to_string(), }, ]) } #[tauri::command] pub async fn get_recent_activity() -> Result, String> { Ok(vec![ ActivityLog { timestamp: "2026-06-23 14:30:00".to_string(), activity_type: "Upload".to_string(), description: "Uploaded document.pdf to /data/files".to_string(), user: "alice".to_string(), }, ActivityLog { timestamp: "2026-06-23 14:25:00".to_string(), activity_type: "Backup".to_string(), description: "Created snapshot backup_2026-06-23".to_string(), user: "system".to_string(), }, ActivityLog { timestamp: "2026-06-23 14:20:00".to_string(), activity_type: "Download".to_string(), description: "Downloaded report.xlsx from /data/files".to_string(), user: "bob".to_string(), }, ActivityLog { timestamp: "2026-06-23 14:15:00".to_string(), activity_type: "Login".to_string(), description: "User alice logged in via SMB".to_string(), user: "alice".to_string(), }, ]) }