//! # pmoapp - Application web UPnP pour PMOMusic //! //! Cette crate fournit l'application web frontend pour le contrôle et la visualisation //! des devices UPnP MediaRenderer, intégrée via RustEmbed pour être servie par pmoserver. //! //! ## Vue d'ensemble //! //! `pmoapp` est une application Vue.js 3 moderne avec TypeScript qui offre une interface //! utilisateur pour : //! - Visualiser les logs système en temps réel (Server-Sent Events) //! - Contrôler les devices UPnP MediaRenderer //! - Afficher et formater automatiquement le XML dans les logs //! //! ## Fonctionnalités //! //! ### 📦 Frontend intégré //! - Application web compilée et embarquée dans le binaire Rust //! - Aucun fichier statique externe à gérer en production //! - Intégration via `RustEmbed` pour une distribution simplifiée //! //! ### 🎨 Interface utilisateur //! - **LogView** : Visualisation des logs en temps réel avec filtres par niveau //! - **Auto-scroll** : Défilement automatique des nouveaux logs (désactivable) //! - **Formatage XML** : Détection et coloration syntaxique automatique du XML //! - **Design responsive** : Compatible desktop et mobile //! - **Thème sombre** : Style inspiré de VS Code pour une meilleure lisibilité //! //! ### 🚀 Zero configuration //! - Pas besoin de serveur web séparé pour les assets //! - Les fichiers sont servis directement depuis la mémoire du binaire //! - Configuration automatique du routing Vue Router //! //! ## Architecture //! //! ### Stack technique //! //! - **Frontend** : Vue.js 3 avec Composition API //! - **Langage** : TypeScript //! - **Build** : Vite (rapide, moderne, HMR) //! - **Routing** : Vue Router //! - **Markdown** : Marked.js pour le rendu //! - **Sécurité** : DOMPurify pour la sanitization HTML //! //! ### Structure des fichiers //! //! ```text //! pmoapp/ //! ├── Cargo.toml # Dépendances Rust (rust-embed) //! ├── src/ //! │ └── lib.rs # Point d'entrée Rust (ce fichier) //! └── webapp/ //! ├── src/ //! │ ├── main.ts # Point d'entrée Vue.js //! │ ├── App.vue # Composant racine //! │ ├── router/ # Configuration Vue Router //! │ └── components/ //! │ ├── LogView.vue # Visualiseur de logs SSE //! │ └── ... //! ├── dist/ # Build output (généré, non versionné) //! ├── package.json # Dépendances npm //! └── vite.config.ts # Configuration Vite //! ``` //! //! ## Workflow de build //! //! ### 1. Build de la webapp (Vue.js) //! //! ```bash //! # Installation des dépendances //! cd pmoapp/webapp //! npm install //! //! # Build de production //! npm run build //! # Génère : webapp/dist/index.html, assets/*.js, assets/*.css //! ``` //! //! ### 2. Compilation Rust //! //! ```bash //! cargo build //! # RustEmbed inclut automatiquement les fichiers de webapp/dist/ //! ``` //! //! ### 3. Utilisation avec Makefile //! //! ```bash //! # Build complet (webapp + Rust) //! make build //! //! # Ou juste la webapp //! make webapp //! //! # Clean //! make clean //! ``` //! //! ## Utilisation //! //! ### Exemple basique //! //! ```rust,ignore //! use pmoapp::Webapp; //! use pmoserver::ServerBuilder; //! //! #[tokio::main] //! async fn main() { //! let mut server = ServerBuilder::new("MyApp", "http://localhost", 8080) //! .build(); //! //! // Ajouter la webapp comme Single Page Application //! server.add_spa::("/app").await; //! //! // Ajouter une redirection de la racine vers /app //! server.add_redirect("/", "/app").await; //! //! server.start().await; //! server.wait().await; //! } //! ``` //! //! ### Exemple avec logs SSE //! //! ```rust,ignore //! use pmoapp::Webapp; //! use pmoserver::{ServerBuilder, logs::{LogState, SseLayer}}; //! use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; //! //! #[tokio::main] //! async fn main() { //! // Configuration des logs avec SSE //! let log_state = LogState::new(1000); // Buffer de 1000 logs //! tracing_subscriber::registry() //! .with(tracing_subscriber::fmt::layer()) //! .with(SseLayer::new(log_state.clone())) //! .init(); //! //! let mut server = ServerBuilder::new("MyApp", "http://localhost", 8080).build(); //! //! // Endpoints SSE pour les logs //! server.add_handler_with_state("/log-sse", pmoserver::logs::log_sse, log_state.clone()).await; //! server.add_handler_with_state("/log-dump", pmoserver::logs::log_dump, log_state).await; //! //! // Webapp (consommera les logs via /log-sse) //! server.add_spa::("/app").await; //! server.add_redirect("/", "/app").await; //! //! server.start().await; //! server.wait().await; //! } //! ``` //! //! ## Développement //! //! ### Mode développement Vue.js //! //! Pour développer la webapp avec Hot Module Replacement : //! //! ```bash //! cd pmoapp/webapp //! npm run dev //! # Serveur de dev sur http://localhost:5173 //! ``` //! //! ### Rebuild après modifications //! //! Après avoir modifié le code Vue.js : //! //! ```bash //! # Rebuild webapp + recompile Rust //! make build //! //! # Ou séparément //! make webapp # Build Vue.js seulement //! cargo build # Recompile Rust (intègre le nouveau dist/) //! ``` //! //! ## Composants Vue.js //! //! ### LogView //! //! Composant principal pour la visualisation des logs : //! //! - **Connexion SSE** : Stream temps réel via EventSource //! - **Filtrage** : Par niveau (TRACE, DEBUG, INFO, WARN, ERROR) //! - **Auto-scroll** : Activable/désactivable //! - **Formatage** : Markdown + détection XML automatique //! - **Buffer** : Limite à 1000 logs en mémoire //! - **Déduplication** : Évite les logs en double //! //! ### Formatage XML //! //! Le composant LogView détecte automatiquement le XML dans les messages : //! //! ```text //! Input: "INFO: ..." //! Output: Bloc de code avec coloration syntaxique XML //! ``` //! //! - Détection via regex : ``, ``, etc.) //! - Conversion en bloc markdown : ` ```xml ... ``` ` //! - Rendu avec coloration et scrollbar pour le XML long //! //! ## Intégration avec pmoupnp //! //! La webapp communique avec les devices UPnP via les endpoints HTTP fournis par //! `pmoserver` et `pmoupnp` : //! //! - `/log-sse` : Stream de logs (Server-Sent Events) //! - `/log-dump` : Historique des logs //! - `/device/*/description.xml` : Descripteurs UPnP //! - `/service/*/control` : Endpoints de contrôle SOAP //! - `/service/*/event` : Souscription aux événements UPnP //! //! ## Notes de déploiement //! //! ### Taille du binaire //! //! La webapp ajoutera ~150KB au binaire (compressé avec gzip par RustEmbed). //! //! ### Cache du navigateur //! //! Les assets sont servis avec des hashes dans les noms de fichiers //! (`index-BBZcSinC.js`) pour un cache busting automatique. //! //! ### Compatibilité navigateurs //! //! - Chrome/Edge : ✅ Moderne //! - Firefox : ✅ Moderne //! - Safari : ✅ iOS 13+ //! - IE11 : ❌ Non supporté (utilise ES modules) //! //! ## Voir aussi //! //! - [`pmoserver`] : Serveur HTTP Axum pour servir la webapp //! - [`pmoupnp`] : Bibliothèque UPnP MediaRenderer //! - [Vue.js Documentation](https://vuejs.org/) //! - [Vite Documentation](https://vitejs.dev/) use rust_embed::RustEmbed; /// Structure représentant l'application web embarquée. /// /// Cette structure utilise `RustEmbed` pour inclure tous les fichiers /// du répertoire `webapp/dist` dans le binaire au moment de la compilation. /// /// ## Exemple /// /// ```rust,ignore /// use pmoapp::{Webapp, WebAppExt}; /// use pmoserver::ServerBuilder; /// /// # async fn example() { /// let mut server = ServerBuilder::new("MyApp", "http://localhost", 8080).build(); /// /// // Ajouter la webapp via le trait WebAppExt /// server.add_webapp::("/app").await; /// # } /// ``` #[derive(RustEmbed, Clone)] #[folder = "webapp/dist"] pub struct Webapp; /// Trait pour étendre un serveur HTTP avec des fonctionnalités webapp. /// /// Ce trait permet à `pmoapp` d'ajouter des méthodes d'extension sur des types /// de serveurs externes (comme `pmoserver::Server`) sans que ces crates dépendent de `pmoapp`. /// /// # Architecture /// /// Similaire au pattern utilisé par `pmoupnp` pour `UpnpServer`, ce trait permet /// une extension propre et découplée : /// /// - `pmoserver` définit un serveur HTTP générique /// - `pmoapp` étend ce serveur avec des méthodes webapp via ce trait /// - Le serveur n'a pas besoin de connaître `pmoapp` /// /// # Exemple d'implémentation /// /// ```ignore /// impl WebAppExt for pmoserver::Server { /// fn add_webapp(&mut self, path: &str) -> ... { /// // Délègue à la méthode interne add_spa /// self.add_spa::(path) /// } /// } /// ``` pub trait WebAppExt { /// Ajoute une Single Page Application au serveur. /// /// # Arguments /// /// * `path` - Le chemin où monter la webapp (ex: "/app") /// /// # Type Parameter /// /// * `W` - Type RustEmbed contenant les fichiers de la webapp async fn add_webapp(&mut self, path: &str) where W: RustEmbed + Clone + Send + Sync + 'static; /// Ajoute une webapp avec une redirection automatique depuis la racine. /// /// # Arguments /// /// * `path` - Le chemin où monter la webapp (ex: "/app") /// /// # Type Parameter /// /// * `W` - Type RustEmbed contenant les fichiers de la webapp async fn add_webapp_with_redirect(&mut self, path: &str) where W: RustEmbed + Clone + Send + Sync + 'static; } // Implémentation du trait pour pmoserver::Server (feature-gated) #[cfg(feature = "pmoserver")] mod pmoserver_impl;