const { WebSocketServer } = require('ws'); const config = require('./config'); const { authenticate } = require('./auth'); const { createJsonSender, createClientHandlers, validateFile, broadcast } = require('./clients'); const server = new WebSocketServer({ port: config.port }); console.log(`WebSocket server running on ws://0.0.0.0:${config.port}`); let pinnedMessage = null; server.on('connection', (ws, req) => { const clientIp = req.socket.remoteAddress; console.log(`Connection from ${clientIp}`); const send = createJsonSender(ws); const client = createClientHandlers(ws, send); const handle = client.wrapHandler((parsed) => { switch (parsed.type) { case 'auth': { const user = authenticate(parsed.login, parsed.password); if (user) { const sysMsg = client.handleAuth(user.login); if (pinnedMessage) { send({ type: 'pinned', message: pinnedMessage.message, pinnedBy: pinnedMessage.from }); } return sysMsg; } client.handleAuthFail('Invalid login or password'); return null; } case 'pin': { pinnedMessage = { type: 'pinned', from: client.getLogin(), timestamp: Date.now(), message: parsed.message, }; console.log(`${client.getLogin()} pinned a message`); return pinnedMessage; } case 'unpin': { pinnedMessage = null; console.log(`${client.getLogin()} unpinned the message`); return { type: 'unpinned', from: client.getLogin(), timestamp: Date.now() }; } case 'text': { if (!parsed.text || typeof parsed.text !== 'string') { send({ type: 'error', message: 'Invalid text message' }); return null; } console.log(`Text from ${client.getLogin()}: ${parsed.text.substring(0, 50)}`); return { type: 'text', from: client.getLogin(), timestamp: Date.now(), text: parsed.text }; } case 'file': { const error = validateFile(parsed); if (error) { send({ type: 'error', message: error }); return null; } console.log(`File from ${client.getLogin()}: ${parsed.filename} (${parsed.mime})`); return { type: 'file', from: client.getLogin(), timestamp: Date.now(), filename: parsed.filename, mime: parsed.mime, data: parsed.data, }; } default: send({ type: 'error', message: 'Unknown message type' }); return null; } }); ws.on('message', (raw) => { const msg = handle(raw); if (msg) { broadcast(server, msg, null); } }); ws.on('close', (code) => { console.log(`Disconnected: ${client.getLogin() || 'unauthenticated'} (code: ${code})`); if (client.getLogin()) { broadcast(server, { type: 'system', text: `${client.getLogin()} left the chat` }, null); } }); ws.on('error', (err) => { console.error(`Socket error (${client.getLogin() || 'unknown'}):`, err.message); }); });