// Global logging toggles // const fileLogging = false; const consoleLogging = true; // Imports // const fs = require('fs'); const http = require('http'); const express = require('express'); // Global server setup const app = express(); const clientPath = __dirname+'/../html'; console.log('Serving static from ' + clientPath); app.use(express.json()); app.use(express.static(clientPath)); const server = http.createServer(app); // Check if string is already in array function isStringUnique(arr, str) { let match = true; for (const s of arr) if (s === str) { match = false; break; } return match; } // Generate the partial ID after username function generatePartialID() { return Math.floor(Math.random()*10000).toString().padStart(4, '0'); } // User data const users = []; // Incoming user connection app.post('/connect', (req, res) => { // Generate id, save user const { name } = req.body; let id = generatePartialID(); const name_id = users.map(u => `${u.name}#${u.id}`); while (!isStringUnique(name_id, `${name}#${id}`)) id = generatePartialID(); users.push({ socket: req.socket, date: new Date(), chats: {}, name, id }); // Log connection, send back id if (consoleLogging) console.log(`User with ID '${name}#${id}' connected.`); res.status(200).json({ id }); }); // User has disconnected app.post('/disconnect', (req, res) => { const { name, id } = req.body; for (let i = 0; i < users.length; i++) if (users[i].name === name && users[i].id === id) { // If id is valid, delete all user data if (consoleLogging) console.log(`User with ID '${name}#${id}' disconnected.`); users.splice(i, 1); break; } // Confirm disconnect res.status(200).send(); }); // User wants to measure ping app.get('/ping', (req, res) => { res.status(200).send(); }); // User wants to change name app.post('/nickname', (req, res) => { const { oldName, name, id } = req.body; // Check if name is taken, if not, append id until unique const name_id = users.map(u => `${u.name}#${u.id}`); let currentID = id; while (!isStringUnique(name_id, `${name}#${currentID}`)) currentID = generatePartialID(); // Change username for (const u of users) if (u.name === oldName && u.id === id) { if (consoleLogging) console.log(`(Re-)named user with ID '${oldName}#${id}'.`); u.name = name; u.id = currentID; break; } // Confirm renaming res.status(200).json({ id: currentID }); }); // User is pulling messages app.post('/getChat', (req, res) => { const { name, id } = req.body; // Go through users' chats, retrieve messages, save by username let chats = {}; const fullName = `${name}#${id}`; for (const u of users) if (fullName in u.chats) { const len = u.chats[fullName].length; if (len > 0) chats[`${u.name}#${u.id}`] = u.chats[fullName].splice(0, len); } // No chat messages were found if (Object.keys(chats).length === 0) { res.status(204).send(); return; } // Log if messages were delivered if (consoleLogging) console.log(`Delivered messages to user with ID '${fullName}'.`); // Send back data res.status(200).json(chats); }); // User sent new message app.post('/message', (req, res) => { const { name, id, recipient, message } = req.body; // Search for senders' name, save message for (const u of users) if (u.name === name && u.id === id) { if (!u.chats[recipient]) u.chats[recipient] = []; u.chats[recipient].push(message); break; } // Log message for debugging if (consoleLogging) console.log(`User '${name}#${id}' sent message '${message}'.`); // Confirm process res.status(200).send(); }); // User requesting all names app.get('/getNames', (req, res) => { if (consoleLogging) console.log('Some user requested all names.'); res.status(200).json(users.map(u => { return { name: u.name, id: u.id }; })); }); // Internal server error server.on('error', err => { console.error('Internal server error', err); }); // Server has closed server.on('close', () => { console.log('Shutting down server ...'); }); // Listen on port 3000 for webserver connection server.listen(3000, () => { console.log('Server running on port 3000'); });