const config = require('./config'); function createJsonSender(ws) { return (data) => { if (ws.readyState === 1) { ws.send(JSON.stringify(data)); } }; } function createClientHandlers(ws, sendJson) { let authenticated = false; let login = null; function getLogin() { return login; } function handleAuth(userLogin) { authenticated = true; login = userLogin; console.log(`Authenticated: ${login}`); sendJson({ type: 'auth_result', success: true }); return { type: 'system', text: `${login} joined the chat` }; } function handleAuthFail(reason) { sendJson({ type: 'auth_result', success: false, reason }); } function wrapHandler(handler) { return (raw) => { let parsed; try { parsed = JSON.parse(raw.toString()); } catch { sendJson({ type: 'error', message: 'Invalid JSON' }); return null; } if (!authenticated) { if (parsed.type === 'auth') { return handler({ type: 'auth', login: parsed.login, password: parsed.password }); } sendJson({ type: 'error', message: 'Authenticate first' }); return null; } return handler(parsed); }; } return { getLogin, handleAuth, handleAuthFail, wrapHandler }; } function formatSizeEstimate(base64Length) { return Math.ceil((base64Length * 3) / 4); } function validateFile(parsed) { if (!parsed.filename || !parsed.mime || !parsed.data) { return 'Invalid file message'; } const size = formatSizeEstimate(parsed.data.length); if (size > config.maxFileSize) { return `File too large (max ${config.maxFileSize / 1024 / 1024} MB)`; } return null; } function broadcast(server, message, excludeWs) { const json = JSON.stringify(message); server.clients.forEach(client => { if (client !== excludeWs && client.readyState === 1) { client.send(json); } }); } module.exports = { createJsonSender, createClientHandlers, validateFile, broadcast };