webui control point step 2

This commit is contained in:
2025-12-05 07:08:03 +01:00
parent b38d2593b3
commit 9f62191830
31 changed files with 1343 additions and 207 deletions

View File

@@ -10,6 +10,7 @@ anyhow = "1.0"
axum = "0.8.4"
tokio = { version = "1.35", features = ["rt-multi-thread", "macros", "sync", "time", "signal"] }
tokio-stream = "0.1"
tokio-util = "0.7"
futures-util = "0.3"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

View File

@@ -27,6 +27,7 @@ use std::future::Future;
use std::net::SocketAddr;
use std::sync::Arc;
use tokio::{signal, sync::RwLock, task::JoinHandle};
use tokio_util::sync::CancellationToken;
use tracing::info;
use utoipa::OpenApi;
use utoipa_swagger_ui::SwaggerUi;
@@ -92,6 +93,7 @@ pub struct Server {
join_handle: Option<JoinHandle<()>>,
log_state: Option<LogState>,
api_registry: ApiRegistryState,
shutdown_token: CancellationToken,
}
impl Server {
@@ -126,6 +128,7 @@ impl Server {
join_handle: None,
log_state: None,
api_registry,
shutdown_token: CancellationToken::new(),
}
}
@@ -136,6 +139,14 @@ impl Server {
Self::new("PMO-Music-Server", url, port)
}
/// Retourne une copie du token d'arrêt gracieux
///
/// Ce token peut être donné aux composants qui ont besoin de savoir
/// quand le serveur s'arrête (threads, tâches longues, etc.)
pub fn shutdown_token(&self) -> CancellationToken {
self.shutdown_token.clone()
}
/// Ajoute une route JSON dynamique
///
/// Crée un endpoint qui retourne du JSON. La closure fournie sera appelée
@@ -532,22 +543,43 @@ impl Server {
);
let router = self.router.clone();
// Créer un channel pour signaler l'arrêt gracieux
let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel::<()>();
// Tâche qui écoute Ctrl+C et signale l'arrêt
let shutdown_token_for_signal = self.shutdown_token.clone();
let shutdown_signal = tokio::spawn(async move {
signal::ctrl_c().await.expect("failed to listen for ctrl_c");
info!("Ctrl+C reçu, arrêt gracieux");
// Déclencher le token d'arrêt pour tous les composants
shutdown_token_for_signal.cancel();
// Envoyer le signal d'arrêt au serveur HTTP (ignorer l'erreur si le receiver a déjà été drop)
let _ = shutdown_tx.send(());
});
// Tâche serveur avec arrêt gracieux
let server_task = tokio::spawn(async move {
let r = router.read().await.clone();
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
axum::serve(listener, r.into_make_service()).await.unwrap();
});
let shutdown_task = tokio::spawn(async move {
signal::ctrl_c().await.expect("failed to listen for ctrl_c");
info!("Ctrl+C reçu, arrêt gracieux");
// Serveur avec arrêt gracieux
axum::serve(listener, r.into_make_service())
.with_graceful_shutdown(async move {
// Attendre le signal d'arrêt
let _ = shutdown_rx.await;
})
.await
.unwrap();
info!("Serveur HTTP arrêté proprement");
});
self.join_handle = Some(tokio::spawn(async move {
tokio::select! {
_ = server_task => {},
_ = shutdown_task => {},
}
// Attendre que le serveur se termine (après réception du signal)
let _ = server_task.await;
// Nettoyer la tâche de signal
let _ = shutdown_signal.await;
}));
}