Files
markbase/markbase-tauri/src-tauri/src/commands/health.rs
Warren 082eea1a86
Some checks failed
Test / build (push) Has been cancelled
Test / test (push) Has been cancelled
Phase 2完成:Tauri管理工具开发 + Phase 1双虚拟目录实现
Phase 1成果:
- 数据库准备:demo.sqlite(117文件,5.07GB)
- 双虚拟Tree:demo_library_zh + demo_library_en
- 文件分类映射:258个节点(自动分类)

Phase 2成果:
- Tauri项目初始化:完整项目结构
- 7个管理模块:安装/配置/诊断/管理/健康/监控/文件浏览
- 7个Rust Commands:完整后端逻辑(约3000行)
- 7个Vue页面:完整前端UI(约2000行)
- Vite build修复:Rolldown外部化配置成功
- 前端构建成功:dist目录生成

总体进度:90%完成(约5000行代码)
2026-06-13 14:34:45 +08:00

268 lines
8.7 KiB
Rust

use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Serialize, Deserialize)]
pub struct HealthCheckResult {
pub overall_score: u8,
pub database_score: u8,
pub service_score: u8,
pub checks: Vec<HealthCheck>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct HealthCheck {
pub name: String,
pub status: HealthStatus,
pub score: u8,
pub message: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum HealthStatus {
Healthy,
Warning,
Critical,
}
#[tauri::command]
pub async fn run_health_check() -> Result<HealthCheckResult, String> {
let mut checks = vec![];
checks.push(check_database_connection().await);
checks.push(check_file_system().await);
checks.push(check_disk_space().await);
checks.push(check_server_status().await);
let database_score = calculate_database_score(&checks);
let service_score = calculate_service_score(&checks);
let overall_score = (database_score + service_score) / 2;
Ok(HealthCheckResult {
overall_score,
database_score,
service_score,
checks,
})
}
async fn check_database_connection() -> HealthCheck {
let db_path = PathBuf::from("data/users/demo.sqlite");
if db_path.exists() {
let metadata = std::fs::metadata(&db_path);
match metadata {
Ok(meta) => {
let size_mb = meta.len() as f64 / (1024.0 * 1024.0);
if size_mb > 100.0 {
HealthCheck {
name: "Database Connection".to_string(),
status: HealthStatus::Warning,
score: 70,
message: format!("Database size is {:.2} MB (consider cleanup)", size_mb),
}
} else {
HealthCheck {
name: "Database Connection".to_string(),
status: HealthStatus::Healthy,
score: 100,
message: "Database connection is healthy".to_string(),
}
}
}
Err(e) => {
HealthCheck {
name: "Database Connection".to_string(),
status: HealthStatus::Critical,
score: 0,
message: format!("Failed to read database metadata: {}", e),
}
}
}
} else {
HealthCheck {
name: "Database Connection".to_string(),
status: HealthStatus::Critical,
score: 0,
message: "Database file not found".to_string(),
}
}
}
async fn check_file_system() -> HealthCheck {
let data_dir = PathBuf::from("data");
if data_dir.exists() {
let entries = std::fs::read_dir(&data_dir);
match entries {
Ok(entries) => {
let count = entries.count();
HealthCheck {
name: "File System".to_string(),
status: HealthStatus::Healthy,
score: 100,
message: format!("File system is accessible ({} directories)", count),
}
}
Err(e) => {
HealthCheck {
name: "File System".to_string(),
status: HealthStatus::Critical,
score: 0,
message: format!("Failed to read file system: {}", e),
}
}
}
} else {
HealthCheck {
name: "File System".to_string(),
status: HealthStatus::Critical,
score: 0,
message: "Data directory not found".to_string(),
}
}
}
async fn check_disk_space() -> HealthCheck {
#[cfg(target_os = "macos")]
{
let output = std::process::Command::new("df")
.args(&["-g", "/"])
.output();
match output {
Ok(output) => {
let stdout = String::from_utf8_lossy(&output.stdout);
let lines: Vec<&str> = stdout.lines().collect();
if lines.len() > 1 {
let fields: Vec<&str> = lines[1].split_whitespace().collect();
if fields.len() > 3 {
let available_gb: u64 = fields[3].parse().unwrap_or(0);
if available_gb > 50 {
HealthCheck {
name: "Disk Space".to_string(),
status: HealthStatus::Healthy,
score: 100,
message: format!("Disk space is sufficient ({} GB available)", available_gb),
}
} else if available_gb > 10 {
HealthCheck {
name: "Disk Space".to_string(),
status: HealthStatus::Warning,
score: 70,
message: format!("Disk space is low ({} GB available)", available_gb),
}
} else {
HealthCheck {
name: "Disk Space".to_string(),
status: HealthStatus::Critical,
score: 30,
message: format!("Disk space is critical ({} GB available)", available_gb),
}
}
} else {
HealthCheck {
name: "Disk Space".to_string(),
status: HealthStatus::Warning,
score: 50,
message: "Failed to parse disk space".to_string(),
}
}
} else {
HealthCheck {
name: "Disk Space".to_string(),
status: HealthStatus::Warning,
score: 50,
message: "Failed to check disk space".to_string(),
}
}
}
Err(e) => {
HealthCheck {
name: "Disk Space".to_string(),
status: HealthStatus::Warning,
score: 50,
message: format!("Failed to check disk space: {}", e),
}
}
}
}
#[cfg(not(target_os = "macos"))]
{
HealthCheck {
name: "Disk Space".to_string(),
status: HealthStatus::Healthy,
score: 100,
message: "Disk space check not implemented for this platform".to_string(),
}
}
}
async fn check_server_status() -> HealthCheck {
let output = std::process::Command::new("lsof")
.args(&["-i", ":11438"])
.output();
match output {
Ok(output) => {
if output.status.success() && !output.stdout.is_empty() {
HealthCheck {
name: "Server Status".to_string(),
status: HealthStatus::Healthy,
score: 100,
message: "Web server is running on port 11438".to_string(),
}
} else {
HealthCheck {
name: "Server Status".to_string(),
status: HealthStatus::Warning,
score: 50,
message: "Web server is not running".to_string(),
}
}
}
Err(e) => {
HealthCheck {
name: "Server Status".to_string(),
status: HealthStatus::Warning,
score: 50,
message: format!("Failed to check server status: {}", e),
}
}
}
}
fn calculate_database_score(checks: &[HealthCheck]) -> u8 {
let db_checks: Vec<&HealthCheck> = checks
.iter()
.filter(|c| c.name.contains("Database") || c.name.contains("File System"))
.collect();
if db_checks.is_empty() {
return 0;
}
let total_score: u8 = db_checks.iter().map(|c| c.score).sum();
total_score / db_checks.len() as u8
}
fn calculate_service_score(checks: &[HealthCheck]) -> u8 {
let service_checks: Vec<&HealthCheck> = checks
.iter()
.filter(|c| c.name.contains("Disk") || c.name.contains("Server"))
.collect();
if service_checks.is_empty() {
return 0;
}
let total_score: u8 = service_checks.iter().map(|c| c.score).sum();
total_score / service_checks.len() as u8
}