Files
serv_benchmark/docs/FEATURE_FILE_ORGANIZATION.md
2026-01-11 23:41:30 +01:00

9.0 KiB

📁 Organisation des fichiers par hostname

Vue d'ensemble

Le système d'upload a été amélioré pour organiser automatiquement les fichiers et images par hostname de device dans des sous-dossiers structurés.

Structure précédente

uploads/
├── 3562b30f85326e79_3.jpg
├── 7660e368d0cb566e_4.png
├── 8b5371f003d8616f_3.png
├── ec199bc98be16a37_3.pdf
└── peripherals/

Nouvelle structure

uploads/
├── srv-proxmox/
│   ├── images/
│   │   ├── 3562b30f85326e79_3.jpg
│   │   └── 7660e368d0cb566e_4.png
│   └── files/
│       └── ec199bc98be16a37_3.pdf
├── rpi4-cluster-01/
│   ├── images/
│   │   └── a1b2c3d4e5f67890_1.jpg
│   └── files/
│       └── datasheet_5.pdf
└── peripherals/
    └── (unchanged)

Avantages

  1. Organisation claire : Les fichiers sont regroupés par device
  2. Séparation images/fichiers : Facilite la gestion et les sauvegardes
  3. Scalabilité : Fonctionne avec des milliers de devices
  4. Navigation facile : Accès direct aux fichiers d'un device
  5. Nettoyage simplifié : Suppression d'un device = suppression d'un dossier

Fonctionnement

Détection automatique

Le système détecte automatiquement si un fichier est une image :

Extensions d'images :

  • .jpg, .jpeg
  • .png
  • .gif
  • .webp
  • .bmp
  • .svg

Type MIME :

  • Tout MIME type commençant par image/

Sanitisation des noms

Les hostnames sont nettoyés pour être utilisables comme noms de dossiers :

# Exemples de sanitisation
"srv-proxmox.local"  "srv-proxmox.local"
"my server (old)"  "my_server_old"
"test@2024"  "test_2024"
"___test___"  "test"

Règles :

  • Caractères interdits remplacés par _
  • Points et tirets conservés
  • Underscores multiples condensés
  • Longueur limitée à 100 caractères
  • Fallback sur "unknown" si vide

Migration des fichiers existants

Un script de migration est fourni pour réorganiser les fichiers existants.

Dry-run (simulation)

Pour voir ce qui serait fait sans modifier les fichiers :

python backend/migrate_file_organization.py

Sortie exemple :

================================================================================
File Organization Migration
================================================================================

Found 15 documents to migrate
Mode: DRY RUN
--------------------------------------------------------------------------------
📄 Document 1 (image):
   Device: srv-proxmox (ID: 3)
   From: ./uploads/3562b30f85326e79_3.jpg
   To:   ./uploads/srv-proxmox/images/3562b30f85326e79_3.jpg
   [DRY RUN - would migrate]

📄 Document 5 (file):
   Device: srv-proxmox (ID: 3)
   From: ./uploads/ec199bc98be16a37_3.pdf
   To:   ./uploads/srv-proxmox/files/ec199bc98be16a37_3.pdf
   [DRY RUN - would migrate]

...

Summary:
  Migrated: 12
  Skipped: 2
  Errors: 1
  Total: 15

This was a DRY RUN. To actually migrate files, run:
  python backend/migrate_file_organization.py --execute

Migration réelle

Pour effectuer réellement la migration :

python backend/migrate_file_organization.py --execute

Avec nettoyage

Pour migrer ET supprimer les dossiers vides :

python backend/migrate_file_organization.py --execute --cleanup

Utilisation de l'API

Upload d'un document

L'API détecte automatiquement le type et place le fichier au bon endroit :

# Upload d'une image
curl -X POST "http://localhost:8007/api/devices/3/docs" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -F "file=@photo.jpg" \
  -F "doc_type=photo"

# Sera stocké dans: uploads/srv-proxmox/images/hash_3.jpg
# Upload d'un PDF
curl -X POST "http://localhost:8007/api/devices/3/docs" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -F "file=@manual.pdf" \
  -F "doc_type=manual"

# Sera stocké dans: uploads/srv-proxmox/files/hash_3.pdf

Téléchargement

Le téléchargement utilise toujours le même endpoint :

curl "http://localhost:8007/api/docs/123/download" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -o document.pdf

Le système lit le stored_path en base de données qui contient le chemin complet.

Architecture technique

Module file_organizer.py

from app.utils.file_organizer import (
    sanitize_hostname,
    get_device_upload_paths,
    ensure_device_directories,
    get_upload_path,
    is_image_file
)

Fonctions principales :

sanitize_hostname(hostname: str) -> str

Nettoie un hostname pour utilisation comme nom de dossier.

