From 115658bf6a385658f2e562f1350e3837de72b7ef Mon Sep 17 00:00:00 2001 From: Eric Coissac Date: Tue, 7 Oct 2025 15:03:52 +0200 Subject: [PATCH] Correction de ChatGPT --- pmocovers/src/pmoserver_impl.rs | 14 +- pmoserver/src/server.rs | 321 ++++++++----------------- pmoupnp/src/actions/action_instance.rs | 2 - pmoupnp/src/object_set.rs | 2 +- pmoupnp/src/ssdp/server.rs | 2 +- 5 files changed, 114 insertions(+), 227 deletions(-) diff --git a/pmocovers/src/pmoserver_impl.rs b/pmocovers/src/pmoserver_impl.rs index c36029ab..bd4a5af5 100644 --- a/pmocovers/src/pmoserver_impl.rs +++ b/pmocovers/src/pmoserver_impl.rs @@ -37,7 +37,7 @@ use axum::{ Json, Router, }; use pmoserver::Server; -use tracing::{debug, info}; +use tracing::{debug, info, warn}; use std::sync::Arc; use utoipa::OpenApi; @@ -52,11 +52,13 @@ async fn get_cover_image( let path = req.uri().path(); let parts: Vec<&str> = path.split('/').collect(); - if parts.len() < 4 { + warn!("{:?}",parts); + + if parts.len() != 2 { return (StatusCode::BAD_REQUEST, "Invalid path").into_response(); } - let pk = parts[3]; + let pk = parts[1]; match cache.get(pk).await { Ok(file_path) => { @@ -83,12 +85,12 @@ async fn get_cover_variant( let path = req.uri().path(); let parts: Vec<&str> = path.split('/').collect(); - if parts.len() < 5 { + if parts.len() != 3 { return (StatusCode::BAD_REQUEST, "Invalid path").into_response(); } - let pk = parts[3]; - let size = match parts[4].parse::() { + let pk = parts[1]; + let size = match parts[2].parse::() { Ok(s) => s, Err(_) => return (StatusCode::BAD_REQUEST, "Invalid size").into_response(), }; diff --git a/pmoserver/src/server.rs b/pmoserver/src/server.rs index 665ada30..3f8f559a 100644 --- a/pmoserver/src/server.rs +++ b/pmoserver/src/server.rs @@ -16,13 +16,15 @@ use crate::logs::{LogState, LoggingOptions, init_logging, log_dump, log_sse}; use axum::handler::Handler; use axum::response::Redirect; -use axum::routing::get; +use axum::routing::{get, post}; use axum::{Json, Router}; use axum_embed::ServeEmbed; use pmoconfig::get_config; use rust_embed::RustEmbed; use serde::Serialize; -use std::{net::SocketAddr, sync::Arc}; +use std::future::Future; +use std::net::SocketAddr; +use std::sync::Arc; use tokio::{signal, sync::RwLock, task::JoinHandle}; use tracing::info; use utoipa_swagger_ui::SwaggerUi; @@ -30,11 +32,8 @@ use utoipa_swagger_ui::SwaggerUi; /// Info serveur sérialisable #[derive(Clone, Serialize, utoipa::ToSchema)] pub struct ServerInfo { - /// Nom du serveur pub name: String, - /// URL de base pub base_url: String, - /// Port HTTP pub http_port: u16, } @@ -80,8 +79,7 @@ impl Server { let config = get_config(); let url = config.get_base_url(); let port = config.get_http_port(); - - return Self::new("PMO-Music-Server", url, port); + Self::new("PMO-Music-Server", url, port) } /// Ajoute une route JSON dynamique @@ -112,11 +110,10 @@ impl Server { pub async fn add_route(&mut self, path: &str, f: F) where F: Fn() -> Fut + Send + Sync + 'static, - Fut: std::future::Future + Send + 'static, + Fut: Future + Send + 'static, T: Serialize + Send + 'static, { let f = Arc::new(f); - let handler = { let f = f.clone(); move || { @@ -128,76 +125,81 @@ impl Server { let route = Router::new().route("/", get(handler)); let mut r = self.router.write().await; - *r = std::mem::take(&mut *r).nest(path, route); - } - - /// Add a new router safely: - /// - If `path` starts with '/', it is merged at root level. - /// - Otherwise, it is nested under the given subpath. - pub async fn add_router(&mut self, path: &str, route: Router) { - let mut r = self.router.write().await; - - // Take current router without losing content - let current = std::mem::take(&mut *r); - - let combined = if path.starts_with('/') { - // Absolute path => merge directly at root - tracing::debug!("Merging router at root path: {}", path); - current.merge(route) + *r = if path == "/" { + std::mem::take(&mut *r).merge(route) } else { - // Relative path => nest under the given path - let normalized = format!("/{}", path.trim_start_matches('/')); - tracing::debug!("Nesting router under: {}", normalized); - current.nest(&normalized, route) + std::mem::take(&mut *r).nest(path, route) }; - - *r = combined; } - - /// Ajoute un répertoire de fichiers statiques - /// - /// Sert des fichiers embarqués via `RustEmbed`. Les fichiers sont compilés - /// dans le binaire à la compilation. - /// - /// # Arguments - /// - /// * `path` - Chemin où monter les fichiers statiques - /// - /// # Type Parameter - /// - /// * `E` - Type RustEmbed définissant le répertoire à servir - /// - /// # Exemple - /// - /// ```ignore - /// use pmoupnp::server::Server; - /// use rust_embed::RustEmbed; - /// - /// #[derive(RustEmbed, Clone)] - /// #[folder = "static/"] - /// struct Assets; - /// - /// # #[tokio::main] - /// # async fn main() { - /// let mut server = Server::new("Test", "http://localhost:3000", 3000); - /// server.add_dir::("/assets").await; - /// // Les fichiers de static/ sont accessibles via /assets/* - /// # } - /// ``` + + /// Ajoute un handler Axum standard + pub async fn add_handler(&mut self, path: &str, handler: H) + where + H: Handler + Clone + 'static, + T: 'static, + { + let route = Router::new().route("/", get(handler.clone())); + + let mut r = self.router.write().await; + *r = if path == "/" { + std::mem::take(&mut *r).merge(route) + } else { + std::mem::take(&mut *r).nest(path, route) + }; + } + + /// Ajoute un handler POST avec état + pub async fn add_post_handler_with_state(&mut self, path: &str, handler: H, state: S) + where + H: Handler + Clone + 'static, + T: 'static, + S: Clone + Send + Sync + 'static, + { + let route = Router::new() + .route("/", post(handler.clone())) + .with_state(state.clone()); + + let mut r = self.router.write().await; + *r = if path == "/" { + std::mem::take(&mut *r).merge(route) + } else { + std::mem::take(&mut *r).nest(path, route) + }; + } + + /// Ajoute un handler avec état + pub async fn add_handler_with_state(&mut self, path: &str, handler: H, state: S) + where + H: Handler + Clone + 'static, + T: 'static, + S: Clone + Send + Sync + 'static, + { + let route = Router::new() + .route("/", get(handler.clone())) + .with_state(state.clone()); + + let mut r = self.router.write().await; + *r = if path == "/" { + std::mem::take(&mut *r).merge(route) + } else { + std::mem::take(&mut *r).nest(path, route) + }; + } + + /// Ajoute un répertoire statique pub async fn add_dir(&mut self, path: &str) where E: RustEmbed + Clone + Send + Sync + 'static, { let serve = ServeEmbed::::new(); - let mut r = self.router.write().await; - if path == "/" { - *r = std::mem::take(&mut *r).fallback_service(serve); + let route = Router::new().fallback_service(serve); + *r = if path == "/" { + std::mem::take(&mut *r).merge(route) } else { - let route = Router::new().fallback_service(serve); - *r = std::mem::take(&mut *r).nest(path, route); - } + std::mem::take(&mut *r).nest(path, route) + }; } /// Ajoute une Single Page Application (SPA) @@ -252,125 +254,12 @@ impl Server { let mut r = self.router.write().await; - if path == "/" { - *r = std::mem::take(&mut *r).fallback_service(serve); + let route = Router::new().fallback_service(serve); + *r = if path == "/" { + std::mem::take(&mut *r).merge(route) } else { - let route = Router::new().fallback_service(serve); - *r = std::mem::take(&mut *r).nest(path, route); - } - } - - /// Ajoute un handler Axum personnalisé - /// - /// Pour des cas d'usage avancés nécessitant un contrôle complet sur le handler. - /// - /// # Arguments - /// - /// * `path` - Chemin de la route - /// * `handler` - Handler Axum - /// - /// # Exemple - /// - /// ```rust,no_run - /// # use pmoupnp::server::Server; - /// # use axum::response::Html; - /// # #[tokio::main] - /// # async fn main() { - /// # let mut server = Server::new("Test", "http://localhost:3000", 3000); - /// async fn custom_handler() -> Html<&'static str> { - /// Html("

Custom Response

") - /// } - /// - /// server.add_handler("/custom", custom_handler).await; - /// # } - /// ``` - pub async fn add_handler(&mut self, path: &str, handler: H) - where - H: Handler, - T: 'static, - { - let route = Router::new().route("/", get(handler)); - - let mut r = self.router.write().await; - *r = std::mem::take(&mut *r).nest(path, route); - } - - /// Ajoute un handler avec state (pour SSE, extracteurs, etc.) - /// - /// Permet d'utiliser des extracteurs Axum comme `State`, `Query`, etc. - /// Idéal pour Server-Sent Events (SSE), WebSockets ou tout handler nécessitant un état partagé. - /// - /// # Arguments - /// - /// * `path` - Chemin de la route - /// * `handler` - Handler Axum avec extracteurs - /// * `state` - État partagé (doit être Clone + Send + Sync) - /// - /// # Exemple avec SSE - /// - /// ```ignore - /// use pmoupnp::server::Server; - /// use axum::extract::State; - /// use axum::response::sse::{Event, Sse, KeepAlive}; - /// use tokio::sync::broadcast; - /// - /// #[derive(Clone)] - /// struct LogState { - /// tx: broadcast::Sender - /// } - /// - /// impl LogState { - /// fn subscribe(&self) -> broadcast::Receiver { - /// self.tx.subscribe() - /// } - /// } - /// - /// async fn log_sse(State(state): State) -> Sse>> { - /// let mut rx = state.subscribe(); - /// let stream = async_stream::stream! { - /// while let Ok(msg) = rx.recv().await { - /// yield Ok(Event::default().data(msg)); - /// } - /// }; - /// Sse::new(stream).keep_alive(KeepAlive::default()) - /// } - /// - /// let log_state = LogState { tx: broadcast::channel(100).0 }; - /// server.add_handler_with_state("/logs", log_sse, log_state).await; - /// ``` - pub async fn add_handler_with_state(&mut self, path: &str, handler: H, state: S) - where - H: Handler, - T: 'static, - S: Clone + Send + Sync + 'static, - { - let route = Router::new().route("/", get(handler)).with_state(state); - - let mut r = self.router.write().await; - *r = std::mem::take(&mut *r).nest(path, route); - } - - /// Ajoute un handler POST avec state - /// - /// Similaire à `add_handler_with_state` mais pour les requêtes POST. - /// - /// # Arguments - /// - /// * `path` - Chemin de la route - /// * `handler` - Handler Axum pour POST - /// * `state` - État partagé - pub async fn add_post_handler_with_state(&mut self, path: &str, handler: H, state: S) - where - H: Handler, - T: 'static, - S: Clone + Send + Sync + 'static, - { - let route = Router::new() - .route("/", axum::routing::post(handler)) - .with_state(state); - - let mut r = self.router.write().await; - *r = std::mem::take(&mut *r).nest(path, route); + std::mem::take(&mut *r).nest(path, route) + }; } /// Ajoute une redirection HTTP @@ -393,23 +282,20 @@ impl Server { /// server.add_redirect("/", "/app").await; /// # } /// ``` + pub async fn add_redirect(&mut self, from: &str, to: &str) { let to = to.to_string(); - let handler = move || { - let to = to.clone(); - async move { Redirect::permanent(&to) } + let make_handler = || { + let target = to.clone(); + get(move || async move { Redirect::permanent(&target) }) }; let mut r = self.router.write().await; - - if from == "/" { - // Pour la racine, utiliser merge au lieu de nest - let route = Router::new().route("/", get(handler)); - *r = std::mem::take(&mut *r).merge(route); + *r = if from == "/" { + std::mem::take(&mut *r).merge(Router::new().route("/", make_handler())) } else { - let route = Router::new().route("/", get(handler)); - *r = std::mem::take(&mut *r).nest(from, route); - } + std::mem::take(&mut *r).nest(from, Router::new().route("/", make_handler())) + }; } /// Ajoute une API documentée avec OpenAPI et Swagger UI @@ -481,21 +367,16 @@ impl Server { /// - `/api/api1/users` et `/api/api2/products` sont accessibles via Axum. /// - `/swagger-ui/api1` et `/swagger-ui/api2` affichent la documentation Swagger correspondante. /// - `/api-docs/api1.json` et `/api-docs/api2.json` fournissent les spécifications OpenAPI respectives. - pub async fn add_openapi( &mut self, api_router: Router, openapi: utoipa::openapi::OpenApi, - name: &str, // nom unique pour différencier Swagger et OpenAPI + name: &str, ) { - use axum::routing::get; - use utoipa_swagger_ui::SwaggerUi; - - // Stocker le router API dans self.api_router si tu veux y accéder plus tard let mut api_r = self.api_router.write().await; *api_r = Some(api_router.clone()); + drop(api_r); - // Générer des chemins uniques pour Swagger UI et OpenAPI JSON let swagger_path = format!("/swagger-ui/{}", name); let swagger_path_static: &'static str = Box::leak(swagger_path.into_boxed_str()); @@ -504,16 +385,31 @@ impl Server { let swagger = SwaggerUi::new(swagger_path_static).url(openapi_json_path_static, openapi); - // Nester le router API sous /api/{name} let base_path = format!("/api/{}", name); let nested_router = Router::new().nest(&base_path, api_router); - // Fusionner avec le router principal let mut r = self.router.write().await; - let mut combined = std::mem::take(&mut *r); - combined = combined.merge(nested_router).merge(swagger); + *r = std::mem::take(&mut *r).merge(nested_router).merge(swagger); + } + /// Ajoute un sous-router au serveur + /// + /// - Si `path` est "/", merge directement au router principal + /// - Sinon, nest le router sous le chemin donné + pub async fn add_router(&mut self, path: &str, sub_router: Router) { + let mut r = self.router.write().await; + + let combined = if path == "/" { + // Merge directement à la racine + r.clone().merge(sub_router) + } else { + // Sous-chemin => nest + let normalized = format!("/{}", path.trim_start_matches('/')); + r.clone().nest(&normalized, sub_router) + }; + *r = combined; } + /// Démarre le serveur HTTP /// /// Lance le serveur sur le port configuré et met en place la gestion @@ -537,16 +433,7 @@ impl Server { self.name, self.base_url, self.http_port, self.base_url, self.http_port ); - // Merger le routeur API si présent - let api_router = self.api_router.read().await; - if let Some(api_r) = api_router.as_ref() { - let mut r = self.router.write().await; - *r = std::mem::take(&mut *r).nest("/api", api_r.clone()); - } - drop(api_router); - let router = self.router.clone(); - let server_task = tokio::spawn(async move { let r = router.read().await.clone(); let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); diff --git a/pmoupnp/src/actions/action_instance.rs b/pmoupnp/src/actions/action_instance.rs index 49ca2938..02e95fd7 100644 --- a/pmoupnp/src/actions/action_instance.rs +++ b/pmoupnp/src/actions/action_instance.rs @@ -3,8 +3,6 @@ use std::sync::Arc; use xmltree::{Element, XMLNode}; use crate::actions::Action; -use crate::actions::Argument; -use crate::actions::ArgumentSet; use crate::actions::ArgInstanceSet; use crate::actions::ActionInstance; use crate::UpnpInstance; diff --git a/pmoupnp/src/object_set.rs b/pmoupnp/src/object_set.rs index 224ce185..2d99ebec 100644 --- a/pmoupnp/src/object_set.rs +++ b/pmoupnp/src/object_set.rs @@ -2,7 +2,7 @@ use std::{collections::HashMap, sync::Arc}; use std::sync::RwLock; -use crate::{UpnpDeepClone, UpnpObjectSet, UpnpObjectSetError, UpnpSet, UpnpTypedObject}; +use crate::{UpnpDeepClone, UpnpObjectSet, UpnpObjectSetError, UpnpTypedObject}; /// Implémentation du clonage profond pour `UpnpObjectSet`. /// diff --git a/pmoupnp/src/ssdp/server.rs b/pmoupnp/src/ssdp/server.rs index fc344487..4f729242 100644 --- a/pmoupnp/src/ssdp/server.rs +++ b/pmoupnp/src/ssdp/server.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; use std::net::{SocketAddr, UdpSocket}; use std::sync::{Arc, RwLock}; use std::time::Duration; -use tracing::{error, info, warn}; +use tracing::{info, warn}; /// Serveur SSDP gérant les annonces et découvertes pub struct SsdpServer {