# Évaluation du plan : centraliser_base_url_axum_middleware
## Résumé de l'audit
Le plan est **bien pensé et cohérent**. Il identifie correctement le problème et la solution. Cependant, j'ai identifié plusieurs points nécessitant des amendements.
---
## Points validés (conformes au code actuel)
1.**Problème bien identifié** : URLs hardcodées avec IP locale (`PMO_SERVER_URL`) retournées au frontend via reverse proxy.
2.**`get_request_base_url` existe déjà** à `pmoserver/src/lib.rs:199` — pas besoin de la recréer.
3.**`covers_route_for` existe déjà** dans `pmocache/src/lib.rs:149`.
4.**`covers_absolute_url_for` utilisée dans les contextes UPnP** :
6.**Architecture du Server** : Les routes sont construites dynamiquement via `Arc<RwLock<Router>>`. Le layer devra être ajouté dans la construction du router, pas après.
---
## Points à amender
### 1. Ajout du layer dans le Server
Le plan suggère d'ajouter le layer "dans `server.rs`" mais la structure du router est complexe :
- Les routes sont dynamiques (`RwLock<Router>`)
- Le router final est un fallback qui délègue
**Correction** : Ajouter le layer directement lors de la création du `registry_route` initial (ligne 120-122) :
```rust
let registry_route = Router::new()
.route("/api/registry", get(get_api_registry))
.with_state(api_registry.clone())
.layer(base_url_layer()); // ← ici
```
### 2. Comportement requis pour LAN vs WAN
Le middleware doit supporter les deux cas d'usage :
- **LAN (sans reverse proxy)** : Pas de headers `X-Forwarded-*` → utiliser l'adresse IP locale du serveur (`PMO_SERVER_URL`)
**Important** : `get_request_base_url` dans `pmoserver/src/lib.rs:199` lit déjà ces headers. Le fallback doit être `PMO_SERVER_URL` qui est configuré au démarrage avec l'IP locale.
### 3. Chemin du middleware dans la pile
Le plan dit d'appliquer le layer "avant" les autres. En réalité, Tower/Acorn applique les couches dans l'ordre où elles sont ajoutées — le premier layer ajouté est le plus extérieur (exécuté en premier). Le `base_url_layer` doit donc être ajouté en **premier** (le plus intérieur) pour voir les headers nettoyés.
### 3. Les handlers n'ont PAS besoin de BaseUrl
Après analyse, **aucun handler** dans le codebase actuel n'appelle `covers_absolute_url_for()` directement pour le frontend. Les `album_art_uri` sont :
- Soit **propagés** depuis les réponses UPnP des media servers (pas des URLs pmomusic)
- Soit **construits en tâche de fond** dans les caches (RadioFrance, RadioParadise)
**Correction** : Le plan surestime le nombre de handlers à modifier. La vraie question est : d'où viennent les URLs incorrectes ?
### 4. Source du problème à clarifier
Les URLs incorrectes ne viennent pas des handlers REST classiques. Elles viennent probablement de :
**a) Tâches de fond** (background tasks) qui stockent des URLs complètes :
-`pmoradiofrance/src/metadata_cache.rs:263` — construit `covers_absolute_url_for()` dans le cache
-`pmoparadise/src/source.rs:216` — même problème
**b) API Qobuz** (`pmoqobuz/src/api_rest.rs:302`) — utilise `covers_route_for` (route relative, OK)
Le plan suggère `localhost:8080` ou `0.0.0.0:8080` comme fallback. Le port doit provenir de la configuration du serveur (`get_server_base_url()` existe déjà dans `pmoserver/src/lib.rs`).
**Correction** : Le fallback utilise `get_server_base_url()` (disponible via `GLOBAL_SERVER`) :
- En LAN : pas de `X-Forwarded-*` → `get_server_base_url()` → URLs en IP locale
- En WAN : `X-Forwarded-*` présents → URLs en URL publique du reverse proxy
### 6. Fonction `audio_route_for` pas nécessaire maintenant
Le plan propose d'ajouter `audio_route_for` dans `pmoaudiocache`. Mais :
- Les fichiers audio sont servis par `pmoaudiocache` lui-même (routes internes)
-Aucune URL audio n'est retournée au frontend via JSON
**Supprimer** cette étape du plan.
---
## Plan amendé
### Étape 0 — Audit spécifique (à faire avant implémentation)
```bash
# Trouver les constructions d'URLs dans les tâches de fond (caches, sources)
1.**Fallback avec panic** : Si ni les headers ni le serveur ne sont disponibles, le middleware panic au démarrage avec un message clair (ex: "BaseUrl: configurer PMO_SERVER_URL ou démarrer le serveur avant les handlers HTTP").
→ **Décision utilisateur** : OK, panic avec message clair.
2.**Reverse proxy avec Authelia** : NPM ajoutera les headers `X-Forwarded-*`. Authelia gère l'authentification separately. Pas de vérification de header supplémentaire nécessaire pour le middleware BaseUrl.
→ **Décision** : Pas de vérification supplémentaire.
## Problème complémentaire : URLs de covers des media servers externes
### Contexte
Quand le control point accède à un media server externe sur le LAN (autre que pmomusic), les URLs d'articles (`album_art_uri`) retournées par ce media server externe contiennent des IPs locales du LAN externe (ex: `http://192.168.1.100:8080/covers/...`).
Ces URLs ne passent pas par notre système de caching et ne peuvent pas être rewritées par le middleware `BaseUrl` car elles sont :
1. Recues depuis le réseau UPnP (pas via HTTP)
2. Propagées directement dans les réponses REST/SSE sans transformation
### Solution proposée : Proxy de covers avec cache
Créer un nouveau endpoint HTTP qui agit comme un proxy transparent :
1.**Détection** : Si l'URL demandée est une URL LAN externe (pas une URL locale de pmomusic)
2.**Caching** : Utiliser `cache.add_from_url()` qui gère déjà la déduplication (pas de double-cache)
3.**Rewriting** : Retourner l'URL locale du cache (`/covers/image/{pk}`)
**Note importante** : `pmocache::add_from_url()` gère déjà :
- La vérification si l'URL est déjà en cache (ligne 673-683)
- Le calcul du pk basé sur le contenu (pas sur l'URL)
- La déduplication automatique pour les mêmes contenus
### Implémentation
**Nouvel endpoint dans `pmocovers/src/lib.rs` ou nouveau fichier `pmocovers/src/proxy.rs`** :
```rust
#[derive(Debug, Deserialize)]
struct CoverProxyParams {
url: String,
}
#[derive(Debug, Serialize)]
struct CoverProxyResponse {
cached_url: String,
pk: String,
}
/// GET /covers/proxy?url=<encoded_url>
/// Proxy transparent qui :
/// 1. Détecte si l'URL est une URL LAN externe (pas déjà locale)
/// 2. Ajoute à cache via add_from_url (déduplication automatique)
3.**Modifier** : `pmocontrol/src/sse.rs` - Transformer les album_art_uri dans les événements
---
## Plan: Passer le SSE en mode Async
### Contexte
Le SSE de PMO Control est **déjà async** (fonctions `pub async fn`), mais le traitement des événements utilise des fonctions **synchrones** (`fn renderer_event_to_payload` → `transform_cover_url_sync`). Cela nécessite des workarounds (threads avec runtime tokio séparés).
### Problèmes actuels
1.**Nested runtime**: `std::thread::spawn` avec `tokio::runtime::Runtime::new()` dans chaque appel
2.**Performance dégradée**: Création d'un thread par URL de cover
3.**Code complexe**: Workarounds pour exécuter de l'async dans du sync
### Solution
Rendre le traitement des événements **entièrement async** :