diff --git a/markbase-tauri/src-tauri/src/commands/mod.rs b/markbase-tauri/src-tauri/src/commands/mod.rs index 72d5c68..32784d8 100644 --- a/markbase-tauri/src-tauri/src/commands/mod.rs +++ b/markbase-tauri/src-tauri/src/commands/mod.rs @@ -8,6 +8,7 @@ pub mod monitor; pub mod backup; pub mod user_management; pub mod share_management; +pub mod system_stats; pub use file_ops::*; pub use install::*; @@ -18,4 +19,5 @@ pub use health::*; pub use monitor::*; pub use backup::*; pub use user_management::*; -pub use share_management::*; \ No newline at end of file +pub use share_management::*; +pub use system_stats::*; \ No newline at end of file diff --git a/markbase-tauri/src-tauri/src/commands/system_stats.rs b/markbase-tauri/src-tauri/src/commands/system_stats.rs new file mode 100644 index 0000000..97df38d --- /dev/null +++ b/markbase-tauri/src-tauri/src/commands/system_stats.rs @@ -0,0 +1,290 @@ +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(), + }, + ]) +} \ No newline at end of file diff --git a/markbase-tauri/src-tauri/src/main.rs b/markbase-tauri/src-tauri/src/main.rs index 3d4d63e..467bedd 100644 --- a/markbase-tauri/src-tauri/src/main.rs +++ b/markbase-tauri/src-tauri/src/main.rs @@ -52,6 +52,9 @@ fn main() { update_share, delete_share, test_share_connection, + get_system_stats, + get_all_services_status, + get_recent_activity, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/markbase-tauri/src/src/router/index.js b/markbase-tauri/src/src/router/index.js index 66d2755..5bb85f1 100644 --- a/markbase-tauri/src/src/router/index.js +++ b/markbase-tauri/src/src/router/index.js @@ -1,5 +1,6 @@ import { createRouter, createWebHistory } from 'vue-router' import Home from '../views/Home.vue' +import Dashboard from '../views/Dashboard.vue' import Install from '../views/Install.vue' import Config from '../views/Config.vue' import Diagnostic from '../views/Diagnostic.vue' @@ -16,6 +17,11 @@ const routes = [ name: 'Home', component: Home }, + { + path: '/dashboard', + name: 'Dashboard', + component: Dashboard + }, { path: '/install', name: 'Install', diff --git a/markbase-tauri/src/src/views/Dashboard.vue b/markbase-tauri/src/src/views/Dashboard.vue new file mode 100644 index 0000000..2c0fc7a --- /dev/null +++ b/markbase-tauri/src/src/views/Dashboard.vue @@ -0,0 +1,302 @@ + + + + + \ No newline at end of file diff --git a/markbase-tauri/src/src/views/Home.vue b/markbase-tauri/src/src/views/Home.vue index 276915d..f0c06ac 100644 --- a/markbase-tauri/src/src/views/Home.vue +++ b/markbase-tauri/src/src/views/Home.vue @@ -4,7 +4,7 @@ import { useRouter } from 'vue-router' import { useAppStore } from '../stores/app' import { invoke } from '@tauri-apps/api/tauri' import { ElMessage } from 'element-plus' -import { Folder, Document, Upload, Clock, UserFilled, FolderOpened } from '@element-plus/icons-vue' +import { Folder, Document, Upload, Clock, UserFilled, FolderOpened, Monitor } from '@element-plus/icons-vue' import { open } from '@tauri-apps/api/dialog' const router = useRouter() @@ -170,6 +170,14 @@ onMounted(async () => {
+ +
+ +

Dashboard

+

System stats overview

+
+
+