optimize and cleanup

This commit is contained in:
MathieuSevignyLavallee 2024-11-15 19:35:41 -05:00
parent b608793ac3
commit 5c21b6a15f
6 changed files with 143 additions and 159 deletions

View file

@ -59,9 +59,6 @@ RUN chown -R nginx:nginx /var/cache/nginx \
# Verify the configuration # Verify the configuration
# RUN nginx -t --dry-run # RUN nginx -t --dry-run
# Switch to non-root user
USER nginx
# Expose HTTP port # Expose HTTP port
EXPOSE 80 EXPOSE 80

View file

@ -1,8 +1,5 @@
js_import njs/main.js; js_import njs/main.js;
js_set $room_cache_check main.checkCache;
js_set $room_cache_set main.setCache;
map $http_upgrade $connection_upgrade { map $http_upgrade $connection_upgrade {
default upgrade; default upgrade;
'' close; '' close;

View file

@ -1,8 +1,9 @@
const roomCache = new Map();
async function fetchRoomInfo(r) { async function fetchRoomInfo(r) {
try { try {
let res = await r.subrequest(`/api/room/${r.variables.room_id}`, { method: 'GET' }); // Make request to API to get room info
let res = await r.subrequest('/api/room/' + r.variables.room_id, {
method: 'GET'
});
if (res.status !== 200) { if (res.status !== 200) {
r.error(`Failed to fetch room info: ${res.status}`); r.error(`Failed to fetch room info: ${res.status}`);
@ -10,7 +11,7 @@ async function fetchRoomInfo(r) {
} }
let room = JSON.parse(res.responseText); let room = JSON.parse(res.responseText);
r.error(`Debug: Room info fetched: ${JSON.stringify(room)}`); r.error(`Debug: Room info: ${JSON.stringify(room)}`); // Debug log
return room; return room;
} catch (error) { } catch (error) {
r.error(`Error fetching room info: ${error}`); r.error(`Error fetching room info: ${error}`);
@ -18,48 +19,36 @@ async function fetchRoomInfo(r) {
} }
} }
function checkCache(r) {
let room = roomCache.get(r.variables.room_id);
if (room) {
r.error(`Cache hit for room_id: ${r.variables.room_id}`);
r.return(200, JSON.stringify(room));
} else {
r.error(`Cache miss for room_id: ${r.variables.room_id}`);
r.return(404);
}
}
function setCache(r) {
let room = JSON.parse(r.responseBody);
roomCache.set(r.variables.room_id, room);
r.error(`Cached room info: ${JSON.stringify(room)}`);
}
async function routeWebSocket(r) { async function routeWebSocket(r) {
let room = roomCache.get(r.variables.room_id); try {
const roomInfo = await fetchRoomInfo(r);
if (!room) { if (!roomInfo || !roomInfo.host) {
r.error(`Cache miss. Fetching room info for: ${r.variables.room_id}`); r.error(`Debug: Invalid room info: ${JSON.stringify(roomInfo)}`);
room = await fetchRoomInfo(r);
if (!room || !room.host) {
r.error(`Invalid room info for room_id: ${r.variables.room_id}`);
r.return(404, 'Room not found or invalid'); r.return(404, 'Room not found or invalid');
return; return;
} }
roomCache.set(r.variables.room_id, room); // Cache the result // Make sure the host includes protocol if not already present
} else { let proxyUrl = roomInfo.host;
r.error(`Cache hit for room_id: ${r.variables.room_id}`); if (!proxyUrl.startsWith('http://') && !proxyUrl.startsWith('https://')) {
proxyUrl = 'http://' + proxyUrl;
} }
let proxyUrl = room.host.startsWith('http://') || room.host.startsWith('https://') r.error(`Debug: Original URL: ${r.uri}`);
? room.host r.error(`Debug: Setting proxy target to: ${proxyUrl}`);
: `http://${room.host}`; r.error(`Debug: Headers: ${JSON.stringify(r.headersIn)}`);
r.error(`Routing WebSocket to: ${proxyUrl}`); // Set the proxy target variable
r.variables.proxy_target = proxyUrl; r.variables.proxy_target = proxyUrl;
// Redirect to the websocket proxy
r.internalRedirect('@websocket_proxy'); r.internalRedirect('@websocket_proxy');
} catch (error) {
r.error(`WebSocket routing error: ${error}`);
r.return(500, 'Internal routing error');
}
} }
export default { routeWebSocket, checkCache, setCache }; export default { routeWebSocket };

View file

@ -8,9 +8,12 @@ export class Student {
} }
connectToRoom(baseUrl) { connectToRoom(baseUrl) {
return new Promise((resolve, reject) => {
try {
this.socket = io(baseUrl, { this.socket = io(baseUrl, {
path: `/api/room/${this.roomName}/socket`, path: `/api/room/${this.roomName}/socket`,
transports: ['websocket'],autoConnect: true, transports: ['websocket'],
autoConnect: true,
reconnection: true, reconnection: true,
reconnectionAttempts: 10, reconnectionAttempts: 10,
reconnectionDelay: 10000, reconnectionDelay: 10000,
@ -18,10 +21,19 @@ export class Student {
}); });
this.socket.on('connect', () => { this.socket.on('connect', () => {
this.joinRoom(this.roomName,this.username); this.joinRoom(this.roomName, this.username);
resolve(this.socket);
}); });
return this.socket; this.socket.on('error', (error) => {
reject(new Error(`Connection error: ${error.message}`));
});
} catch (error) {
console.error(`Error connecting ${this.name} to room ${this.roomId}:`, error.message);
reject(error);
}
});
} }
joinRoom(roomName, username) { joinRoom(roomName, username) {

View file

@ -8,9 +8,12 @@ export class Teacher {
} }
connectToRoom(baseUrl) { connectToRoom(baseUrl) {
return new Promise((resolve, reject) => {
try {
this.socket = io(baseUrl, { this.socket = io(baseUrl, {
path: `/api/room/${this.roomName}/socket`, path: `/api/room/${this.roomName}/socket`,
transports: ['websocket'],autoConnect: true, transports: ['websocket'],
autoConnect: true,
reconnection: true, reconnection: true,
reconnectionAttempts: 10, reconnectionAttempts: 10,
reconnectionDelay: 10000, reconnectionDelay: 10000,
@ -19,12 +22,22 @@ export class Teacher {
this.socket.on('connect', () => { this.socket.on('connect', () => {
this.createRoom(this.roomName); this.createRoom(this.roomName);
resolve(this.socket);
});
this.socket.on('error', (error) => {
reject(new Error(`Connection error: ${error.message}`));
}); });
this.socket.on('create-success', () => { this.socket.on('create-success', () => {
}); });
return this.socket; } catch (error) {
console.error(`Error connecting ${this.name} to room ${this.roomId}:`, error.message);
reject(error);
}
});
} }
createRoom() { createRoom() {

View file

@ -3,126 +3,102 @@ import { Student } from './class/student.js';
import { Teacher } from './class/teacher.js'; import { Teacher } from './class/teacher.js';
const BASE_URL = 'http://localhost'; const BASE_URL = 'http://localhost';
const user = { const user = { username: 'admin@example.com', password: 'adminPassword' };
username: 'admin@example.com', const numberRooms = 5;
password: 'adminPassword', const studentPerRoom = 59; // Max is 60; 1 slot reserved for the teacher
};
const numberRooms = 30;
const studentPerRoom = 59; // Max is 60; 1 slot is reserved for the teacher
const roomAssociations = {}; const roomAssociations = {};
const allSockets = []; // Track all active WebSocket connections const allSockets = []; // Track all active WebSocket connections
async function createRoomsAndTeachers(token) { async function createRoomContainers(token) {
const roomCreationPromises = []; const roomCreationPromises = Array.from({ length: numberRooms }, async () => {
const teachers = []; const room = await createRoomContainer(BASE_URL, token);
for (let index = 0; index < numberRooms; index++) {
roomCreationPromises.push(
createRoomContainer(BASE_URL, token).then((room) => {
if (room?.id) { if (room?.id) {
const teacher = new Teacher(`teacher_${index}`, room.id); roomAssociations[room.id] = { teacher: null, students: [] };
teachers.push(teacher); console.log(`Created room with ID: ${room.id}`);
} else {
roomAssociations[room.id] = { console.warn('Failed to create a room.');
teacher,
students: [],
};
// Track teacher WebSocket for cleanup
if (teacher.socket) {
allSockets.push(teacher.socket);
}
}
})
);
}
await Promise.allSettled(roomCreationPromises);
console.log(`Created ${Object.keys(roomAssociations).length} rooms with associated teachers.`);
return teachers;
}
async function connectTeachersToRooms(teachers) {
const teacherConnectionPromises = teachers.map(async (teacher) => {
await teacher.connectToRoom(BASE_URL);
if (teacher.socket) {
allSockets.push(teacher.socket); // Track WebSocket
} }
}); });
await Promise.allSettled(teacherConnectionPromises); await Promise.allSettled(roomCreationPromises);
console.log('All teachers connected to their rooms.'); console.log(`Created ${Object.keys(roomAssociations).length} room containers.`);
}
async function addAndConnectTeachers() {
const teacherCreationPromises = Object.keys(roomAssociations).map(async (roomId, index) => {
const teacher = new Teacher(`teacher_${index}`, roomId);
const socket = await teacher.connectToRoom(BASE_URL);
if (socket.connected) {
allSockets.push(socket);
roomAssociations[roomId].teacher = teacher;
console.log(`Teacher ${teacher.username} connected to room ${roomId}.`);
} else {
console.warn(`Failed to connect teacher_${index} to room ${roomId}`);
}
});
await Promise.allSettled(teacherCreationPromises);
console.log('All teachers added and connected to their respective rooms.');
} }
async function addAndConnectStudents() { async function addAndConnectStudents() {
const studentCreationPromises = []; const studentCreationPromises = Object.entries(roomAssociations).flatMap(([roomId, association], roomIndex) =>
Array.from({ length: studentPerRoom }, async (_, i) => {
Object.entries(roomAssociations).forEach(([roomId, association], roomIndex) => {
for (let i = 0; i < studentPerRoom; i++) {
const student = new Student(`student_${roomIndex}_${i}`, roomId); const student = new Student(`student_${roomIndex}_${i}`, roomId);
association.students.push(student); const socket = await student.connectToRoom(BASE_URL);
studentCreationPromises.push( if (socket.connected) {
student.connectToRoom(BASE_URL).then(() => { allSockets.push(socket);
if (student.socket) { association.students.push(student);
allSockets.push(student.socket); // Track WebSocket console.log(`Student ${student.username} connected to room ${roomId}.`);
} else {
console.warn(`Failed to connect student_${roomIndex}_${i} to room ${roomId}`);
} }
}) })
); );
}
});
await Promise.allSettled(studentCreationPromises); await Promise.allSettled(studentCreationPromises);
console.log('All students connected to their respective rooms.'); console.log('All students added and connected to their respective rooms.');
} }
function closeAllSockets() { function closeAllSockets() {
console.log('Closing all WebSocket connections...'); console.log('Closing all Socket.IO connections...');
allSockets.forEach((socket) => { allSockets.forEach((socket) => {
if (socket && socket.connected) {
try { try {
if (socket.readyState === socket.OPEN) { socket.disconnect();
socket.close(); // Gracefully close the WebSocket console.log('Disconnected Socket.IO connection.');
console.log('Closed WebSocket connection.');
}
} catch (error) { } catch (error) {
console.error('Error closing WebSocket:', error.message); console.error('Error disconnecting Socket.IO socket:', error.message);
}
} }
}); });
console.log('All WebSocket connections closed.'); console.log('All Socket.IO connections disconnected.');
} }
async function main() { async function main() {
try { try {
const token = await attemptLoginOrRegister(BASE_URL, user.username, user.password); const token = await attemptLoginOrRegister(BASE_URL, user.username, user.password);
if (!token) { if (!token) throw new Error('Failed to log in.');
console.error('Failed to log in. Exiting...');
return;
}
const teachers = await createRoomsAndTeachers(token); await createRoomContainers(token);
await connectTeachersToRooms(teachers); await addAndConnectTeachers();
await addAndConnectStudents(); await addAndConnectStudents();
console.log('All tasks completed.'); console.log('All tasks completed.');
} catch (error) { } catch (error) {
console.error('An error occurred:', error.message); console.error('An error occurred:', error.message);
} finally {
closeAllSockets();
} }
} }
// Handle script termination (Ctrl+C) // Handle script termination and exit
process.on('SIGINT', () => { process.on('SIGINT', () => {
console.log('Script interrupted (Ctrl+C).'); console.log('Script interrupted (Ctrl+C).');
closeAllSockets(); closeAllSockets();
process.exit(0); // Exit cleanly process.exit(0);
}); });
// Handle script exit process.on('exit', closeAllSockets);
process.on('exit', () => {
closeAllSockets();
});
main(); main();