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行代码)
268 lines
8.7 KiB
Rust
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
|
|
} |