get_device_upload_paths(base_dir: str, hostname: str) -> Tuple[str, str]

Retourne les chemins (images, files) pour un device.

ensure_device_directories(base_dir: str, hostname: str) -> Tuple[str, str]

Crée les dossiers s'ils n'existent pas et retourne les chemins.

get_upload_path(base_dir: str, hostname: str, is_image: bool, filename: str) -> str

Retourne le chemin complet où stocker un fichier.

is_image_file(filename: str, mime_type: str = None) -> bool

Détermine si un fichier est une image.

Modification de docs.py

Avant :

stored_path = os.path.join(settings.UPLOAD_DIR, stored_filename)
os.makedirs(settings.UPLOAD_DIR, exist_ok=True)

Après :

is_image = is_image_file(file.filename, file.content_type)
stored_path = get_upload_path(
    settings.UPLOAD_DIR,
    device.hostname,
    is_image,
    stored_filename
)

Compatibilité

Anciens fichiers

Les fichiers existants continuent de fonctionner grâce au stored_path en base de données :

  • Les anciens chemins (uploads/hash_id.ext) restent valides
  • Les nouveaux uploads utilisent la nouvelle structure
  • La migration est optionnelle mais recommandée

Téléchargement

L'API de téléchargement utilise le stored_path de la base de données, donc :

  • Anciens fichiers : fonctionnent
  • Nouveaux fichiers : fonctionnent
  • Fichiers migrés : fonctionnent

Cas d'usage

Sauvegarde sélective

# Sauvegarder seulement les images d'un device
rsync -av uploads/srv-proxmox/images/ backup/srv-proxmox-images/

# Sauvegarder tous les PDF
find uploads/*/files -name "*.pdf" -exec cp {} backup/pdfs/ \;

Nettoyage par device

# Supprimer tous les fichiers d'un device désinstallé
rm -rf uploads/old-server/

Audit de l'espace

# Voir l'espace utilisé par device
du -sh uploads/*/

# Sortie :
# 45M    uploads/srv-proxmox/
# 120M   uploads/rpi4-cluster-01/
# 2.3M   uploads/laptop-dev/

Migration progressive

Vous pouvez migrer progressivement :

  1. Phase 1 : Déployer le nouveau code

    • Nouveaux uploads utilisent la nouvelle structure
    • Anciens fichiers restent en place
  2. Phase 2 : Tester la migration

    • Faire un dry-run
    • Vérifier les chemins générés
  3. Phase 3 : Migrer en production

    • Exécuter la migration réelle
    • Vérifier que les téléchargements fonctionnent
  4. Phase 4 : Nettoyage

    • Nettoyer les dossiers vides
    • Archiver les anciens fichiers si nécessaire

Sécurité

Validation

  • Les noms de fichiers sont hashés (pas de conflit de noms)
  • Les hostnames sont sanitisés (pas d'injection de chemin)
  • Les tailles de fichiers sont vérifiées
  • Les extensions sont validées

Isolation

  • Chaque device a son propre dossier
  • Pas de risque de collision entre devices
  • Permissions préservées

Performance

Impact

  • Création de dossiers : négligeable (mkdir -p)
  • Upload : identique à avant
  • Download : identique à avant
  • Migration : proportionnel au nombre de fichiers

Optimisations

  • Les dossiers sont créés une seule fois
  • Pas de scans récursifs
  • Utilise les fonctions OS natives

Limitations

  1. Hostname changeant : Si un hostname change, les fichiers restent dans l'ancien dossier

    • Solution : Script de remapping si nécessaire
  2. Caractères spéciaux : Certains caractères sont remplacés par _

    • C'est intentionnel pour la compatibilité filesystem
  3. Périphériques : Le dossier peripherals/ garde sa propre structure

    • Pour éviter de casser le code existant

FAQ

Q: Que se passe-t-il si je ne migre pas les anciens fichiers ? R: Ils continuent de fonctionner normalement. Seuls les nouveaux uploads utilisent la nouvelle structure.

Q: Puis-je revenir en arrière ? R: Oui, en modifiant les stored_path en base de données et en déplaçant les fichiers.

Q: La migration supprime-t-elle les fichiers originaux ? R: Non, elle les déplace (move, pas copy). Les fichiers ne sont pas dupliqués.

Q: Que faire si un device a le même hostname qu'un autre ? R: Les fichiers iront dans le même dossier, mais les noms de fichiers incluent le device_id donc pas de collision.


Fichiers créés :

  • backend/app/utils/file_organizer.py - Module utilitaire
  • backend/migrate_file_organization.py - Script de migration

Fichiers modifiés :

  • backend/app/api/docs.py - Utilise la nouvelle organisation

Créé le : 2026-01-11