Skip to content

Commit ffce46b

Browse files
h0lybyteclaude
andauthored
feat(discordsh): add real-time health monitoring with sysinfo (Phase 3) (#7223)
Add HealthMonitor with background 60s polling for CPU, memory, threads, and process metrics via sysinfo 0.38.2. Wire shared Arc<HealthMonitor> into both Discord bot and Axum HTTP server. /health command shows rich embed with status thresholds (Healthy/Warning/Critical), /status embed gains health fields, HTTP /health returns JSON with system metrics, and Refresh button triggers immediate force_refresh(). Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 4ef8cfe commit ffce46b

12 files changed

Lines changed: 541 additions & 104 deletions

File tree

Cargo.lock

Lines changed: 54 additions & 40 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/discordsh/axum-discordsh/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ num_cpus = "1.17.0"
3030
dotenvy = "0.15"
3131
serenity = { version = "0.12.5", default-features = false, features = ["client", "gateway", "model", "rustls_backend", "cache"] }
3232
poise = "0.6.1"
33+
sysinfo = "0.38.2"
3334

3435
# Workspace path dependencies
3536
jedi = { path = "../../../packages/rust/jedi" }

apps/discordsh/axum-discordsh/src/astro/askama.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ pub struct AstroTemplate<'a> {
1414
}
1515

1616
impl<'a> AstroTemplate<'a> {
17-
pub fn new(
18-
content: &'a str,
19-
path: &'a str,
20-
title: &'a str,
21-
description: &'a str,
22-
) -> Self {
23-
Self { content, path, title, description }
17+
pub fn new(content: &'a str, path: &'a str, title: &'a str, description: &'a str) -> Self {
18+
Self {
19+
content,
20+
path,
21+
title,
22+
description,
23+
}
2424
}
2525
}
2626

@@ -32,7 +32,11 @@ impl<T: Template> IntoResponse for TemplateResponse<T> {
3232
Ok(html) => Html(html).into_response(),
3333
Err(err) => {
3434
tracing::error!(error = %err, "template rendering failed");
35-
(StatusCode::INTERNAL_SERVER_ERROR, "Failed to render template").into_response()
35+
(
36+
StatusCode::INTERNAL_SERVER_ERROR,
37+
"Failed to render template",
38+
)
39+
.into_response()
3640
}
3741
}
3842
}

apps/discordsh/axum-discordsh/src/astro/mod.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
pub mod askama;
22

33
use axum::{
4-
http::{header, StatusCode},
5-
response::IntoResponse,
64
Router,
5+
http::{StatusCode, header},
6+
response::IntoResponse,
77
};
88
use std::convert::Infallible;
99
use std::path::PathBuf;
@@ -23,7 +23,10 @@ impl StaticConfig {
2323
let precompressed = std::env::var("STATIC_PRECOMPRESSED")
2424
.map(|v| v != "0" && v.to_lowercase() != "false")
2525
.unwrap_or(true);
26-
Self { base_dir, precompressed }
26+
Self {
27+
base_dir,
28+
precompressed,
29+
}
2730
}
2831
}
2932

@@ -33,9 +36,8 @@ pub fn build_static_router(config: &StaticConfig) -> Router {
3336

3437
// Read Astro's 404.html at startup for the not-found fallback
3538
let not_found_html = Arc::new(
36-
std::fs::read_to_string(base.join("404.html")).unwrap_or_else(|_| {
37-
"<html><body><h1>404 - Not Found</h1></body></html>".to_string()
38-
}),
39+
std::fs::read_to_string(base.join("404.html"))
40+
.unwrap_or_else(|_| "<html><body><h1>404 - Not Found</h1></body></html>".to_string()),
3941
);
4042

4143
let serve_dir = |path: PathBuf| {

apps/discordsh/axum-discordsh/src/discord/bot.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
use std::sync::Arc;
2+
13
use anyhow::Result;
24
use kbve::entity::client::vault::VaultClient;
35
use poise::serenity_prelude as serenity;
46
use tracing::{info, warn};
57

68
use super::commands;
79
use super::components;
10+
use crate::health::HealthMonitor;
811

912
/// Vault secret UUID for the Discord bot token (shared with the Python notification-bot).
1013
const DISCORD_TOKEN_VAULT_ID: &str = "39781c47-be8f-4a10-ae3a-714da299ca07";
@@ -15,6 +18,8 @@ const DISCORD_TOKEN_VAULT_ID: &str = "39781c47-be8f-4a10-ae3a-714da299ca07";
1518
pub struct Data {
1619
/// Instant the bot started; used to compute uptime for the status embed.
1720
pub start_time: std::time::Instant,
21+
/// Shared health monitor for system metrics.
22+
pub health_monitor: Arc<HealthMonitor>,
1823
}
1924

2025
/// Error type for poise commands.
@@ -79,7 +84,7 @@ async fn event_handler(
7984

8085
// ── Bot startup ─────────────────────────────────────────────────────────
8186

82-
pub async fn start() -> Result<()> {
87+
pub async fn start(health_monitor: Arc<HealthMonitor>) -> Result<()> {
8388
let token = match resolve_token().await {
8489
Some(t) => t,
8590
None => {
@@ -100,7 +105,7 @@ pub async fn start() -> Result<()> {
100105
},
101106
..Default::default()
102107
})
103-
.setup(|ctx, ready, framework| {
108+
.setup(move |ctx, ready, framework| {
104109
Box::pin(async move {
105110
info!("Discord bot connected as {}", ready.user.name);
106111

@@ -129,6 +134,7 @@ pub async fn start() -> Result<()> {
129134

130135
Ok(Data {
131136
start_time: std::time::Instant::now(),
137+
health_monitor,
132138
})
133139
})
134140
})

0 commit comments

Comments
 (0)