mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
not finished
This commit is contained in:
parent
5c24ae56a9
commit
11222c70bd
12 changed files with 1080 additions and 200 deletions
|
|
@ -2,7 +2,8 @@ import http from "http";
|
||||||
import { Server, ServerOptions } from "socket.io";
|
import { Server, ServerOptions } from "socket.io";
|
||||||
import { setupWebsocket } from "./socket/setupWebSocket";
|
import { setupWebsocket } from "./socket/setupWebSocket";
|
||||||
import dotenv from "dotenv";
|
import dotenv from "dotenv";
|
||||||
import express from 'express';
|
import express from "express";
|
||||||
|
import os from "os"; // Import the os module
|
||||||
|
|
||||||
// Load environment variables
|
// Load environment variables
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
@ -36,6 +37,7 @@ app.get('/health', (_, res) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const ioOptions: Partial<ServerOptions> = {
|
const ioOptions: Partial<ServerOptions> = {
|
||||||
path: `/api/room/${roomId}/socket`,
|
path: `/api/room/${roomId}/socket`,
|
||||||
cors: {
|
cors: {
|
||||||
|
|
@ -52,4 +54,4 @@ setupWebsocket(io);
|
||||||
|
|
||||||
server.listen(port, () => {
|
server.listen(port, () => {
|
||||||
console.log(`WebSocket server is running on port ${port}`);
|
console.log(`WebSocket server is running on port ${port}`);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { Server, Socket } from "socket.io";
|
import { Server, Socket } from "socket.io";
|
||||||
|
import os from "os";
|
||||||
|
|
||||||
const MAX_USERS_PER_ROOM = 60;
|
const MAX_USERS_PER_ROOM = 60;
|
||||||
const MAX_TOTAL_CONNECTIONS = 2000;
|
const MAX_TOTAL_CONNECTIONS = 2000;
|
||||||
|
|
@ -19,10 +20,10 @@ export const setupWebsocket = (io: Server): void => {
|
||||||
|
|
||||||
socket.on("create-room", (sentRoomName) => {
|
socket.on("create-room", (sentRoomName) => {
|
||||||
// Ensure sentRoomName is a string before applying toUpperCase()
|
// Ensure sentRoomName is a string before applying toUpperCase()
|
||||||
const roomName = (typeof sentRoomName === "string" && sentRoomName.trim() !== "")
|
const roomName = (typeof sentRoomName === "string" && sentRoomName.trim() !== "")
|
||||||
? sentRoomName.toUpperCase()
|
? sentRoomName.toUpperCase()
|
||||||
: generateRoomName();
|
: generateRoomName();
|
||||||
|
|
||||||
if (!io.sockets.adapter.rooms.get(roomName)) {
|
if (!io.sockets.adapter.rooms.get(roomName)) {
|
||||||
socket.join(roomName);
|
socket.join(roomName);
|
||||||
socket.emit("create-success", roomName);
|
socket.emit("create-success", roomName);
|
||||||
|
|
@ -96,10 +97,62 @@ export const setupWebsocket = (io: Server): void => {
|
||||||
socket.on("error", (error) => {
|
socket.on("error", (error) => {
|
||||||
console.error("WebSocket server error:", error);
|
console.error("WebSocket server error:", error);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Stress Testing
|
||||||
|
|
||||||
|
socket.on("message-test", ({ roomName, message }: { roomName: string; message: string }) => {
|
||||||
|
console.log(`Message reçu dans la salle ${roomName} : ${message}`);
|
||||||
|
socket.to(roomName).emit("message", { id: socket.id, message });
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("get-usage", () => {
|
||||||
|
try {
|
||||||
|
const memoryUsage = process.memoryUsage();
|
||||||
|
const cpuUsage = process.cpuUsage();
|
||||||
|
const totalMemory = os.totalmem();
|
||||||
|
const freeMemory = os.freemem();
|
||||||
|
const loadAverage = os.loadavg(); // Load average over 1, 5, and 15 minutes
|
||||||
|
|
||||||
|
// Calculate CPU usage percentage
|
||||||
|
const userCpuPercentage = ((cpuUsage.user / 1e6) / os.cpus().length).toFixed(2); // in %
|
||||||
|
const systemCpuPercentage = ((cpuUsage.system / 1e6) / os.cpus().length).toFixed(2); // in %
|
||||||
|
|
||||||
|
const usageData = {
|
||||||
|
memory: {
|
||||||
|
total: totalMemory,
|
||||||
|
free: freeMemory,
|
||||||
|
rss: memoryUsage.rss,
|
||||||
|
heapTotal: memoryUsage.heapTotal,
|
||||||
|
heapUsed: memoryUsage.heapUsed,
|
||||||
|
external: memoryUsage.external,
|
||||||
|
usagePercentage: (((totalMemory - freeMemory) / totalMemory) * 100).toFixed(2), // % used
|
||||||
|
},
|
||||||
|
cpu: {
|
||||||
|
user: cpuUsage.user,
|
||||||
|
system: cpuUsage.system,
|
||||||
|
userPercentage: userCpuPercentage,
|
||||||
|
systemPercentage: systemCpuPercentage,
|
||||||
|
},
|
||||||
|
system: {
|
||||||
|
uptime: os.uptime(), // System uptime in seconds
|
||||||
|
loadAverage: {
|
||||||
|
"1min": loadAverage[0],
|
||||||
|
"5min": loadAverage[1],
|
||||||
|
"15min": loadAverage[2],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.emit("usage-data", usageData); // Send usage data back to the client
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error getting usage data:", error);
|
||||||
|
socket.emit("error", { message: "Failed to retrieve usage data" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const generateRoomName = (length = 6): string => {
|
const generateRoomName = (length = 6): string => {
|
||||||
const characters = "0123456789";
|
const characters = "0123456789";
|
||||||
let result = "";
|
let result = "";
|
||||||
|
|
|
||||||
|
|
@ -19,19 +19,20 @@ export class Student {
|
||||||
reconnectionDelay: 10000,
|
reconnectionDelay: 10000,
|
||||||
timeout: 20000,
|
timeout: 20000,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.socket.on('connect', () => {
|
this.socket.on('connect', () => {
|
||||||
this.joinRoom(this.roomName, this.username);
|
this.joinRoom(this.roomName, this.username);
|
||||||
resolve(this.socket);
|
this.listenForMessages(); // Start listening for messages
|
||||||
|
resolve(this.socket);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.socket.on('error', (error) => {
|
this.socket.on('error', (error) => {
|
||||||
reject(new Error(`Connection error: ${error.message}`));
|
reject(new Error(`Connection error: ${error.message}`));
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error connecting ${this.name} to room ${this.roomId}:`, error.message);
|
console.error(`Error connecting ${this.name} to room ${this.roomName}:`, error.message);
|
||||||
reject(error);
|
reject(error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -41,4 +42,18 @@ export class Student {
|
||||||
this.socket.emit('join-room', { roomName, username });
|
this.socket.emit('join-room', { roomName, username });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendMessage(message) {
|
||||||
|
if (this.socket && this.socket.connected) {
|
||||||
|
this.socket.emit('message-test', { room: this.roomName, message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
listenForMessages() {
|
||||||
|
if (this.socket) {
|
||||||
|
this.socket.on('message-test', (data) => {
|
||||||
|
console.log(`Message received in room ${this.roomName} by ${this.username}:`, data.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,23 +19,19 @@ export class Teacher {
|
||||||
reconnectionDelay: 10000,
|
reconnectionDelay: 10000,
|
||||||
timeout: 20000,
|
timeout: 20000,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.socket.on('connect', () => {
|
this.socket.on('connect', () => {
|
||||||
this.createRoom(this.roomName);
|
this.createRoom(this.roomName);
|
||||||
resolve(this.socket);
|
this.listenForMessages(); // Start listening for messages
|
||||||
|
resolve(this.socket);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.socket.on('error', (error) => {
|
this.socket.on('error', (error) => {
|
||||||
reject(new Error(`Connection error: ${error.message}`));
|
reject(new Error(`Connection error: ${error.message}`));
|
||||||
});
|
});
|
||||||
|
|
||||||
this.socket.on('create-success', () => {
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error connecting ${this.name} to room ${this.roomId}:`, error.message);
|
console.error(`Error connecting ${this.name} to room ${this.roomName}:`, error.message);
|
||||||
reject(error);
|
reject(error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -45,4 +41,18 @@ export class Teacher {
|
||||||
this.socket.emit('create-room', this.roomName || undefined);
|
this.socket.emit('create-room', this.roomName || undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendMessage(message) {
|
||||||
|
if (this.socket && this.socket.connected) {
|
||||||
|
this.socket.emit('message-test', { room: this.roomName, message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
listenForMessages() {
|
||||||
|
if (this.socket) {
|
||||||
|
this.socket.on('message-test', (data) => {
|
||||||
|
console.log(`Message received in room ${this.roomName} by ${this.username}:`, data.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,15 @@
|
||||||
import { attemptLoginOrRegister, createRoomContainer } from './utility/apiServices.js';
|
import { attemptLoginOrRegister, createRoomContainer, captureResourceUsageForContainers } from './utility/apiServices.js';
|
||||||
import { Student } from './class/student.js';
|
import { Student } from './class/student.js';
|
||||||
import { Teacher } from './class/teacher.js';
|
import { Teacher } from './class/teacher.js';
|
||||||
import { writeMetricsToFile } from './utility/writeMetrics.js';
|
import { writeMetricsToFile, generateGraphs } from './utility/writeMetrics.js';
|
||||||
|
|
||||||
const BASE_URL = 'http://localhost';
|
const BASE_URL = 'http://localhost';
|
||||||
const user = { username: 'admin@example.com', password: 'adminPassword' };
|
const user = { username: 'admin@example.com', password: 'adminPassword' };
|
||||||
const numberRooms = 5;
|
const numberRooms = 20;
|
||||||
const studentPerRoom = 59; // Max : 60, 1 place réservée pour le professeur
|
const studentPerRoom = 58; // Max: 60, 1 reserved for teacher
|
||||||
const roomAssociations = {};
|
const roomAssociations = {};
|
||||||
const allSockets = []; // Suivi de toutes les connexions WebSocket actives
|
const allSockets = []; // Tracks all active WebSocket connections
|
||||||
|
|
||||||
// Métriques
|
|
||||||
const metrics = {
|
const metrics = {
|
||||||
roomsCreated: 0,
|
roomsCreated: 0,
|
||||||
roomsFailed: 0,
|
roomsFailed: 0,
|
||||||
|
|
@ -18,133 +17,211 @@ const metrics = {
|
||||||
teachersFailed: 0,
|
teachersFailed: 0,
|
||||||
studentsConnected: 0,
|
studentsConnected: 0,
|
||||||
studentsFailed: 0,
|
studentsFailed: 0,
|
||||||
|
messagesSent: 0,
|
||||||
|
messagesReceived: 0,
|
||||||
|
totalLatency: 0,
|
||||||
|
maxLatency: 0,
|
||||||
|
minLatency: Number.MAX_SAFE_INTEGER,
|
||||||
|
throughput: 0,
|
||||||
startTime: null,
|
startTime: null,
|
||||||
endTime: null,
|
endTime: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Creates rooms and tracks their creation
|
||||||
async function createRoomContainers(token) {
|
async function createRoomContainers(token) {
|
||||||
console.time('Temps de création des salles');
|
console.time('Room creation time');
|
||||||
const roomCreationPromises = Array.from({ length: numberRooms }, async () => {
|
const roomCreationPromises = Array.from({ length: numberRooms }, async () => {
|
||||||
const room = await createRoomContainer(BASE_URL, token);
|
try {
|
||||||
if (room?.id) {
|
const room = await createRoomContainer(BASE_URL, token);
|
||||||
roomAssociations[room.id] = { teacher: null, students: [] };
|
if (room?.id) {
|
||||||
metrics.roomsCreated++;
|
roomAssociations[room.id] = { teacher: null, students: [] };
|
||||||
console.log(`Salle créée avec l'ID : ${room.id}`);
|
metrics.roomsCreated++;
|
||||||
} else {
|
console.log(`Room created with ID: ${room.id}`);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
metrics.roomsFailed++;
|
metrics.roomsFailed++;
|
||||||
console.warn('Échec de la création d’une salle.');
|
console.warn('Failed to create a room.');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await Promise.allSettled(roomCreationPromises);
|
await Promise.allSettled(roomCreationPromises);
|
||||||
console.timeEnd('Temps de création des salles');
|
console.timeEnd('Room creation time');
|
||||||
console.log(`Nombre total de salles créées : ${Object.keys(roomAssociations).length}`);
|
console.log(`Total rooms created: ${Object.keys(roomAssociations).length}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Connects teachers to their respective rooms
|
||||||
async function addAndConnectTeachers() {
|
async function addAndConnectTeachers() {
|
||||||
console.time('Temps de connexion des enseignants');
|
console.time('Teacher connection time');
|
||||||
const teacherCreationPromises = Object.keys(roomAssociations).map(async (roomId, index) => {
|
const teacherPromises = Object.keys(roomAssociations).map(async (roomId, index) => {
|
||||||
const teacher = new Teacher(`teacher_${index}`, roomId);
|
const teacher = new Teacher(`teacher_${index}`, roomId);
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
const socket = await teacher.connectToRoom(BASE_URL);
|
const socket = await teacher.connectToRoom(BASE_URL);
|
||||||
const latency = Date.now() - start;
|
const latency = Date.now() - start;
|
||||||
|
|
||||||
|
metrics.totalLatency += latency;
|
||||||
|
metrics.maxLatency = Math.max(metrics.maxLatency, latency);
|
||||||
|
metrics.minLatency = Math.min(metrics.minLatency, latency);
|
||||||
|
|
||||||
if (socket.connected) {
|
if (socket.connected) {
|
||||||
allSockets.push(socket);
|
allSockets.push(socket);
|
||||||
roomAssociations[roomId].teacher = teacher;
|
roomAssociations[roomId].teacher = teacher;
|
||||||
metrics.teachersConnected++;
|
metrics.teachersConnected++;
|
||||||
console.log(`Enseignant ${teacher.username} connecté à la salle ${roomId}. Latence : ${latency}ms`);
|
console.log(`Teacher ${teacher.username} connected to room ${roomId}. Latency: ${latency}ms`);
|
||||||
} else {
|
} else {
|
||||||
metrics.teachersFailed++;
|
metrics.teachersFailed++;
|
||||||
console.warn(`Échec de la connexion de l'enseignant ${index} à la salle ${roomId}`);
|
console.warn(`Failed to connect teacher ${index} to room ${roomId}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await Promise.allSettled(teacherCreationPromises);
|
await Promise.allSettled(teacherPromises);
|
||||||
console.timeEnd('Temps de connexion des enseignants');
|
console.timeEnd('Teacher connection time');
|
||||||
console.log('Tous les enseignants ont été ajoutés et connectés à leurs salles respectives.');
|
console.log('All teachers connected to their respective rooms.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Connects students to their respective rooms
|
||||||
async function addAndConnectStudents() {
|
async function addAndConnectStudents() {
|
||||||
console.time('Temps de connexion des étudiants');
|
console.time('Student connection time');
|
||||||
const studentCreationPromises = Object.entries(roomAssociations).flatMap(([roomId, association], roomIndex) =>
|
const studentPromises = Object.entries(roomAssociations).flatMap(([roomId, association], roomIndex) =>
|
||||||
Array.from({ length: studentPerRoom }, async (_, i) => {
|
Array.from({ length: studentPerRoom }, async (_, i) => {
|
||||||
const student = new Student(`student_${roomIndex}_${i}`, roomId);
|
const student = new Student(`student_${roomIndex}_${i}`, roomId);
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
const socket = await student.connectToRoom(BASE_URL);
|
const socket = await student.connectToRoom(BASE_URL);
|
||||||
const latency = Date.now() - start;
|
const latency = Date.now() - start;
|
||||||
|
|
||||||
|
metrics.totalLatency += latency;
|
||||||
|
metrics.maxLatency = Math.max(metrics.maxLatency, latency);
|
||||||
|
metrics.minLatency = Math.min(metrics.minLatency, latency);
|
||||||
|
|
||||||
if (socket.connected) {
|
if (socket.connected) {
|
||||||
allSockets.push(socket);
|
allSockets.push(socket);
|
||||||
association.students.push(student);
|
association.students.push(student);
|
||||||
metrics.studentsConnected++;
|
metrics.studentsConnected++;
|
||||||
console.log(`Étudiant ${student.username} connecté à la salle ${roomId}. Latence : ${latency}ms`);
|
|
||||||
} else {
|
} else {
|
||||||
metrics.studentsFailed++;
|
metrics.studentsFailed++;
|
||||||
console.warn(`Échec de la connexion de l'étudiant ${roomIndex}_${i} à la salle ${roomId}`);
|
console.warn(`Failed to connect student ${roomIndex}_${i} to room ${roomId}`);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
await Promise.allSettled(studentCreationPromises);
|
await Promise.allSettled(studentPromises);
|
||||||
console.timeEnd('Temps de connexion des étudiants');
|
console.timeEnd('Student connection time');
|
||||||
console.log('Tous les étudiants ont été ajoutés et connectés à leurs salles respectives.');
|
console.log('All students connected to their respective rooms.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Simulates conversations in all rooms
|
||||||
|
async function simulateConversation() {
|
||||||
|
console.log("Conversation simulation started...");
|
||||||
|
const messages = [
|
||||||
|
"Bonjour, tout le monde !",
|
||||||
|
"Pouvez-vous répondre à la question 1 ?",
|
||||||
|
"J'ai une question sur l'exercice.",
|
||||||
|
"Voici la réponse à la question 1.",
|
||||||
|
"Merci pour vos réponses, continuons avec la question 2.",
|
||||||
|
"Je ne comprends pas bien, pouvez-vous expliquer à nouveau ?",
|
||||||
|
];
|
||||||
|
|
||||||
|
const interval = 1000;
|
||||||
|
const duration = 10000;
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
|
while (Date.now() - startTime < duration) {
|
||||||
|
for (const [roomId, association] of Object.entries(roomAssociations)) {
|
||||||
|
if (association.teacher) {
|
||||||
|
const teacherMessage = `Teacher says: ${messages[Math.floor(Math.random() * messages.length)]}`;
|
||||||
|
association.teacher.sendMessage(teacherMessage);
|
||||||
|
metrics.messagesSent++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const student of association.students) {
|
||||||
|
const studentMessage = `${student.username} says: ${messages[Math.floor(Math.random() * messages.length)]}`;
|
||||||
|
student.sendMessage(studentMessage);
|
||||||
|
metrics.messagesSent++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, interval));
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Conversation simulation ended.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Closes all active WebSocket connections
|
||||||
function closeAllSockets() {
|
function closeAllSockets() {
|
||||||
console.log('Fermeture de toutes les connexions Socket.IO...');
|
console.log('Closing all Socket.IO connections...');
|
||||||
allSockets.forEach((socket) => {
|
allSockets.forEach((socket) => {
|
||||||
if (socket && socket.connected) {
|
if (socket && socket.connected) {
|
||||||
try {
|
try {
|
||||||
socket.disconnect();
|
socket.disconnect();
|
||||||
console.log('Connexion Socket.IO déconnectée.');
|
console.log('Socket.IO connection disconnected.');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Erreur lors de la déconnexion du socket Socket.IO :', error.message);
|
console.error('Error disconnecting Socket.IO connection:', error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
console.log('Toutes les connexions Socket.IO ont été déconnectées.');
|
console.log('All Socket.IO connections have been disconnected.');
|
||||||
}
|
|
||||||
|
|
||||||
function generateReport() {
|
|
||||||
console.log('Toutes les tâches ont été terminées.');
|
|
||||||
console.log('--- Résultats du test de charge ---');
|
|
||||||
console.log(`Salles créées : ${metrics.roomsCreated}`);
|
|
||||||
console.log(`Échecs de création de salles : ${metrics.roomsFailed}`);
|
|
||||||
console.log(`Enseignants connectés : ${metrics.teachersConnected}`);
|
|
||||||
console.log(`Échecs de connexion des enseignants : ${metrics.teachersFailed}`);
|
|
||||||
console.log(`Étudiants connectés : ${metrics.studentsConnected}`);
|
|
||||||
console.log(`Échecs de connexion des étudiants : ${metrics.studentsFailed}`);
|
|
||||||
console.log(`Durée totale d'exécution : ${(metrics.endTime - metrics.startTime) / 1000}s`);
|
|
||||||
console.log('Utilisation de la mémoire :', process.memoryUsage());
|
|
||||||
writeMetricsToFile( metrics);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Main function to orchestrate the workflow
|
||||||
async function main() {
|
async function main() {
|
||||||
try {
|
try {
|
||||||
metrics.startTime = new Date();
|
metrics.startTime = new Date();
|
||||||
const token = await attemptLoginOrRegister(BASE_URL, user.username, user.password);
|
|
||||||
if (!token) throw new Error('Échec de la connexion.');
|
|
||||||
|
|
||||||
|
// Login or register
|
||||||
|
const token = await attemptLoginOrRegister(BASE_URL, user.username, user.password);
|
||||||
|
if (!token) throw new Error('Failed to login or register.');
|
||||||
|
|
||||||
|
// Room creation
|
||||||
await createRoomContainers(token);
|
await createRoomContainers(token);
|
||||||
await addAndConnectTeachers();
|
|
||||||
await addAndConnectStudents();
|
// Resource monitoring and test activities
|
||||||
|
const roomIds = Object.keys(roomAssociations);
|
||||||
|
const usageCaptureInterval = 100;
|
||||||
|
let testCompleted = false;
|
||||||
|
|
||||||
|
const resourceCapturePromise = captureResourceUsageForContainers(
|
||||||
|
BASE_URL,
|
||||||
|
roomIds,
|
||||||
|
usageCaptureInterval,
|
||||||
|
() => testCompleted,
|
||||||
|
metrics
|
||||||
|
);
|
||||||
|
|
||||||
|
const testPromise = (async () => {
|
||||||
|
await addAndConnectTeachers();
|
||||||
|
await addAndConnectStudents();
|
||||||
|
await simulateConversation();
|
||||||
|
testCompleted = true;
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||||
|
})();
|
||||||
|
|
||||||
|
await Promise.all([resourceCapturePromise, testPromise]);
|
||||||
|
|
||||||
metrics.endTime = new Date();
|
metrics.endTime = new Date();
|
||||||
|
writeMetricsToFile(metrics);
|
||||||
|
await generateGraphs(metrics.resourceUsage, metrics);
|
||||||
|
|
||||||
generateReport();
|
console.log("All tasks completed successfully!");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Une erreur est survenue :', error.message);
|
console.error('Error:', error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gestion de l'interruption et de la fermeture
|
// Handle process interruptions
|
||||||
process.on('SIGINT', () => {
|
process.on('SIGINT', () => {
|
||||||
console.log('Script interrompu (Ctrl+C).');
|
console.log('Process interrupted (Ctrl+C).');
|
||||||
closeAllSockets();
|
closeAllSockets();
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('exit', closeAllSockets);
|
process.on('exit', () => closeAllSockets());
|
||||||
|
process.on('uncaughtException', (err) => {
|
||||||
|
console.error('Uncaught Exception:', err);
|
||||||
|
closeAllSockets();
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
process.on('unhandledRejection', (reason, promise) => {
|
||||||
|
console.error('Unhandled Rejection:', promise, 'reason:', reason);
|
||||||
|
closeAllSockets();
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
main();
|
main();
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
{
|
|
||||||
"roomsCreated": 5,
|
|
||||||
"roomsFailed": 0,
|
|
||||||
"teachersConnected": 5,
|
|
||||||
"teachersFailed": 0,
|
|
||||||
"studentsConnected": 295,
|
|
||||||
"studentsFailed": 0,
|
|
||||||
"startTime": "2024-11-16T01:06:28.606Z",
|
|
||||||
"endTime": "2024-11-16T01:06:31.135Z",
|
|
||||||
"executionTime": 2.529,
|
|
||||||
"memoryUsage": {
|
|
||||||
"rss": 72040448,
|
|
||||||
"heapTotal": 51122176,
|
|
||||||
"heapUsed": 23656264,
|
|
||||||
"external": 4503630,
|
|
||||||
"arrayBuffers": 170538
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
{
|
|
||||||
"roomsCreated": 5,
|
|
||||||
"roomsFailed": 0,
|
|
||||||
"teachersConnected": 5,
|
|
||||||
"teachersFailed": 0,
|
|
||||||
"studentsConnected": 295,
|
|
||||||
"studentsFailed": 0,
|
|
||||||
"startTime": "2024-11-16T01:08:33.133Z",
|
|
||||||
"endTime": "2024-11-16T01:08:35.626Z",
|
|
||||||
"executionTime": 2.493,
|
|
||||||
"memoryUsage": {
|
|
||||||
"rss": 71737344,
|
|
||||||
"heapTotal": 51122176,
|
|
||||||
"heapUsed": 23472768,
|
|
||||||
"external": 4499368,
|
|
||||||
"arrayBuffers": 166276
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
{
|
|
||||||
"roomsCreated": 5,
|
|
||||||
"roomsFailed": 0,
|
|
||||||
"teachersConnected": 5,
|
|
||||||
"teachersFailed": 0,
|
|
||||||
"studentsConnected": 295,
|
|
||||||
"studentsFailed": 0,
|
|
||||||
"startTime": "2024-11-16T01:09:27.129Z",
|
|
||||||
"endTime": "2024-11-16T01:09:30.032Z",
|
|
||||||
"executionTime": 2.903,
|
|
||||||
"memoryUsage": {
|
|
||||||
"rss": 72146944,
|
|
||||||
"heapTotal": 50860032,
|
|
||||||
"heapUsed": 22783944,
|
|
||||||
"external": 4496062,
|
|
||||||
"arrayBuffers": 162970
|
|
||||||
}
|
|
||||||
}
|
|
||||||
549
test/stressTest/package-lock.json
generated
549
test/stressTest/package-lock.json
generated
|
|
@ -10,6 +10,7 @@
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.7.7",
|
"axios": "^1.7.7",
|
||||||
|
"chartjs-node-canvas": "^4.1.6",
|
||||||
"dockerode": "^4.0.2",
|
"dockerode": "^4.0.2",
|
||||||
"socket.io": "^4.8.1",
|
"socket.io": "^4.8.1",
|
||||||
"socket.io-client": "^4.8.1"
|
"socket.io-client": "^4.8.1"
|
||||||
|
|
@ -20,6 +21,25 @@
|
||||||
"resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz",
|
||||||
"integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q=="
|
"integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@mapbox/node-pre-gyp": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"detect-libc": "^2.0.0",
|
||||||
|
"https-proxy-agent": "^5.0.0",
|
||||||
|
"make-dir": "^3.1.0",
|
||||||
|
"node-fetch": "^2.6.7",
|
||||||
|
"nopt": "^5.0.0",
|
||||||
|
"npmlog": "^5.0.1",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"semver": "^7.3.5",
|
||||||
|
"tar": "^6.1.11"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"node-pre-gyp": "bin/node-pre-gyp"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@socket.io/component-emitter": {
|
"node_modules/@socket.io/component-emitter": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||||
|
|
@ -46,6 +66,11 @@
|
||||||
"undici-types": "~6.19.8"
|
"undici-types": "~6.19.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/abbrev": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
|
||||||
|
},
|
||||||
"node_modules/accepts": {
|
"node_modules/accepts": {
|
||||||
"version": "1.3.8",
|
"version": "1.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||||
|
|
@ -58,6 +83,43 @@
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/agent-base": {
|
||||||
|
"version": "6.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
||||||
|
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ansi-regex": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/aproba": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
|
||||||
|
},
|
||||||
|
"node_modules/are-we-there-yet": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
|
||||||
|
"deprecated": "This package is no longer supported.",
|
||||||
|
"dependencies": {
|
||||||
|
"delegates": "^1.0.0",
|
||||||
|
"readable-stream": "^3.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/asn1": {
|
"node_modules/asn1": {
|
||||||
"version": "0.2.6",
|
"version": "0.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
|
||||||
|
|
@ -81,6 +143,11 @@
|
||||||
"proxy-from-env": "^1.1.0"
|
"proxy-from-env": "^1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/balanced-match": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||||
|
},
|
||||||
"node_modules/base64-js": {
|
"node_modules/base64-js": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
|
|
@ -126,6 +193,15 @@
|
||||||
"readable-stream": "^3.4.0"
|
"readable-stream": "^3.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/brace-expansion": {
|
||||||
|
"version": "1.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
|
"dependencies": {
|
||||||
|
"balanced-match": "^1.0.0",
|
||||||
|
"concat-map": "0.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/buffer": {
|
"node_modules/buffer": {
|
||||||
"version": "5.7.1",
|
"version": "5.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
||||||
|
|
@ -158,11 +234,51 @@
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/canvas": {
|
||||||
|
"version": "2.11.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz",
|
||||||
|
"integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@mapbox/node-pre-gyp": "^1.0.0",
|
||||||
|
"nan": "^2.17.0",
|
||||||
|
"simple-get": "^3.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/chart.js": {
|
||||||
|
"version": "3.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.9.1.tgz",
|
||||||
|
"integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w==",
|
||||||
|
"peer": true
|
||||||
|
},
|
||||||
|
"node_modules/chartjs-node-canvas": {
|
||||||
|
"version": "4.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/chartjs-node-canvas/-/chartjs-node-canvas-4.1.6.tgz",
|
||||||
|
"integrity": "sha512-UQJbPWrvqB/FoLclGA9BaLQmZbzSYlujF4w8NZd6Xzb+sqgACBb2owDX6m7ifCXLjUW5Nz0Qx0qqrTtQkkSoYw==",
|
||||||
|
"dependencies": {
|
||||||
|
"canvas": "^2.8.0",
|
||||||
|
"tslib": "^2.3.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"chart.js": "^3.5.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chownr": {
|
"node_modules/chownr": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
||||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
|
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/color-support": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
|
||||||
|
"bin": {
|
||||||
|
"color-support": "bin.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/combined-stream": {
|
"node_modules/combined-stream": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
|
|
@ -174,6 +290,16 @@
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/concat-map": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
|
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
|
||||||
|
},
|
||||||
|
"node_modules/console-control-strings": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
|
||||||
|
},
|
||||||
"node_modules/cookie": {
|
"node_modules/cookie": {
|
||||||
"version": "0.7.2",
|
"version": "0.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||||
|
|
@ -224,6 +350,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/decompress-response": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
|
||||||
|
"dependencies": {
|
||||||
|
"mimic-response": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/delayed-stream": {
|
"node_modules/delayed-stream": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
|
|
@ -232,6 +369,19 @@
|
||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/delegates": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="
|
||||||
|
},
|
||||||
|
"node_modules/detect-libc": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/docker-modem": {
|
"node_modules/docker-modem": {
|
||||||
"version": "5.0.3",
|
"version": "5.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.3.tgz",
|
||||||
|
|
@ -259,6 +409,11 @@
|
||||||
"node": ">= 8.0"
|
"node": ">= 8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/emoji-regex": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||||
|
},
|
||||||
"node_modules/end-of-stream": {
|
"node_modules/end-of-stream": {
|
||||||
"version": "1.4.4",
|
"version": "1.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||||
|
|
@ -344,6 +499,90 @@
|
||||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||||
},
|
},
|
||||||
|
"node_modules/fs-minipass": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
|
||||||
|
"dependencies": {
|
||||||
|
"minipass": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fs-minipass/node_modules/minipass": {
|
||||||
|
"version": "3.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
|
||||||
|
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
|
||||||
|
"dependencies": {
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fs.realpath": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
|
||||||
|
},
|
||||||
|
"node_modules/gauge": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
|
||||||
|
"deprecated": "This package is no longer supported.",
|
||||||
|
"dependencies": {
|
||||||
|
"aproba": "^1.0.3 || ^2.0.0",
|
||||||
|
"color-support": "^1.1.2",
|
||||||
|
"console-control-strings": "^1.0.0",
|
||||||
|
"has-unicode": "^2.0.1",
|
||||||
|
"object-assign": "^4.1.1",
|
||||||
|
"signal-exit": "^3.0.0",
|
||||||
|
"string-width": "^4.2.3",
|
||||||
|
"strip-ansi": "^6.0.1",
|
||||||
|
"wide-align": "^1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/glob": {
|
||||||
|
"version": "7.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
|
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||||
|
"deprecated": "Glob versions prior to v9 are no longer supported",
|
||||||
|
"dependencies": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.1.1",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-unicode": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
|
||||||
|
},
|
||||||
|
"node_modules/https-proxy-agent": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
|
||||||
|
"dependencies": {
|
||||||
|
"agent-base": "6",
|
||||||
|
"debug": "4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ieee754": {
|
"node_modules/ieee754": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||||
|
|
@ -363,11 +602,51 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/inflight": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||||
|
"deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
|
||||||
|
"dependencies": {
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/inherits": {
|
"node_modules/inherits": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/is-fullwidth-code-point": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/make-dir": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
|
||||||
|
"dependencies": {
|
||||||
|
"semver": "^6.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/make-dir/node_modules/semver": {
|
||||||
|
"version": "6.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||||
|
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mime-db": {
|
"node_modules/mime-db": {
|
||||||
"version": "1.52.0",
|
"version": "1.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
|
|
@ -387,6 +666,70 @@
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mimic-response": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/minimatch": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^1.1.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/minipass": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/minizlib": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
|
||||||
|
"dependencies": {
|
||||||
|
"minipass": "^3.0.0",
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/minizlib/node_modules/minipass": {
|
||||||
|
"version": "3.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
|
||||||
|
"integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
|
||||||
|
"dependencies": {
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mkdirp": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||||
|
"bin": {
|
||||||
|
"mkdirp": "bin/cmd.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mkdirp-classic": {
|
"node_modules/mkdirp-classic": {
|
||||||
"version": "0.5.3",
|
"version": "0.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
||||||
|
|
@ -400,8 +743,7 @@
|
||||||
"node_modules/nan": {
|
"node_modules/nan": {
|
||||||
"version": "2.22.0",
|
"version": "2.22.0",
|
||||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz",
|
"resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz",
|
||||||
"integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==",
|
"integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw=="
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"node_modules/negotiator": {
|
"node_modules/negotiator": {
|
||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
|
|
@ -411,6 +753,51 @@
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-fetch": {
|
||||||
|
"version": "2.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||||
|
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||||
|
"dependencies": {
|
||||||
|
"whatwg-url": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "4.x || >=6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"encoding": "^0.1.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"encoding": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/nopt": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"abbrev": "1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"nopt": "bin/nopt.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/npmlog": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
|
||||||
|
"deprecated": "This package is no longer supported.",
|
||||||
|
"dependencies": {
|
||||||
|
"are-we-there-yet": "^2.0.0",
|
||||||
|
"console-control-strings": "^1.1.0",
|
||||||
|
"gauge": "^3.0.0",
|
||||||
|
"set-blocking": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/object-assign": {
|
"node_modules/object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
|
@ -427,6 +814,14 @@
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/path-is-absolute": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/proxy-from-env": {
|
"node_modules/proxy-from-env": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||||
|
|
@ -454,6 +849,21 @@
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/rimraf": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||||
|
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||||
|
"dependencies": {
|
||||||
|
"glob": "^7.1.3"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rimraf": "bin.js"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/safe-buffer": {
|
"node_modules/safe-buffer": {
|
||||||
"version": "5.2.1",
|
"version": "5.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
|
|
@ -478,6 +888,56 @@
|
||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/semver": {
|
||||||
|
"version": "7.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
|
||||||
|
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/set-blocking": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
|
||||||
|
},
|
||||||
|
"node_modules/signal-exit": {
|
||||||
|
"version": "3.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||||
|
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
|
||||||
|
},
|
||||||
|
"node_modules/simple-concat": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/simple-get": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
|
||||||
|
"dependencies": {
|
||||||
|
"decompress-response": "^4.2.0",
|
||||||
|
"once": "^1.3.1",
|
||||||
|
"simple-concat": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/socket.io": {
|
"node_modules/socket.io": {
|
||||||
"version": "4.8.1",
|
"version": "4.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz",
|
||||||
|
|
@ -560,6 +1020,46 @@
|
||||||
"safe-buffer": "~5.2.0"
|
"safe-buffer": "~5.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/string-width": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
|
"dependencies": {
|
||||||
|
"emoji-regex": "^8.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
|
"strip-ansi": "^6.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/strip-ansi": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tar": {
|
||||||
|
"version": "6.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
|
||||||
|
"integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
|
||||||
|
"dependencies": {
|
||||||
|
"chownr": "^2.0.0",
|
||||||
|
"fs-minipass": "^2.0.0",
|
||||||
|
"minipass": "^5.0.0",
|
||||||
|
"minizlib": "^2.1.1",
|
||||||
|
"mkdirp": "^1.0.3",
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tar-fs": {
|
"node_modules/tar-fs": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz",
|
||||||
|
|
@ -586,6 +1086,24 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tar/node_modules/chownr": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tr46": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||||
|
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||||
|
},
|
||||||
|
"node_modules/tslib": {
|
||||||
|
"version": "2.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
||||||
|
},
|
||||||
"node_modules/tweetnacl": {
|
"node_modules/tweetnacl": {
|
||||||
"version": "0.14.5",
|
"version": "0.14.5",
|
||||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||||
|
|
@ -609,6 +1127,28 @@
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/webidl-conversions": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-url": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||||
|
"dependencies": {
|
||||||
|
"tr46": "~0.0.3",
|
||||||
|
"webidl-conversions": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/wide-align": {
|
||||||
|
"version": "1.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
|
||||||
|
"integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
|
||||||
|
"dependencies": {
|
||||||
|
"string-width": "^1.0.2 || 2 || 3 || 4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/wrappy": {
|
"node_modules/wrappy": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
|
|
@ -641,6 +1181,11 @@
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yallist": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.7.7",
|
"axios": "^1.7.7",
|
||||||
|
"chartjs-node-canvas": "^4.1.6",
|
||||||
"dockerode": "^4.0.2",
|
"dockerode": "^4.0.2",
|
||||||
"socket.io": "^4.8.1",
|
"socket.io": "^4.8.1",
|
||||||
"socket.io-client": "^4.8.1"
|
"socket.io-client": "^4.8.1"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import { io } from "socket.io-client";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs in a user.
|
* Logs in a user.
|
||||||
|
|
@ -8,24 +9,18 @@ import axios from "axios";
|
||||||
* @returns {Promise<string>} - The authentication token if successful.
|
* @returns {Promise<string>} - The authentication token if successful.
|
||||||
*/
|
*/
|
||||||
async function login(baseUrl, email, password) {
|
async function login(baseUrl, email, password) {
|
||||||
if (!email || !password) {
|
if (!email || !password) throw new Error("Email and password are required.");
|
||||||
throw new Error("Email and password are required.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = `${baseUrl}/api/user/login`;
|
|
||||||
const payload = { email, password };
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await axios.post(url, payload, {
|
const res = await axios.post(`${baseUrl}/api/user/login`, { email, password }, {
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.status !== 200 || !res.data.token) {
|
if (res.status === 200 && res.data.token) {
|
||||||
throw new Error(`Login failed. Status: ${res.status}`);
|
console.log(`Login successful for ${email}`);
|
||||||
|
return res.data.token;
|
||||||
}
|
}
|
||||||
|
throw new Error(`Login failed. Status: ${res.status}`);
|
||||||
console.log(`Login successful for ${email}`);
|
|
||||||
return res.data.token;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Login error for ${email}:`, error.message);
|
console.error(`Login error for ${email}:`, error.message);
|
||||||
throw error;
|
throw error;
|
||||||
|
|
@ -40,24 +35,18 @@ async function login(baseUrl, email, password) {
|
||||||
* @returns {Promise<string>} - A success message if registration is successful.
|
* @returns {Promise<string>} - A success message if registration is successful.
|
||||||
*/
|
*/
|
||||||
async function register(baseUrl, email, password) {
|
async function register(baseUrl, email, password) {
|
||||||
if (!email || !password) {
|
if (!email || !password) throw new Error("Email and password are required.");
|
||||||
throw new Error("Email and password are required.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = `${baseUrl}/api/user/register`;
|
|
||||||
const payload = { email, password };
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await axios.post(url, payload, {
|
const res = await axios.post(`${baseUrl}/api/user/register`, { email, password }, {
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.status !== 200) {
|
if (res.status === 200) {
|
||||||
throw new Error(`Registration failed. Status: ${res.status}`);
|
console.log(`Registration successful for ${email}`);
|
||||||
|
return res.data.message || "Registration completed successfully.";
|
||||||
}
|
}
|
||||||
|
throw new Error(`Registration failed. Status: ${res.status}`);
|
||||||
console.log(`Registration successful for ${email}`);
|
|
||||||
return res.data.message || "Registration completed successfully.";
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Registration error for ${email}:`, error.message);
|
console.error(`Registration error for ${email}:`, error.message);
|
||||||
throw error;
|
throw error;
|
||||||
|
|
@ -73,21 +62,14 @@ async function register(baseUrl, email, password) {
|
||||||
*/
|
*/
|
||||||
export async function attemptLoginOrRegister(baseUrl, username, password) {
|
export async function attemptLoginOrRegister(baseUrl, username, password) {
|
||||||
try {
|
try {
|
||||||
const token = await login(baseUrl, username, password);
|
return await login(baseUrl, username, password);
|
||||||
console.log(`User successfully logged in: ${username}`);
|
|
||||||
return token;
|
|
||||||
} catch (loginError) {
|
} catch (loginError) {
|
||||||
console.log(`Login failed for ${username}. Attempting registration...`);
|
console.log(`Login failed for ${username}. Attempting registration...`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const registerResponse = await register(baseUrl, username, password);
|
await register(baseUrl, username, password);
|
||||||
console.log(`User successfully registered: ${username}`);
|
return await login(baseUrl, username, password);
|
||||||
|
|
||||||
const token = await login(baseUrl, username, password);
|
|
||||||
console.log(`User successfully logged in after registration: ${username}`);
|
|
||||||
return token;
|
|
||||||
} catch (registerError) {
|
} catch (registerError) {
|
||||||
console.error(`Registration failed for ${username}:`, registerError.message);
|
console.error(`Registration and login failed for ${username}:`, registerError.message);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -100,28 +82,76 @@ export async function attemptLoginOrRegister(baseUrl, username, password) {
|
||||||
* @returns {Promise<object>} - The created room object if successful.
|
* @returns {Promise<object>} - The created room object if successful.
|
||||||
*/
|
*/
|
||||||
export async function createRoomContainer(baseUrl, token) {
|
export async function createRoomContainer(baseUrl, token) {
|
||||||
if (!token) {
|
if (!token) throw new Error("Authorization token is required.");
|
||||||
throw new Error("Authorization token is required.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = `${baseUrl}/api/room`;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await axios.post(url, {}, {
|
const res = await axios.post(`${baseUrl}/api/room`, {}, {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization: `Bearer ${token}`,
|
Authorization: `Bearer ${token}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.status !== 200) {
|
if (res.status === 200) return res.data;
|
||||||
throw new Error(`Room creation failed. Status: ${res.status}`);
|
throw new Error(`Room creation failed. Status: ${res.status}`);
|
||||||
}
|
|
||||||
|
|
||||||
//console.log("Room successfully created:", res.data);
|
|
||||||
return res.data;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Room creation error:", error.message);
|
console.error("Room creation error:", error.message);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Captures resource usage from multiple containers via WebSocket.
|
||||||
|
* @param {string} baseUrl - The base URL of the API.
|
||||||
|
* @param {string[]} roomIds - List of room IDs.
|
||||||
|
* @param {number} interval - Time interval between captures (ms).
|
||||||
|
* @param {function} shouldStop - Callback to determine if capturing should stop.
|
||||||
|
* @param {object} metrics - Metrics object to store resource usage.
|
||||||
|
*/
|
||||||
|
export async function captureResourceUsageForContainers(baseUrl, roomIds, interval, shouldStop, metrics) {
|
||||||
|
console.log("Starting resource usage capture...");
|
||||||
|
|
||||||
|
const sockets = {};
|
||||||
|
const resourceData = {};
|
||||||
|
|
||||||
|
// Initialize WebSocket connections for each room
|
||||||
|
roomIds.forEach((id) => {
|
||||||
|
resourceData[id] = [];
|
||||||
|
const socket = io(baseUrl, {
|
||||||
|
path: `/api/room/${id}/socket`,
|
||||||
|
transports: ["websocket"],
|
||||||
|
autoConnect: true,
|
||||||
|
reconnection: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("connect", () => console.log(`Connected to room ${id}`));
|
||||||
|
socket.on("connect_error", (err) => console.error(`Connection error for room ${id}:`, err.message));
|
||||||
|
sockets[id] = socket;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Capture resource usage periodically
|
||||||
|
while (!shouldStop()) {
|
||||||
|
for (const id of roomIds) {
|
||||||
|
const socket = sockets[id];
|
||||||
|
if (socket?.connected) {
|
||||||
|
try {
|
||||||
|
socket.emit("get-usage");
|
||||||
|
socket.once("usage-data", (data) => {
|
||||||
|
resourceData[id].push({ timestamp: Date.now(), ...data });
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Error capturing metrics for room ${id}:`, error.message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn(`Socket not connected for room ${id}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, interval));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close all WebSocket connections
|
||||||
|
Object.values(sockets).forEach((socket) => socket.close());
|
||||||
|
console.log("Resource usage capture completed.");
|
||||||
|
|
||||||
|
metrics.resourceUsage = resourceData;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,227 @@
|
||||||
import fs from 'fs';
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import { ChartJSNodeCanvas } from "chartjs-node-canvas";
|
||||||
|
|
||||||
/**
|
// Ensure a directory exists, creating it if necessary
|
||||||
* Écrit les métriques dans un fichier JSON.
|
function ensureDirectoryExists(directory) {
|
||||||
* @param {string} filename - Nom du fichier où écrire les métriques.
|
try {
|
||||||
* @param {Object} metrics - Objet contenant les métriques à enregistrer.
|
fs.mkdirSync(directory, { recursive: true });
|
||||||
*/
|
} catch (err) {
|
||||||
|
console.error(`Error creating directory ${directory}:`, err.message);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write metrics to a JSON file
|
||||||
export function writeMetricsToFile(metrics) {
|
export function writeMetricsToFile(metrics) {
|
||||||
|
if (!metrics.endTime) {
|
||||||
|
console.error("Error: metrics.endTime is not defined. Ensure it is set before calling this function.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const directory = `./metrics/${metrics.startTime.toISOString().replace(/[:.]/g, "-")}`;
|
||||||
|
const filename = path.join(directory, "metrics_report.json");
|
||||||
|
|
||||||
|
// Ensure the directory exists
|
||||||
|
ensureDirectoryExists(directory);
|
||||||
|
|
||||||
const metricsData = {
|
const metricsData = {
|
||||||
...metrics,
|
summary: {
|
||||||
startTime: metrics.startTime?.toISOString(),
|
roomsCreated: metrics.roomsCreated,
|
||||||
endTime: metrics.endTime?.toISOString(),
|
roomsFailed: metrics.roomsFailed,
|
||||||
executionTime: metrics.endTime && metrics.startTime
|
teachersConnected: metrics.teachersConnected,
|
||||||
? (metrics.endTime - metrics.startTime) / 1000
|
teachersFailed: metrics.teachersFailed,
|
||||||
: null,
|
studentsConnected: metrics.studentsConnected,
|
||||||
memoryUsage: process.memoryUsage(),
|
studentsFailed: metrics.studentsFailed,
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
messagesSent: metrics.messagesSent,
|
||||||
|
messagesReceived: metrics.messagesReceived,
|
||||||
|
throughput: metrics.throughput.toFixed(2), // Messages per second
|
||||||
|
},
|
||||||
|
latencies: {
|
||||||
|
totalLatency: metrics.totalLatency,
|
||||||
|
averageLatency: metrics.teachersConnected + metrics.studentsConnected
|
||||||
|
? (metrics.totalLatency / (metrics.teachersConnected + metrics.studentsConnected)).toFixed(2)
|
||||||
|
: null,
|
||||||
|
maxLatency: metrics.maxLatency,
|
||||||
|
minLatency: metrics.minLatency,
|
||||||
|
},
|
||||||
|
timing: {
|
||||||
|
startTime: metrics.startTime?.toISOString(),
|
||||||
|
endTime: metrics.endTime?.toISOString(),
|
||||||
|
executionTimeInSeconds: metrics.endTime && metrics.startTime
|
||||||
|
? (metrics.endTime - metrics.startTime) / 1000
|
||||||
|
: null,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
fs.writeFile(`metrics_report_${Date.now()}`, JSON.stringify(metricsData, null, 4), (err) => {
|
// Write metrics to a file
|
||||||
|
fs.writeFile(filename, JSON.stringify(metricsData, null, 4), (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error('Erreur lors de l\'écriture des métriques dans le fichier :', err.message);
|
console.error(`Error writing metrics to file:`, err.message);
|
||||||
} else {
|
} else {
|
||||||
console.log(`Métriques enregistrées dans le fichier metrics_report_${Date.now()}.`);
|
console.log(`Metrics saved to file: ${filename}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate charts for resource data
|
||||||
|
export async function generateGraphs(resourceData, metrics) {
|
||||||
|
if (!metrics.endTime) {
|
||||||
|
console.error("Error: metrics.endTime is not defined. Ensure it is set before calling this function.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const directory = `./metrics/${metrics.startTime.toISOString().replace(/[:.]/g, "-")}`;
|
||||||
|
ensureDirectoryExists(directory);
|
||||||
|
|
||||||
|
const chartJSNodeCanvas = new ChartJSNodeCanvas({ width: 800, height: 600 });
|
||||||
|
|
||||||
|
// Aggregated data for all containers
|
||||||
|
const aggregatedTimestamps = [];
|
||||||
|
const aggregatedMemoryUsage = [];
|
||||||
|
const aggregatedCpuUserPercentage = [];
|
||||||
|
const aggregatedCpuSystemPercentage = [];
|
||||||
|
|
||||||
|
// Generate charts for individual containers
|
||||||
|
for (const [roomId, data] of Object.entries(resourceData)) {
|
||||||
|
if (!data || !data.length) {
|
||||||
|
console.warn(`No data available for room ${roomId}. Skipping individual charts.`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract data
|
||||||
|
const timestamps = data.map((point) => new Date(point.timestamp).toLocaleTimeString());
|
||||||
|
const memoryUsage = data.map((point) => (point.memory.rss || 0) / (1024 * 1024)); // MB
|
||||||
|
const cpuUserPercentage = data.map((point) => point.cpu.userPercentage || 0);
|
||||||
|
const cpuSystemPercentage = data.map((point) => point.cpu.systemPercentage || 0);
|
||||||
|
|
||||||
|
// Update aggregated data
|
||||||
|
data.forEach((point, index) => {
|
||||||
|
if (!aggregatedTimestamps[index]) {
|
||||||
|
aggregatedTimestamps[index] = timestamps[index];
|
||||||
|
aggregatedMemoryUsage[index] = 0;
|
||||||
|
aggregatedCpuUserPercentage[index] = 0;
|
||||||
|
aggregatedCpuSystemPercentage[index] = 0;
|
||||||
|
}
|
||||||
|
aggregatedMemoryUsage[index] += memoryUsage[index];
|
||||||
|
aggregatedCpuUserPercentage[index] += cpuUserPercentage[index];
|
||||||
|
aggregatedCpuSystemPercentage[index] += cpuSystemPercentage[index];
|
||||||
|
});
|
||||||
|
|
||||||
|
// Memory usage chart
|
||||||
|
await saveChart(
|
||||||
|
chartJSNodeCanvas,
|
||||||
|
{
|
||||||
|
labels: timestamps,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "Memory Usage (MB)",
|
||||||
|
data: memoryUsage,
|
||||||
|
borderColor: "blue",
|
||||||
|
fill: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"Time",
|
||||||
|
"Memory Usage (MB)",
|
||||||
|
path.join(directory, `memory-usage-room-${roomId}.png`)
|
||||||
|
);
|
||||||
|
|
||||||
|
// CPU usage chart
|
||||||
|
await saveChart(
|
||||||
|
chartJSNodeCanvas,
|
||||||
|
{
|
||||||
|
labels: timestamps,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "CPU User Usage (%)",
|
||||||
|
data: cpuUserPercentage,
|
||||||
|
borderColor: "red",
|
||||||
|
fill: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "CPU System Usage (%)",
|
||||||
|
data: cpuSystemPercentage,
|
||||||
|
borderColor: "orange",
|
||||||
|
fill: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"Time",
|
||||||
|
"CPU Usage (%)",
|
||||||
|
path.join(directory, `cpu-usage-room-${roomId}.png`)
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`Charts generated for room ${roomId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure aggregated data is not empty
|
||||||
|
if (!aggregatedTimestamps.length) {
|
||||||
|
console.error("Error: Aggregated data is empty. Verify container data.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aggregated memory usage chart
|
||||||
|
await saveChart(
|
||||||
|
chartJSNodeCanvas,
|
||||||
|
{
|
||||||
|
labels: aggregatedTimestamps,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "Total Memory Usage (MB)",
|
||||||
|
data: aggregatedMemoryUsage,
|
||||||
|
borderColor: "blue",
|
||||||
|
fill: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"Time",
|
||||||
|
"Memory Usage (MB)",
|
||||||
|
path.join(directory, "aggregated-memory-usage.png")
|
||||||
|
);
|
||||||
|
|
||||||
|
// Aggregated CPU usage chart
|
||||||
|
await saveChart(
|
||||||
|
chartJSNodeCanvas,
|
||||||
|
{
|
||||||
|
labels: aggregatedTimestamps,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "Total CPU User Usage (%)",
|
||||||
|
data: aggregatedCpuUserPercentage,
|
||||||
|
borderColor: "red",
|
||||||
|
fill: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Total CPU System Usage (%)",
|
||||||
|
data: aggregatedCpuSystemPercentage,
|
||||||
|
borderColor: "orange",
|
||||||
|
fill: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"Time",
|
||||||
|
"CPU Usage (%)",
|
||||||
|
path.join(directory, "aggregated-cpu-usage.png")
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("Aggregated charts generated.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to save a chart
|
||||||
|
async function saveChart(chartJSNodeCanvas, data, xLabel, yLabel, outputFile) {
|
||||||
|
const chartConfig = {
|
||||||
|
type: "line",
|
||||||
|
data,
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
x: { title: { display: true, text: xLabel } },
|
||||||
|
y: { title: { display: true, text: yLabel } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const chartBuffer = await chartJSNodeCanvas.renderToBuffer(chartConfig);
|
||||||
|
fs.writeFileSync(outputFile, chartBuffer);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue