# Session 2025-12-31 : Correction Docker - Servir les images ## 🎯 Problème Les images uploadées dans le module périphériques n'étaient pas accessibles depuis le frontend. **Erreurs** : ``` GET http://10.0.0.50:8087/app/uploads/peripherals/photos/3/csfingerprint_20251231_092242.webp [HTTP/1.1 404 Not Found] ``` ## 🔍 Analyse ### Problème 1 : Montage de volume impossible Tentative initiale de monter `./uploads` vers `/usr/share/nginx/html/app/uploads` dans le conteneur nginx. **Erreur Docker** : ``` error mounting "/home/gilles/projects/serv_benchmark/uploads" to rootfs at "/usr/share/nginx/html/app/uploads": mkdirat /var/lib/docker/rootfs/overlayfs/.../usr/share/nginx/html/app: read-only file system ``` **Cause** : Le système de fichiers root du conteneur nginx:alpine est en lecture seule. Docker ne peut pas créer le répertoire intermédiaire `/app/` dans `/usr/share/nginx/html/`. ### Problème 2 : Chemin filesystem vs chemin web - **Base de données** : Stocke les chemins filesystem du backend : `/app/uploads/peripherals/photos/3/image.webp` - **Frontend** : A besoin de chemins web accessibles via nginx : `/uploads/peripherals/photos/3/image.webp` ## ✅ Solutions implémentées ### 1. Montage simplifié des uploads **Fichier** : `docker-compose.yml` (ligne 37) ```yaml volumes: - ./uploads:/uploads:ro ``` ✅ Montage direct vers `/uploads` (pas de répertoire intermédiaire à créer) ### 2. Configuration nginx personnalisée **Fichier créé** : `frontend/nginx.conf` ```nginx server { listen 80; server_name localhost; # Serve static frontend files location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ =404; } # Serve uploaded files location /uploads/ { alias /uploads/; autoindex off; # Cache uploaded images for 1 day expires 1d; add_header Cache-Control "public, immutable"; } # Security headers add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; } ``` **Fonctionnalités** : - ✅ Location `/uploads/` sert les fichiers depuis `/uploads/` dans le conteneur - ✅ Cache navigateur 1 jour pour les images (performance) - ✅ En-têtes de sécurité (XSS, Clickjacking, MIME sniffing) **Montage dans Docker** : `docker-compose.yml` (ligne 35) ```yaml volumes: - ./frontend/nginx.conf:/etc/nginx/conf.d/default.conf:ro ``` ### 3. Conversion des chemins dans l'API backend **Fichier** : `backend/app/api/endpoints/peripherals.py` #### Endpoint `/peripherals/{id}/photos` (lignes 329-355) ```python @router.get("/{peripheral_id}/photos") def get_photos( peripheral_id: int, db: Session = Depends(get_peripherals_db) ): """Get all photos for a peripheral""" photos = db.query(PeripheralPhoto).filter( PeripheralPhoto.peripheral_id == peripheral_id ).all() # Convert stored paths to web-accessible URLs result = [] for photo in photos: photo_dict = { "id": photo.id, "peripheral_id": photo.peripheral_id, "filename": photo.filename, "stored_path": photo.stored_path.replace('/app/uploads/', '/uploads/') if photo.stored_path.startswith('/app/uploads/') else photo.stored_path, "mime_type": photo.mime_type, "size_bytes": photo.size_bytes, "description": photo.description, "is_primary": photo.is_primary, "uploaded_at": photo.uploaded_at } result.append(photo_dict) return result ``` **Transformation** : - Base de données : `/app/uploads/peripherals/photos/3/image.webp` - API retourne : `/uploads/peripherals/photos/3/image.webp` #### Endpoint `/peripherals/{id}/documents` (lignes 425-450) Même transformation pour les documents : ```python stored_path": doc.stored_path.replace('/app/uploads/', '/uploads/') if doc.stored_path.startswith('/app/uploads/') else doc.stored_path ``` ### 4. Configuration frontend **Fichier** : `frontend/config.js` (lignes 29-31) ```javascript if (!window.BenchConfig.uploadsPath) { window.BenchConfig.uploadsPath = '/uploads'; } ``` Permet de centraliser la configuration du chemin des uploads si besoin de le modifier. ## 📊 Flux complet ``` 1. Upload photo └─> Backend stocke : /app/uploads/peripherals/photos/3/image.webp (filesystem) 2. Frontend demande : GET /api/peripherals/3/photos └─> Backend convertit : /app/uploads/... → /uploads/... └─> API retourne : /uploads/peripherals/photos/3/image.webp 3. Frontend affiche : └─> Nginx sert depuis : /uploads/ (monté depuis ./uploads) └─> HTTP 200 OK ``` ## 🧪 Tests de validation ### Test 1 : Fichier existe dans le conteneur ```bash docker exec linux_benchtools_frontend ls -la /uploads/peripherals/photos/3/ ``` **Résultat** : ``` -rwxrwxrwx 1 root root 20084 Dec 31 09:22 csfingerprint_20251231_092242.webp ``` ✅ OK ### Test 2 : API retourne le bon chemin ```bash curl http://10.0.0.50:8007/api/peripherals/3/photos ``` **Résultat** : ```json { "stored_path": "/uploads/peripherals/photos/3/csfingerprint_20251231_092242.webp" } ``` ✅ OK ### Test 3 : Nginx sert l'image ```bash curl -I http://10.0.0.50:8087/uploads/peripherals/photos/3/csfingerprint_20251231_092242.webp ``` **Résultat** : ``` HTTP/1.1 200 OK Content-Type: image/webp Content-Length: 20084 Cache-Control: max-age=86400 Cache-Control: public, immutable ``` ✅ OK ### Test 4 : Frontend accessible ```bash curl -I http://10.0.0.50:8087/peripherals.html ``` **Résultat** : ``` HTTP/1.1 200 OK Content-Type: text/html X-Frame-Options: SAMEORIGIN X-Content-Type-Options: nosniff X-XSS-Protection: 1; mode=block ``` ✅ OK ## 📁 Fichiers modifiés ### Créés - ✅ `frontend/nginx.conf` - Configuration nginx personnalisée - ✅ `docs/SESSION_2025-12-31_DOCKER_IMAGES_FIX.md` - Cette documentation ### Modifiés - ✅ `docker-compose.yml` - Montage `/uploads` et config nginx - ✅ `frontend/config.js` - Ajout `uploadsPath` - ✅ `backend/app/api/endpoints/peripherals.py` - Conversion chemins dans API - ✅ `backend/app/schemas/peripheral.py` - Suppression tentative @property (non retenue) ## 🔄 Commandes de déploiement ```bash # Rebuild backend avec nouvelles routes API docker compose up -d --build backend # Recréer frontend avec nginx.conf docker compose up -d frontend # Vérifier tous les conteneurs docker compose ps ``` ## 🎯 Améliorations futures possibles - [ ] Ajouter compression gzip pour les images dans nginx - [ ] Implémenter un CDN ou proxy cache pour les uploads - [ ] Ajouter authentification pour certains uploads sensibles - [ ] Lazy loading des images dans le frontend - [ ] WebP avec fallback JPEG pour compatibilité navigateurs anciens --- **Date** : 31 décembre 2025 **Statut** : ✅ Résolu et testé **Impact** : Les images des périphériques sont maintenant accessibles depuis le frontend