# Système de Gestion des Fichiers - API Geosector

## Vue d'ensemble

Ce document décrit l'organisation et la gestion des fichiers uploadés dans l'API Geosector. Le système permet de stocker et organiser différents types de fichiers par entité, utilisateur, opération et passage.

## Structure des Dossiers

```
uploads/
├── entites/
│   ├── {entite_id}/
│   │   ├── documents/          # PDF, Excel généraux de l'entité
│   │   ├── images/            # Images de l'entité
│   │   ├── users/             # Dossier pour les fichiers des utilisateurs
│   │   │   └── {user_id}/     # Images par utilisateur (avatars, etc.)
│   │   └── operations/        # Dossier pour les opérations
│   │       └── {operation_id}/
│   │           ├── documents/ # Fichiers Excel de l'opération
│   │           └── passages/  # Fichiers des passages de cette opération
│   │               └── {passage_id}/ # PDF et images par passage
│   └── temp/                  # Fichiers temporaires avant validation
```

### Exemples de chemins

- Document d'entité : `uploads/entites/5/documents/reglement_2024.pdf`
- Avatar utilisateur : `uploads/entites/5/users/123/avatar.jpg`
- Excel d'opération : `uploads/entites/5/operations/2644/documents/planning.xlsx`
- Photo de passage : `uploads/entites/5/operations/2644/passages/789/photo_1.jpg`

## Structure de la Table `medias`

### Table existante enrichie

```sql
-- Structure complète de la table medias
CREATE TABLE `medias` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `support` varchar(45) NOT NULL DEFAULT '' COMMENT 'Type de support (entite, user, operation, passage)',
  `support_id` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'ID de l\'élément associé',
  `fichier` varchar(250) NOT NULL DEFAULT '' COMMENT 'Nom du fichier stocké',
  `file_type` varchar(50) DEFAULT NULL COMMENT 'Extension du fichier (pdf, jpg, xlsx, etc.)',
  `file_size` int(10) unsigned DEFAULT NULL COMMENT 'Taille du fichier en octets',
  `mime_type` varchar(100) DEFAULT NULL COMMENT 'Type MIME du fichier',
  `original_name` varchar(255) DEFAULT NULL COMMENT 'Nom original du fichier uploadé',
  `fk_entite` int(10) unsigned DEFAULT NULL COMMENT 'ID de l\'entité propriétaire',
  `fk_operation` int(10) unsigned DEFAULT NULL COMMENT 'ID de l\'opération (pour passages)',
  `file_path` varchar(500) DEFAULT NULL COMMENT 'Chemin complet du fichier',
  `original_width` int(10) unsigned DEFAULT NULL COMMENT 'Largeur originale de l\'image',
  `original_height` int(10) unsigned DEFAULT NULL COMMENT 'Hauteur originale de l\'image',
  `processed_width` int(10) unsigned DEFAULT NULL COMMENT 'Largeur après traitement',
  `processed_height` int(10) unsigned DEFAULT NULL COMMENT 'Hauteur après traitement',
  `is_processed` tinyint(1) unsigned DEFAULT 0 COMMENT 'Image redimensionnée (1) ou originale (0)',
  `description` varchar(100) NOT NULL DEFAULT '' COMMENT 'Description du fichier',
  `created_at` timestamp NOT NULL DEFAULT current_timestamp(),
  `fk_user_creat` int(10) unsigned NOT NULL DEFAULT 0,
  `updated_at` timestamp NULL DEFAULT NULL ON UPDATE current_timestamp(),
  `fk_user_modif` int(10) unsigned NOT NULL DEFAULT 0,
  PRIMARY KEY (`id`),
  UNIQUE KEY `id_UNIQUE` (`id`),
  KEY `idx_entite` (`fk_entite`),
  KEY `idx_operation` (`fk_operation`),
  KEY `idx_support_type` (`support`, `support_id`),
  KEY `idx_file_type` (`file_type`),
  CONSTRAINT `fk_medias_entite` FOREIGN KEY (`fk_entite`) REFERENCES `entites` (`id`) ON UPDATE CASCADE ON DELETE CASCADE,
  CONSTRAINT `fk_medias_operation` FOREIGN KEY (`fk_operation`) REFERENCES `operations` (`id`) ON UPDATE CASCADE ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
```

### Migration SQL pour table existante

