// src/services/storageService.js const fs = require('fs').promises; const path = require('path'); const { Buffer } = require('buffer'); const config = require('../config'); /** * Service de gestion du stockage des fichiers */ class StorageService { /** * Crée un dossier s'il n'existe pas déjà * @param {string} dirPath - Chemin du dossier à créer * @returns {Promise} Chemin du dossier créé */ static async createDirectory(dirPath) { try { await fs.access(dirPath); } catch (error) { if (error.code === 'ENOENT') { await fs.mkdir(dirPath, { recursive: true }); } else { throw error; } } return dirPath; } /** * Supprime un dossier et son contenu * @param {string} dirPath - Chemin du dossier à supprimer */ static async deleteDirectory(dirPath) { try { await fs.access(dirPath); await fs.rm(dirPath, { recursive: true, force: true }); } catch (error) { if (error.code !== 'ENOENT') { throw error; } } } /** * Cherche toutes les images dans un dossier * @param {string} dirPath - Dossier à scanner * @returns {Promise>} Liste des chemins d'images trouvées */ static async scanImages(dirPath = 'storage') { const basePath = path.join(config.paths.storage, dirPath); let results = []; try { await fs.access(basePath); } catch (error) { if (error.code === 'ENOENT') { await fs.mkdir(basePath, { recursive: true }); } else { throw error; } } async function scanDirectory(directory) { const files = await fs.readdir(directory); for (const file of files) { const filePath = path.join(directory, file); const stat = await fs.stat(filePath); if (stat.isDirectory()) { await scanDirectory(filePath); } else if (file.endsWith('.jpg')) { results.push(filePath); } } } await scanDirectory(basePath); return results; } /** * Enregistre un contenu dans un fichier * @param {string} filePath - Chemin du fichier * @param {Buffer} content - Contenu à enregistrer */ static async saveFile(filePath, content) { const dirPath = path.dirname(filePath); await this.createDirectory(dirPath); if (Buffer.isBuffer(content)) { await fs.writeFile(filePath, content); } else { throw new Error('Le contenu doit être un buffer'); } } /** * Récupère le contenu d'un fichier * @param {string} filePath - Chemin du fichier * @returns {Promise} Contenu du fichier */ static async getFile(filePath) { return await fs.readFile(filePath); } /** * Supprime un fichier * @param {string} filePath - Chemin du fichier à supprimer * @returns {Promise} Message de confirmation */ static async deleteFile(filePath) { try { await fs.access(filePath); await fs.rm(filePath); return `Fichier ${filePath} supprimé avec succès.`; } catch (error) { if (error.code === 'ENOENT') { return `Fichier ${filePath} inexistant.`; } else { throw error; } } } /** * Gestionnaire pour les opérations de projet */ static project = { /** * Crée le répertoire d'un projet * @param {number} projectId - ID du projet */ createProjectDirectory: async function(projectId) { const projectPath = path.join(config.paths.storage, `${projectId}`); await StorageService.createDirectory(projectPath); await StorageService.createDirectory(path.join(projectPath, 'images')); await StorageService.createDirectory(path.join(projectPath, 'videos')); console.log(`[STORAGE] Répertoire créé : ${projectPath}`); }, /** * Supprime le répertoire d'un projet et son contenu * @param {number} projectId - ID du projet */ deleteProjectDirectory: async function(projectId) { const projectPath = path.join(config.paths.storage, `${projectId}`); await StorageService.deleteDirectory(projectPath); console.log(`[STORAGE] Répertoire supprimé : ${projectPath}`); } }; /** * Gestionnaire pour les opérations de mesures (images) */ static measurement = { /** * Récupère l'image d'une mesure * @param {number} projectId - ID du projet * @param {number} orderId - ID d'ordre de la mesure * @returns {Promise} Contenu de l'image */ getMeasurementImage: async function(projectId, orderId) { const imagePath = path.join(config.paths.storage, `${projectId}`, 'images', `${orderId}.jpg`); console.log(`[STORAGE] Récupération de l'image : ${imagePath}`); return await StorageService.getFile(imagePath); }, /** * Enregistre l'image d'une mesure * @param {Object} image - Objet image avec buffer * @param {number} projectId - ID du projet * @param {number} orderId - ID d'ordre de la mesure * @returns {Promise} Chemin de l'image enregistrée */ uploadMeasurementImage: async function(image, projectId, orderId) { const imagePath = path.join(config.paths.storage, `${projectId}`, 'images', `${orderId}.jpg`); console.log(`[STORAGE] Enregistrement de l'image : ${imagePath}`); await StorageService.saveFile(imagePath, image.buffer); return imagePath; } }; /** * Gestionnaire pour les opérations de vidéos */ static video = { /** * Récupère une vidéo * @param {number} projectId - ID du projet * @param {number} videoId - ID de la vidéo * @returns {Promise} Contenu de la vidéo */ getVideo: async function(projectId, videoId) { const videoPath = path.join(config.paths.storage, `${projectId}`, 'videos', `${videoId}.mp4`); console.log(`[STORAGE] Récupération de la vidéo : ${videoPath}`); return await StorageService.getFile(videoPath); }, /** * Supprime une vidéo * @param {string} videoPath - Chemin de la vidéo à supprimer * @returns {Promise} Message de confirmation */ deleteVideo: async function(videoPath) { console.log(`[STORAGE] Suppression de la vidéo : ${videoPath}`); return await StorageService.deleteFile(videoPath); } }; } module.exports = StorageService;