diff --git a/VNode/services/game/session-manager.js b/VNode/services/game/session-manager.js index a5ad5ee9..edcc41bd 100644 --- a/VNode/services/game/session-manager.js +++ b/VNode/services/game/session-manager.js @@ -21,7 +21,9 @@ const { mqttSessionListTopic, mqttSessionListResponseTopic, mqttSessionCreateTopic, - mqttSessionDeleteTopic + mqttSessionDeleteTopic, + mqttSessionDeleteMediaTopic, + mqttSessionRenameMediaTopic }, httpPort } @@ -116,6 +118,8 @@ client.on('connect', () => { client.subscribe(mqttSessionListTopic); client.subscribe(mqttSessionCreateTopic); client.subscribe(mqttSessionDeleteTopic); + client.subscribe(mqttSessionDeleteMediaTopic); + client.subscribe(mqttSessionRenameMediaTopic); console.log(`[INFO] Abonné aux topics session`); }); @@ -163,6 +167,26 @@ client.on('message', (topic, message) => { } catch (e) { console.error('[ERREUR] Impossible de parser la demande de suppression', e); } + } else if (topic === mqttSessionDeleteMediaTopic) { + try { + const payload = JSON.parse(message.toString()); + console.log(`[INFO] Demande de suppression de média: ${payload.MediaPath} pour session: ${payload.SessionId}`); + if (payload.SessionId && payload.MediaPath) { + deleteMedia(payload.SessionId, payload.MediaPath); + } + } catch (e) { + console.error('[ERREUR] Impossible de parser la demande de suppression de média', e); + } + } else if (topic === mqttSessionRenameMediaTopic) { + try { + const payload = JSON.parse(message.toString()); + console.log(`[INFO] Demande de renommage de média: ${payload.OldPath} -> ${payload.NewName}`); + if (payload.SessionId && payload.OldPath && payload.NewName) { + renameMedia(payload.SessionId, payload.OldPath, payload.NewName); + } + } catch (e) { + console.error('[ERREUR] Impossible de parser la demande de renommage', e); + } } }); @@ -285,6 +309,91 @@ function deleteSession(sessionId) { } } +function deleteMedia(sessionId, relativePath) { + // Sécuriser le chemin + // relativePath devrait être du type "/assets/..." + if (!relativePath) return; + + // Nettoyer le chemin (retirer le slash initial s'il existe) + if (relativePath.startsWith('/')) relativePath = relativePath.substring(1); + + const fullPath = path.join(quizzDir, sessionId, relativePath); + + // Vérifier que le chemin reste dans le dossier de la session (protection directory traversal) + if (!fullPath.startsWith(path.join(quizzDir, sessionId))) { + console.error(`[ERREUR] Tentative de suppression hors session: ${fullPath}`); + return; + } + + if (!fs.existsSync(fullPath)) { + console.error(`[ERREUR] Le fichier ${fullPath} n'existe pas`); + return; + } + + try { + fs.unlinkSync(fullPath); + console.log(`[INFO] Média supprimé: ${fullPath}`); + } catch (e) { + console.error(`[ERREUR] Impossible de supprimer le fichier ${fullPath}`, e); + } +} + client.on('error', (error) => { console.error('[ERREUR] Erreur de connexion au broker MQTT:', error.message); }); + +// Helper pour les renames tenaces sur Windows +function renameWithRetry(oldPath, newPath, retries = 5, delay = 100) { + try { + fs.renameSync(oldPath, newPath); + console.log(`[INFO] Média renommé : ${oldPath} -> ${newPath}`); + } catch (e) { + if (retries > 0) { + console.log(`[WARN] Échec renommage, nouvel essai dans ${delay}ms... (${retries} restants)`); + setTimeout(() => { + renameWithRetry(oldPath, newPath, retries - 1, delay * 2); + }, delay); + } else { + console.error(`[ERREUR] Impossible de renommer le fichier après plusieurs essais`, e); + } + } +} + +function renameMedia(sessionId, oldRelativePath, newName) { + if (!oldRelativePath || !newName) return; + + // Clean paths + if (oldRelativePath.startsWith('/')) oldRelativePath = oldRelativePath.substring(1); + + const oldFullPath = path.join(quizzDir, sessionId, oldRelativePath); + + // Security check + if (!oldFullPath.startsWith(path.join(quizzDir, sessionId))) { + console.error(`[ERREUR] Tentative de renommage hors session`); + return; + } + + if (!fs.existsSync(oldFullPath)) { + console.error(`[ERREUR] Fichier à renommer introuvable: ${oldFullPath}`); + return; + } + + const dir = path.dirname(oldFullPath); + const ext = path.extname(oldFullPath); + // newName comes as "Q-005", we add the extension + const newFilename = newName + ext; + const newFullPath = path.join(dir, newFilename); + + // Prevent overwriting existing files (check & delete) + if (fs.existsSync(newFullPath)) { + console.log(`[INFO] Le fichier de destination existe déjà, on supprime : ${newFullPath}`); + try { + fs.unlinkSync(newFullPath); + } catch (e) { + console.error(`[ERREUR] Impossible de supprimer le fichier existant ${newFullPath}`, e); + // On continue quand même pour tenter le rename (le rename écrasera peut-être ou échouera) + } + } + + renameWithRetry(oldFullPath, newFullPath); +}