```sql
-- Ajout des nouvelles colonnes à la table existante
ALTER TABLE `medias`
ADD COLUMN `file_type` varchar(50) DEFAULT NULL COMMENT 'Extension du fichier (pdf, jpg, xlsx, etc.)' AFTER `fichier`,
ADD COLUMN `file_size` int(10) unsigned DEFAULT NULL COMMENT 'Taille du fichier en octets' AFTER `file_type`,
ADD COLUMN `mime_type` varchar(100) DEFAULT NULL COMMENT 'Type MIME du fichier' AFTER `file_size`,
ADD COLUMN `original_name` varchar(255) DEFAULT NULL COMMENT 'Nom original du fichier uploadé' AFTER `mime_type`,
ADD COLUMN `fk_entite` int(10) unsigned DEFAULT NULL COMMENT 'ID de l\'entité propriétaire' AFTER `support_id`,
ADD COLUMN `fk_operation` int(10) unsigned DEFAULT NULL COMMENT 'ID de l\'opération (pour passages)' AFTER `fk_entite`,
ADD COLUMN `file_path` varchar(500) DEFAULT NULL COMMENT 'Chemin complet du fichier' AFTER `original_name`,
ADD COLUMN `original_width` int(10) unsigned DEFAULT NULL COMMENT 'Largeur originale de l\'image' AFTER `file_path`,
ADD COLUMN `original_height` int(10) unsigned DEFAULT NULL COMMENT 'Hauteur originale de l\'image' AFTER `original_width`,
ADD COLUMN `processed_width` int(10) unsigned DEFAULT NULL COMMENT 'Largeur après traitement' AFTER `original_height`,
ADD COLUMN `processed_height` int(10) unsigned DEFAULT NULL COMMENT 'Hauteur après traitement' AFTER `processed_width`,
ADD COLUMN `is_processed` tinyint(1) unsigned DEFAULT 0 COMMENT 'Image redimensionnée (1) ou originale (0)' AFTER `processed_height`;

-- Ajout des index pour optimiser les requêtes
ALTER TABLE `medias`
ADD INDEX `idx_entite` (`fk_entite`),
ADD INDEX `idx_operation` (`fk_operation`),
ADD INDEX `idx_support_type` (`support`, `support_id`),
ADD INDEX `idx_file_type` (`file_type`);

-- Ajout des contraintes de clés étrangères
ALTER TABLE `medias`
ADD CONSTRAINT `fk_medias_entite` FOREIGN KEY (`fk_entite`) REFERENCES `entites` (`id`) ON UPDATE CASCADE ON DELETE CASCADE,
ADD CONSTRAINT `fk_medias_operation` FOREIGN KEY (`fk_operation`) REFERENCES `operations` (`id`) ON UPDATE CASCADE ON DELETE CASCADE;
```

## Types de Support

### 1. Entité (`support = 'entite'`)

- **Fichiers autorisés** : PDF, Excel, Images (JPG, PNG)
- **Taille max** : 20 MB
- **Usage** : Documents généraux de l'entité (règlements, statuts, etc.)
- **Chemin** : `uploads/entites/{entite_id}/documents/`

### 2. Utilisateur (`support = 'user'`)

- **Fichiers autorisés** : Images uniquement (JPG, PNG, GIF, WebP)
- **Taille max** : 5 MB
- **Usage** : Avatars, photos de profil
- **Chemin** : `uploads/entites/{entite_id}/users/{user_id}/`
- **Traitement** : Redimensionnement automatique

### 3. Opération (`support = 'operation'`)

- **Fichiers autorisés** : Excel uniquement (XLS, XLSX)
- **Taille max** : 20 MB
- **Usage** : Plannings, listes, données d'opération
- **Chemin** : `uploads/entites/{entite_id}/operations/{operation_id}/documents/`

### 4. Passage (`support = 'passage'`)

- **Fichiers autorisés** : PDF et Images (JPG, PNG, PDF)
- **Taille max** : 10 MB par fichier
- **Usage** : Reçus, photos de passage, justificatifs
- **Chemin** : `uploads/entites/{entite_id}/operations/{operation_id}/passages/{passage_id}/`
- **Traitement** : Redimensionnement automatique pour les images

## Traitement Automatique des Images

### Règles de redimensionnement

- **Dimension maximale** : 250px (hauteur ou largeur, selon la plus grande)
- **Résolution** : 72 DPI (optimisé web)
- **Préservation du ratio** : Redimensionnement proportionnel
- **Formats supportés** : JPG, PNG, GIF, WebP
- **Qualité JPEG** : 85% (bon compromis qualité/poids)

### Exemples de transformation

```
Image originale 1000x800px → Image traitée 250x200px
Image originale 600x1200px → Image traitée 125x250px
Image originale 200x150px → Pas de redimensionnement (déjà < 250px)
```

### Workflow de traitement

1. **Upload** → Validation du type MIME
2. **Analyse** → Détection des dimensions originales
3. **Traitement** → Redimensionnement si nécessaire
4. **Optimisation** → Compression et résolution web
5. **Sauvegarde** → Image optimisée + métadonnées
6. **Nettoyage** → Suppression du fichier temporaire

