vyuh 0.2.11

Vyuh web framework for Axum and SQLx with handler-first APIs
Documentation
use schemars::JsonSchema;
use serde::Serialize;
use sysinfo::{Pid, System};

use crate::Site;

#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct StatusOut {
    pub site: SiteStatus,
    pub process: ProcessStatus,
    pub system: SystemStatus,
}

#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct SiteStatus {
    pub vyuh_version: &'static str,
    pub package_name: &'static str,
    pub host: String,
    pub port: u16,
    pub project_dir: String,
    pub timezone: String,
    pub database_backend: &'static str,
    pub uptime_seconds: u64,
    pub features: Vec<&'static str>,
    pub operation_count: usize,
    pub command_count: usize,
    pub service_count: usize,
}

#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct ProcessStatus {
    pub pid: u32,
    pub executable_path: Option<String>,
    pub current_dir: Option<String>,
    pub argv: Vec<String>,
    pub memory_bytes: Option<u64>,
    pub virtual_memory_bytes: Option<u64>,
    pub cpu_percent: Option<f32>,
    pub thread_count: Option<usize>,
    pub open_file_count: Option<usize>,
}

#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct SystemStatus {
    pub hostname: Option<String>,
    pub os_name: Option<String>,
    pub os_version: Option<String>,
    pub kernel_version: Option<String>,
    pub architecture: &'static str,
    pub cpu_brand: Option<String>,
    pub cpu_count: usize,
    pub global_cpu_percent: f32,
    pub load_average: LoadAverage,
    pub total_memory_bytes: u64,
    pub used_memory_bytes: u64,
    pub available_memory_bytes: u64,
    pub total_swap_bytes: u64,
    pub used_swap_bytes: u64,
    pub boot_time_seconds: u64,
}

#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct LoadAverage {
    pub one: f64,
    pub five: f64,
    pub fifteen: f64,
}

pub fn collect(site: &Site) -> StatusOut {
    let mut system = System::new_all();
    system.refresh_all();
    let pid = std::process::id();
    let process = system.process(Pid::from_u32(pid));
    let conf = site.conf();
    let load = System::load_average();

    StatusOut {
        site: SiteStatus {
            vyuh_version: env!("CARGO_PKG_VERSION"),
            package_name: env!("CARGO_PKG_NAME"),
            host: conf.host.clone(),
            port: conf.port,
            project_dir: site.project_dir().display().to_string(),
            timezone: site.timezone().to_string(),
            database_backend: database_backend(),
            uptime_seconds: site.uptime().as_secs(),
            features: enabled_features(),
            operation_count: site.iter_operations().count(),
            command_count: site.console_command_infos().len(),
            service_count: site.console_service_infos().len(),
        },
        process: ProcessStatus {
            pid,
            executable_path: process
                .and_then(|process| process.exe())
                .map(|path| path.display().to_string()),
            current_dir: std::env::current_dir()
                .ok()
                .map(|path| path.display().to_string()),
            argv: std::env::args().collect(),
            memory_bytes: process.map(|process| process.memory()),
            virtual_memory_bytes: process.map(|process| process.virtual_memory()),
            cpu_percent: process.map(|process| process.cpu_usage()),
            thread_count: None,
            open_file_count: None,
        },
        system: SystemStatus {
            hostname: System::host_name(),
            os_name: System::name(),
            os_version: System::os_version(),
            kernel_version: System::kernel_version(),
            architecture: std::env::consts::ARCH,
            cpu_brand: system.cpus().first().map(|cpu| cpu.brand().to_string()),
            cpu_count: system.cpus().len(),
            global_cpu_percent: system.global_cpu_usage(),
            load_average: LoadAverage {
                one: load.one,
                five: load.five,
                fifteen: load.fifteen,
            },
            total_memory_bytes: system.total_memory(),
            used_memory_bytes: system.used_memory(),
            available_memory_bytes: system.available_memory(),
            total_swap_bytes: system.total_swap(),
            used_swap_bytes: system.used_swap(),
            boot_time_seconds: System::boot_time(),
        },
    }
}

fn enabled_features() -> Vec<&'static str> {
    let mut features = Vec::new();
    if cfg!(feature = "postgres") {
        features.push("postgres");
    }
    if cfg!(feature = "mysql") {
        features.push("mysql");
    }
    if cfg!(feature = "sqlite") {
        features.push("sqlite");
    }
    if cfg!(feature = "cors") {
        features.push("cors");
    }
    if cfg!(feature = "argon2") {
        features.push("argon2");
    }
    features
}

fn database_backend() -> &'static str {
    if cfg!(feature = "postgres") {
        "postgres"
    } else if cfg!(feature = "mysql") {
        "mysql"
    } else if cfg!(feature = "sqlite") {
        "sqlite"
    } else {
        "unknown"
    }
}