Skip to main content

yggdrasil/api/database/
system_status.rs

1#![allow(clippy::unused_unit, deprecated)]
2
3//! 服务器状态聚合查询(只读):应用内指标 + 主机层指标。
4//!
5//! 应用内:DB 连接池、moka 缓存命中率、活跃会话数、进程运行时间。
6//! 主机层:sysinfo 后台采样快照(CPU/内存/磁盘/load),由 [`crate::sysinfo_sampler`] 维护。
7
8use dioxus::prelude::*;
9use serde::{Deserialize, Serialize};
10
11// admin 鉴权 + AppError 仅在 server 构建里被 server function 体引用。
12#[cfg(feature = "server")]
13use crate::api::auth::get_current_admin_user;
14#[cfg(feature = "server")]
15use crate::api::error::AppError;
16
17/// 单个缓存的统计快照(前端展示用)。
18#[derive(Serialize, Deserialize, Debug, Clone)]
19pub struct CacheStat {
20    pub name: String,
21    pub entry_count: u64,
22    pub hits: u64,
23    pub misses: u64,
24    /// 命中率(0.0–1.0)。
25    pub hit_rate: f64,
26}
27
28/// 服务器状态聚合数据。
29#[derive(Serialize, Deserialize, Debug, Clone)]
30pub struct ServerStatus {
31    /// 进程运行时间(秒)。
32    pub uptime_secs: u64,
33    /// DB 连接池总大小(已创建连接数)。
34    pub pool_size: usize,
35    /// 连接池最大容量。
36    pub pool_max_size: usize,
37    /// 当前空闲可用连接数。
38    pub pool_available: usize,
39    /// 正在等待获取连接的请求数。
40    pub pool_waiting: usize,
41    /// 有效会话数(sessions 表 expires_at > now())。
42    pub active_sessions: i64,
43    /// 主机层指标快照(CPU/内存/磁盘等)。
44    pub host: crate::sysinfo_sampler::SystemSnapshot,
45    /// 各缓存命中率与条目数。
46    pub caches: Vec<CacheStat>,
47}
48
49/// 进程启动时刻(LazyLock),用于计算运行时间。
50#[cfg(feature = "server")]
51static STARTED_AT: std::sync::LazyLock<std::time::Instant> =
52    std::sync::LazyLock::new(std::time::Instant::now);
53
54/// 获取服务器状态(只读,管理员)。
55#[server(GetServerStatus, "/api")]
56pub async fn get_server_status() -> Result<ServerStatus, ServerFnError> {
57    let _user = get_current_admin_user().await?;
58
59    #[cfg(feature = "server")]
60    {
61        use crate::cache::cache_stats;
62        use crate::db::pool::{get_conn, DB_POOL};
63
64        // 连接池状态
65        let pool_status = DB_POOL.status();
66        let pool_size = pool_status.size;
67        let pool_max_size = pool_status.max_size;
68        let pool_available = pool_status.available;
69        let pool_waiting = pool_status.waiting;
70
71        // 活跃会话数
72        let client = get_conn().await.map_err(AppError::db_conn)?;
73        let active_sessions: i64 = client
74            .query_one(
75                "SELECT count(*) FROM sessions WHERE expires_at > now()",
76                &[],
77            )
78            .await
79            .map_err(AppError::query)?
80            .get(0);
81
82        // 进程运行时间
83        let uptime_secs = STARTED_AT.elapsed().as_secs();
84
85        // 主机层快照
86        let host = crate::sysinfo_sampler::read_snapshot().await;
87
88        // 缓存统计
89        let caches = cache_stats()
90            .into_iter()
91            .map(|s| CacheStat {
92                name: s.name.to_string(),
93                entry_count: s.entry_count,
94                hits: s.hits,
95                misses: s.misses,
96                hit_rate: s.hit_rate,
97            })
98            .collect();
99
100        Ok(ServerStatus {
101            uptime_secs,
102            pool_size,
103            pool_max_size,
104            pool_available,
105            pool_waiting,
106            active_sessions,
107            host,
108            caches,
109        })
110    }
111    #[cfg(not(feature = "server"))]
112    {
113        Ok(ServerStatus {
114            uptime_secs: 0,
115            pool_size: 0,
116            pool_max_size: 0,
117            pool_available: 0,
118            pool_waiting: 0,
119            active_sessions: 0,
120            host: crate::sysinfo_sampler::read_snapshot().await,
121            caches: vec![],
122        })
123    }
124}