## API Endpoints

### Routes de gestion des fichiers

```php
// Upload de fichiers
POST /api/medias/upload
Content-Type: multipart/form-data
Body: {
  "file": [fichier],
  "support": "entite|user|operation|passage",
  "support_id": 123,
  "description": "Description du fichier"
}

// Récupération d'un fichier
GET /api/medias/{id}

// Liste des fichiers par support
GET /api/medias/list/{support}/{support_id}

// Suppression d'un fichier
DELETE /api/medias/{id}
```

### Exemples de requêtes

#### Upload d'un avatar utilisateur

```bash
curl -X POST "https://api.geosector.fr/medias/upload" \
  -H "Authorization: Bearer {token}" \
  -F "file=@avatar.jpg" \
  -F "support=user" \
  -F "support_id=123" \
  -F "description=Avatar utilisateur"
```

#### Upload d'une photo de passage

```bash
curl -X POST "https://api.geosector.fr/medias/upload" \
  -H "Authorization: Bearer {token}" \
  -F "file=@photo_passage.jpg" \
  -F "support=passage" \
  -F "support_id=789" \
  -F "description=Photo du passage"
```

## Sécurité et Contrôles

### Validation des fichiers

- **Types MIME** : Vérification stricte du type de fichier
- **Extensions** : Validation de l'extension par rapport au contenu
- **Taille** : Limite selon le type de support
- **Contenu** : Scan antivirus recommandé en production

### Contrôles d'accès

- **Authentification** : Token JWT requis
- **Autorisation** : Utilisateur ne peut accéder qu'aux fichiers de son entité
- **Vérification** : Contrôle que l'utilisateur appartient à l'entité du fichier
- **Logs** : Traçabilité complète des uploads et accès

### Nommage des fichiers

```php
// Format : {timestamp}_{random}_{sanitized_name}.{extension}
// Exemple : 1640995200_a1b2c3_document_reglement.pdf
```

## Gestion des Erreurs

### Codes d'erreur HTTP

- **400** : Fichier invalide ou paramètres manquants
- **401** : Non authentifié
- **403** : Accès refusé à cette entité
- **413** : Fichier trop volumineux
- **415** : Type de fichier non supporté
- **500** : Erreur serveur lors du traitement

### Messages d'erreur

```json
{
  "status": "error",
  "message": "Type de fichier non autorisé pour ce support",
  "code": "INVALID_FILE_TYPE",
  "allowed_types": ["jpg", "png", "gif", "webp"]
}
```

## Maintenance et Nettoyage

### Nettoyage automatique

- **Fichiers temporaires** : Suppression après 24h
- **Fichiers orphelins** : Détection et suppression des fichiers sans référence en base
- **Anciennes opérations** : Suppression en cascade lors de la suppression d'une opération

### Commandes de maintenance

```bash
# Nettoyage des fichiers temporaires
php scripts/cleanup_temp_files.php

# Détection des fichiers orphelins
php scripts/find_orphan_files.php

# Statistiques d'utilisation
php scripts/storage_stats.php
```

## Performances et Optimisation

### Optimisations

- **CDN** : Recommandé pour la distribution des fichiers
- **Cache** : Headers de cache appropriés pour les fichiers statiques
- **Compression** : Gzip pour les réponses API
- **Index** : Index optimisés sur la table medias

### Monitoring

- **Espace disque** : Surveillance de l'utilisation
- **Performance** : Temps de traitement des images
- **Erreurs** : Logs des échecs d'upload et de traitement

## Exemples d'Utilisation

### Cas d'usage typiques

1. **Upload d'avatar utilisateur**

   - Fichier JPG de 2MB
   - Redimensionnement automatique à 250x250px
   - Stockage dans `uploads/entites/5/users/123/`

2. **Document d'opération**

   - Fichier Excel de planning
   - Stockage dans `uploads/entites/5/operations/2644/documents/`
   - Pas de traitement (fichier conservé tel quel)

3. **Photo de passage**
   - Photo JPG de 8MB prise sur mobile
   - Redimensionnement automatique à 250px max
   - Stockage dans `uploads/entites/5/operations/2644/passages/789/`

### Intégration frontend

```javascript
// Upload avec progress
const uploadFile = async (file, support, supportId, description) => {
  const formData = new FormData();
  formData.append('file', file);
  formData.append('support', support);
  formData.append('support_id', supportId);
  formData.append('description', description);

  const response = await fetch('/api/medias/upload', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${token}`,
    },
    body: formData,
  });

  return response.json();
};
```

---

**Version** : 1.0  
**Date** : Juin 2025  
**Auteur** : API Geosector Team
