Files
timelapse-backend/src/services/storageService.js
Kerboul 4513af3aa0
All checks were successful
SSH Backend Deploy / ssh-deploy (push) Successful in 1m51s
feat(database): Implement DatabaseManager for managing database structure and initialization
feat(routes): Add camera, image, measurement, project, and video routes with Swagger documentation

feat(services): Create storageService and videoService for file management and video processing

fix(errorHandler): Enhance error handling with standardized responses and database operation wrappers
2025-04-27 01:02:33 +02:00

209 lines
6.3 KiB
JavaScript

// 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<string>} 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<Array<string>>} 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<Buffer>} Contenu du fichier
*/
static async getFile(filePath) {
return await fs.readFile(filePath);
}
/**
* Supprime un fichier
* @param {string} filePath - Chemin du fichier à supprimer
* @returns {Promise<string>} 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<Buffer>} 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<string>} 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<Buffer>} 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<string>} Message de confirmation
*/
deleteVideo: async function(videoPath) {
console.log(`[STORAGE] Suppression de la vidéo : ${videoPath}`);
return await StorageService.deleteFile(videoPath);
}
};
}
module.exports = StorageService;