Private
Public Access
1
0

Add GET /api/v1/system/services/{name} endpoint for service health checks

- Add ServiceStatus struct with name, display_name, active_state, sub_state,
  load_state, enabled_state, main_pid, healthy fields
- Add get_service_status() to PackageManagerBackend trait
- Implement get_service_status() in AptBackend with systemd and OpenRC support
- Add get_service_status HTTP handler in system.rs
- Add /system/services/{name} route
- Add E2E test for service status endpoint
- Bump version to 0.3.6
This commit is contained in:
2026-05-04 23:44:26 +00:00
parent 385c675736
commit 165db77a14
8 changed files with 298 additions and 35 deletions

View File

@ -8,7 +8,7 @@ use chrono::{DateTime, Utc};
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::{RwLock, broadcast};
use tokio::sync::{broadcast, RwLock};
use uuid::Uuid;
/// Job status
@ -271,11 +271,7 @@ impl JobManager {
if let Some(job) = jobs.get_mut(job_id) {
job.complete();
event_data = Some((
job.status.clone(),
job.progress,
job.message.clone(),
));
event_data = Some((job.status.clone(), job.progress, job.message.clone()));
} else {
event_data = None;
}
@ -296,11 +292,7 @@ impl JobManager {
if let Some(job) = jobs.get_mut(job_id) {
job.fail(error);
event_data = Some((
job.status.clone(),
job.progress,
job.message.clone(),
));
event_data = Some((job.status.clone(), job.progress, job.message.clone()));
} else {
event_data = None;
}

View File

@ -256,10 +256,8 @@ impl Handler<BroadcastEvent> for WsJobActor {
let event = msg.0;
// Check if this client should receive this event
let should_forward = self.subscribed_all
|| self
.subscribed_jobs
.contains(&event.job_id.to_string());
let should_forward =
self.subscribed_all || self.subscribed_jobs.contains(&event.job_id.to_string());
if should_forward {
let server_msg = WsServerMessage::from_job_status_event(&event);
@ -300,24 +298,22 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WsJobActor {
// Parse as client message
match serde_json::from_str::<WsClientMessage>(&text) {
Ok(client_msg) => match client_msg {
WsClientMessage::Subscribe { job_id } => {
match job_id {
Some(id) => {
self.subscribed_jobs.insert(id.clone());
let msg = WsServerMessage::subscribed(&Some(id));
if let Ok(json) = serde_json::to_string(&msg) {
ctx.text(json);
}
}
None => {
self.subscribed_all = true;
let msg = WsServerMessage::subscribed(&None);
if let Ok(json) = serde_json::to_string(&msg) {
ctx.text(json);
}
WsClientMessage::Subscribe { job_id } => match job_id {
Some(id) => {
self.subscribed_jobs.insert(id.clone());
let msg = WsServerMessage::subscribed(&Some(id));
if let Ok(json) = serde_json::to_string(&msg) {
ctx.text(json);
}
}
}
None => {
self.subscribed_all = true;
let msg = WsServerMessage::subscribed(&None);
if let Ok(json) = serde_json::to_string(&msg) {
ctx.text(json);
}
}
},
WsClientMessage::Unsubscribe { job_id } => {
self.subscribed_jobs.remove(&job_id);
let msg = WsServerMessage::unsubscribed(&job_id);
@ -333,7 +329,10 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WsJobActor {
text = %text,
"Invalid WebSocket client message"
);
let msg = WsServerMessage::error("invalid_message", &format!("Invalid message: {}", e));
let msg = WsServerMessage::error(
"invalid_message",
&format!("Invalid message: {}", e),
);
if let Ok(json) = serde_json::to_string(&msg) {
ctx.text(json);
}