Files
Vulture/VNode/services/game/session-manager.js
Jérémy CHOMAZ 0f0f1ffe33 (update) : implémentation création et suppression de sessions)
- Ajout de createSession() pour créer un dossier session avec config par défaut

- Ajout de deleteSession() pour supprimer un dossier session récursivement

- Abonnement aux topics game/session/create et game/session/delete

- Rafraîchissement automatique de la liste après création/suppression
2026-02-05 21:52:26 +01:00

291 lines
9.8 KiB
JavaScript

const fs = require('fs');
const mqtt = require('mqtt');
const path = require('path');
const express = require('express');
const multer = require('multer');
const cors = require('cors');
// Lecture du fichier de configuration
const configPath = path.join(__dirname, '../config/configuration.json');
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
// Extraction des informations de config
const {
mqttHost,
services: {
session: {
MQTTconfig: {
mqttSessionRequestTopic,
mqttSessionGetTopic,
mqttSessionUpdateTopic,
mqttSessionListTopic,
mqttSessionListResponseTopic,
mqttSessionCreateTopic,
mqttSessionDeleteTopic
},
httpPort
}
}
} = config;
const PORT = httpPort || 3000;
const quizzDir = path.join(__dirname, 'quizz');
// Configurer Express
const app = express();
app.use(cors());
// Configuration de Multer pour l'upload
const storage = multer.diskStorage({
destination: function (req, file, cb) {
const sessionId = req.params.sessionId;
let subfolder = 'assets';
// Organiser par type (envoyé par le frontend dans req.body.type)
if (req.body.type === 'video') subfolder = 'assets/videos';
else if (req.body.type === 'audio') subfolder = 'assets/audios';
else if (req.body.type === 'picture') subfolder = 'assets/pictures';
const uploadPath = path.join(quizzDir, sessionId, subfolder);
// Créer le dossier s'il n'existe pas
fs.mkdirSync(uploadPath, { recursive: true });
cb(null, uploadPath);
},
filename: function (req, file, cb) {
// Utiliser l'ID de la question comme nom de fichier si disponible
const ext = path.extname(file.originalname);
let filename = 'file-' + Date.now() + '-' + Math.round(Math.random() * 1E9);
if (req.body.questionId) {
filename = req.body.questionId;
}
cb(null, filename + ext);
}
});
const upload = multer({ storage: storage });
// Route d'upload
app.post('/upload/:sessionId', upload.single('file'), (req, res) => {
if (!req.file) {
return res.status(400).send('No file uploaded.');
}
// Calculer le chemin relatif pour l'URL
// req.file.path est le chemin absolu sur le disque
// On veut le chemin relatif à partir du dossier de session
const sessionId = req.params.sessionId;
const sessionDir = path.join(quizzDir, sessionId);
// path.relative(from, to) -> donne le chemin relatif
let relativeId = path.relative(sessionDir, req.file.path);
// Remplacer les backslashes par des slashs pour les URLs web
relativeId = relativeId.replace(/\\/g, '/');
// Ajouter le slash initial
const relativePath = `/${relativeId}`;
res.json({ path: relativePath, fullPath: req.file.path });
});
// App.use pour servir les fichiers statiques
app.use('/quizz', express.static(quizzDir));
app.listen(PORT, () => {
console.log(`[HTTP] Serveur d'upload démarré sur le port ${PORT}`);
});
// Connexion au broker MQTT
const client = mqtt.connect(mqttHost);
console.log("------------------------------------------------------------------------------");
console.log("[CONFIG] Session Manager chargé (Multi-Session + Upload)");
console.log("[CONFIG] Hôte MQTT :", mqttHost);
console.log("[CONFIG] Port HTTP :", PORT);
console.log("[CONFIG] Dossier Quizz :", quizzDir);
console.log("------------------------------------------------------------------------------");
client.on('connect', () => {
console.log(`[INFO] Connecté au broker MQTT à ${mqttHost}`);
client.subscribe(mqttSessionRequestTopic);
client.subscribe(mqttSessionUpdateTopic);
client.subscribe(mqttSessionListTopic);
client.subscribe(mqttSessionCreateTopic);
client.subscribe(mqttSessionDeleteTopic);
console.log(`[INFO] Abonné aux topics session`);
});
client.on('message', (topic, message) => {
if (topic === mqttSessionListTopic) {
console.log(`[INFO] Demande de liste de sessions`);
sendSessionList();
} else if (topic === mqttSessionRequestTopic) {
try {
const payload = JSON.parse(message.toString());
console.log(`[INFO] Demande de configuration pour session: ${payload.SessionId}`);
if (payload.SessionId) {
sendSessionConfiguration(payload.SessionId);
}
} catch (e) { console.error("Erreur payload request", e); }
} else if (topic === mqttSessionUpdateTopic) {
try {
const payload = JSON.parse(message.toString());
console.log(`[INFO] Mise à jour configuration pour session: ${payload.SessionId}`);
if (payload.SessionId && payload.Config) {
saveSessionConfiguration(payload.SessionId, payload.Config);
}
} catch (e) {
console.error('[ERREUR] Impossible de parser la mise à jour', e);
}
} else if (topic === mqttSessionCreateTopic) {
try {
const payload = JSON.parse(message.toString());
console.log(`[INFO] Demande de création de session: ${payload.SessionName}`);
if (payload.SessionName) {
createSession(payload.SessionName);
}
} catch (e) {
console.error('[ERREUR] Impossible de parser la demande de création', e);
}
} else if (topic === mqttSessionDeleteTopic) {
try {
const payload = JSON.parse(message.toString());
console.log(`[INFO] Demande de suppression de session: ${payload.SessionId}`);
if (payload.SessionId) {
deleteSession(payload.SessionId);
}
} catch (e) {
console.error('[ERREUR] Impossible de parser la demande de suppression', e);
}
}
});
function sendSessionList() {
fs.readdir(quizzDir, { withFileTypes: true }, (err, entries) => {
if (err) {
console.error('[ERREUR] Lecture dossier quizz', err);
return;
}
const sessions = [];
entries.forEach(entry => {
if (entry.isDirectory()) {
const configPath = path.join(quizzDir, entry.name, 'session-configuration.json');
if (fs.existsSync(configPath)) {
try {
const sessConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
sessions.push({
id: entry.name,
title: sessConfig.PackTitle || entry.name
});
} catch (e) {
sessions.push({ id: entry.name, title: entry.name + " (Erreur Config)" });
}
}
}
});
client.publish(mqttSessionListResponseTopic, JSON.stringify(sessions));
console.log(`[INFO] Liste envoyée : ${sessions.length} sessions`);
});
}
function sendSessionConfiguration(sessionId) {
const sessionFilePath = path.join(quizzDir, sessionId, 'session-configuration.json');
fs.readFile(sessionFilePath, 'utf8', (err, data) => {
if (err) {
console.error('[ERREUR] Impossible de lire le fichier de session', err);
return;
}
client.publish(mqttSessionGetTopic, data);
console.log(`[INFO] Configuration envoyée pour ${sessionId}`);
});
}
function saveSessionConfiguration(sessionId, newConfig) {
const sessionFilePath = path.join(quizzDir, sessionId, 'session-configuration.json');
// Validation
if (!newConfig.Questions || !Array.isArray(newConfig.Questions)) {
console.error('[ERREUR] Configuration invalide');
return;
}
const data = JSON.stringify(newConfig, null, 2);
fs.writeFile(sessionFilePath, data, (err) => {
if (err) {
console.error('[ERREUR] Impossible d\'écrire le fichier de session', err);
} else {
console.log(`[INFO] Session ${sessionId} mise à jour avec succès`);
// Confirmer la sauvegarde en renvoyant la config
client.publish(mqttSessionGetTopic, data);
}
});
}
function createSession(sessionName) {
// Nettoyer le nom pour le dossier
const safeName = sessionName.replace(/[^a-z0-9]/gi, '_').toLowerCase();
const newSessionPath = path.join(quizzDir, safeName);
if (fs.existsSync(newSessionPath)) {
console.error(`[ERREUR] La session ${safeName} existe déjà`);
return;
}
// Créer le dossier
try {
fs.mkdirSync(newSessionPath, { recursive: true });
// Créer la structure de base
const defaultConfig = {
PackId: "",
PackTitle: sessionName,
Questions: []
};
fs.writeFileSync(
path.join(newSessionPath, 'session-configuration.json'),
JSON.stringify(defaultConfig, null, 2)
);
console.log(`[INFO] Nouvelle session créée: ${safeName}`);
// Renvoyer la liste mise à jour
sendSessionList();
} catch (e) {
console.error(`[ERREUR] Impossible de créer la session ${safeName}`, e);
}
}
function deleteSession(sessionId) {
const sessionPath = path.join(quizzDir, sessionId);
if (!fs.existsSync(sessionPath)) {
console.error(`[ERREUR] La session ${sessionId} n'existe pas`);
return;
}
try {
fs.rmSync(sessionPath, { recursive: true, force: true });
console.log(`[INFO] Session supprimée: ${sessionId}`);
// Renvoyer la liste mise à jour
sendSessionList();
} catch (e) {
console.error(`[ERREUR] Impossible de supprimer la session ${sessionId}`, e);
}
}
client.on('error', (error) => {
console.error('[ERREUR] Erreur de connexion au broker MQTT:', error.message);
});