mirror of
https://github.com/arthur-pbty/visio.git
synced 2026-06-03 23:36:40 +02:00
feat: add settings page for user display name and implement local storage hook
feat: create SocketContext for managing socket connections feat: implement useLocalStorage hook for persistent state management feat: set up SQLite database with room management functions feat: add utility functions for generating room IDs and formatting dates
This commit is contained in:
@@ -0,0 +1,227 @@
|
||||
const { createServer } = require('http');
|
||||
const { parse } = require('url');
|
||||
const next = require('next');
|
||||
const { Server } = require('socket.io');
|
||||
const Database = require('better-sqlite3');
|
||||
const path = require('path');
|
||||
|
||||
const dev = process.env.NODE_ENV !== 'production';
|
||||
const hostname = 'localhost';
|
||||
const port = parseInt(process.env.PORT || '3000', 10);
|
||||
|
||||
const app = next({ dev, hostname, port });
|
||||
const handle = app.getRequestHandler();
|
||||
|
||||
// Connexion à la base de données SQLite
|
||||
const dbPath = path.join(process.cwd(), 'visio.db');
|
||||
const db = new Database(dbPath);
|
||||
|
||||
// Fonction pour nettoyer les salles inactives dans la DB
|
||||
function cleanupInactiveRoomsInDB() {
|
||||
const stmt = db.prepare(`
|
||||
UPDATE rooms SET is_active = 0
|
||||
WHERE is_active = 1
|
||||
AND datetime(last_activity, '+5 minutes') < datetime('now')
|
||||
`);
|
||||
const result = stmt.run();
|
||||
if (result.changes > 0) {
|
||||
console.log(`Nettoyage: ${result.changes} salle(s) inactive(s) fermée(s)`);
|
||||
}
|
||||
return result.changes;
|
||||
}
|
||||
|
||||
// Stockage des salles et participants en mémoire
|
||||
const rooms = new Map();
|
||||
const roomTimers = new Map();
|
||||
|
||||
// Durée d'inactivité avant fermeture (5 minutes)
|
||||
const INACTIVITY_TIMEOUT = 5 * 60 * 1000;
|
||||
|
||||
app.prepare().then(() => {
|
||||
// Nettoyer les salles inactives au démarrage
|
||||
console.log('Nettoyage des salles inactives au démarrage...');
|
||||
cleanupInactiveRoomsInDB();
|
||||
|
||||
// Lancer un nettoyage périodique toutes les minutes
|
||||
setInterval(() => {
|
||||
cleanupInactiveRoomsInDB();
|
||||
}, 60 * 1000);
|
||||
|
||||
const httpServer = createServer((req, res) => {
|
||||
const parsedUrl = parse(req.url, true);
|
||||
handle(req, res, parsedUrl);
|
||||
});
|
||||
|
||||
const io = new Server(httpServer, {
|
||||
cors: {
|
||||
origin: '*',
|
||||
methods: ['GET', 'POST']
|
||||
}
|
||||
});
|
||||
|
||||
// Fonction pour réinitialiser le timer d'inactivité
|
||||
function resetRoomTimer(roomId) {
|
||||
if (roomTimers.has(roomId)) {
|
||||
clearTimeout(roomTimers.get(roomId));
|
||||
}
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
const room = rooms.get(roomId);
|
||||
if (room && room.participants.size === 0) {
|
||||
console.log(`Fermeture de la salle ${roomId} pour inactivité`);
|
||||
io.to(roomId).emit('room-closed');
|
||||
rooms.delete(roomId);
|
||||
roomTimers.delete(roomId);
|
||||
|
||||
// Marquer la salle comme fermée dans la DB
|
||||
fetch(`http://localhost:${port}/api/rooms/${roomId}`, {
|
||||
method: 'DELETE'
|
||||
}).catch(console.error);
|
||||
}
|
||||
}, INACTIVITY_TIMEOUT);
|
||||
|
||||
roomTimers.set(roomId, timer);
|
||||
}
|
||||
|
||||
io.on('connection', (socket) => {
|
||||
console.log('Nouvelle connexion:', socket.id);
|
||||
|
||||
let currentRoom = null;
|
||||
let currentUser = null;
|
||||
|
||||
socket.on('join-room', ({ roomId, userName }) => {
|
||||
currentRoom = roomId;
|
||||
currentUser = { id: socket.id, name: userName, videoOff: false, muted: false };
|
||||
|
||||
// Rejoindre la room Socket.io
|
||||
socket.join(roomId);
|
||||
|
||||
// Initialiser ou récupérer la salle
|
||||
if (!rooms.has(roomId)) {
|
||||
rooms.set(roomId, {
|
||||
participants: new Map()
|
||||
});
|
||||
}
|
||||
|
||||
const room = rooms.get(roomId);
|
||||
|
||||
// Envoyer la liste des participants existants au nouveau (avec leur état audio/vidéo)
|
||||
const existingUsers = Array.from(room.participants.values());
|
||||
socket.emit('existing-users', { users: existingUsers });
|
||||
|
||||
// Ajouter le nouveau participant
|
||||
room.participants.set(socket.id, currentUser);
|
||||
|
||||
// Notifier les autres de la nouvelle connexion
|
||||
socket.to(roomId).emit('user-joined', {
|
||||
userId: socket.id,
|
||||
userName: userName
|
||||
});
|
||||
|
||||
// Réinitialiser le timer d'inactivité
|
||||
resetRoomTimer(roomId);
|
||||
|
||||
console.log(`${userName} a rejoint la salle ${roomId}`);
|
||||
});
|
||||
|
||||
socket.on('offer', ({ offer, to }) => {
|
||||
const room = rooms.get(currentRoom);
|
||||
const fromUser = room?.participants.get(socket.id);
|
||||
|
||||
socket.to(to).emit('offer', {
|
||||
offer,
|
||||
from: socket.id,
|
||||
fromName: fromUser?.name || 'Inconnu'
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('answer', ({ answer, to }) => {
|
||||
socket.to(to).emit('answer', {
|
||||
answer,
|
||||
from: socket.id
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('ice-candidate', ({ candidate, to }) => {
|
||||
socket.to(to).emit('ice-candidate', {
|
||||
candidate,
|
||||
from: socket.id
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('name-change', ({ name }) => {
|
||||
if (currentRoom && currentUser) {
|
||||
currentUser.name = name;
|
||||
const room = rooms.get(currentRoom);
|
||||
if (room) {
|
||||
room.participants.set(socket.id, currentUser);
|
||||
}
|
||||
|
||||
socket.to(currentRoom).emit('user-name-changed', {
|
||||
userId: socket.id,
|
||||
newName: name
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('video-toggle', ({ videoOff }) => {
|
||||
if (currentRoom && currentUser) {
|
||||
currentUser.videoOff = videoOff;
|
||||
const room = rooms.get(currentRoom);
|
||||
if (room) {
|
||||
room.participants.set(socket.id, currentUser);
|
||||
}
|
||||
|
||||
socket.to(currentRoom).emit('user-video-toggle', {
|
||||
userId: socket.id,
|
||||
videoOff
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('audio-toggle', ({ muted }) => {
|
||||
if (currentRoom && currentUser) {
|
||||
currentUser.muted = muted;
|
||||
const room = rooms.get(currentRoom);
|
||||
if (room) {
|
||||
room.participants.set(socket.id, currentUser);
|
||||
}
|
||||
|
||||
socket.to(currentRoom).emit('user-audio-toggle', {
|
||||
userId: socket.id,
|
||||
muted
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Répondre aux pings des clients pour la détection de déconnexion
|
||||
socket.on('ping-server', () => {
|
||||
socket.emit('pong-server');
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
if (currentRoom) {
|
||||
const room = rooms.get(currentRoom);
|
||||
if (room) {
|
||||
room.participants.delete(socket.id);
|
||||
|
||||
// Notifier les autres
|
||||
socket.to(currentRoom).emit('user-left', {
|
||||
userId: socket.id
|
||||
});
|
||||
|
||||
console.log(`Utilisateur ${socket.id} a quitté la salle ${currentRoom}`);
|
||||
|
||||
// Si la salle est vide, démarrer le timer de fermeture
|
||||
if (room.participants.size === 0) {
|
||||
resetRoomTimer(currentRoom);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
httpServer.listen(port, () => {
|
||||
console.log(`> Serveur prêt sur http://${hostname}:${port}`);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user