diff --git a/server.js b/server.js index 3e43369..d1370f8 100644 --- a/server.js +++ b/server.js @@ -1,13 +1,21 @@ +// Import dependencies const { WebSocket, WebSocketServer } = require("ws"); +const express = require("express"); const dotenv = require("dotenv"); dotenv.config(); -const express = require("express"); - +// Setup Environmental Variables const PORT = process.env.PORT || 3000; const PREFIX = process.env.PREFIX || ""; const PUBLIC = process.env.PUBLIC || ""; +// Setup Express Router +// Create the routes of the application +// Here are two pages and a wildcard: +// at / there is the index.html page, where to draw +// at /destination there is the destination.html page, where to receive the drawings +// the wildcard /* serves the static files from the public folder + const router = express.Router(); const routes = (app) => { app.get("/", (req, res) => { @@ -26,6 +34,7 @@ const routes = (app) => { return app; }; +// Setup the Express server const server = express() .set("view engine", "html") .engine("html", require("hbs").__express) @@ -33,12 +42,19 @@ const server = express() .use(express.static("public")) .listen(PORT, () => console.log(`Listening on ${PORT}`)); +// Setup the Websocket server on top of the Express one const wss = new WebSocketServer({ server, clientTracking: true }); +// Global variables to manage the connected Destination and User clients let DESTINATIONS = new Set(); let USERS = new Set(); var theme = ""; +// The message processor defines which function is associated to every websocket message type. +// It map a type to a function, passing some optional parameters such as the message itself and the websocket client that sent it. +// for example an incoming message like {type: hello} will trigger the registerDest(ws, msg) function. +// In this way to add message types and functionalities gets easier, and avoid long chain of if-else statements. + const messageProcessor = { default: (ws, msg) => unknownMsg(msg), hello: (ws, msg) => registerDest(ws, msg), @@ -46,11 +62,16 @@ const messageProcessor = { theme: (ws, msg) => ((theme = msg.theme), broadcast(msg)), }; +// Message processor functions + +// Default function, to cactch unkown message types const unknownMsg = (msg) => { console.log("Unknown message type..."); console.log(msg); }; +// Add the ws client in the destinations set +// Removing it when it disconnets const registerDest = (ws, msg) => { console.log("Destination client connected"); DESTINATIONS.add(ws); @@ -59,14 +80,17 @@ const registerDest = (ws, msg) => { }); }; +// Send a message to all the connected Destinations const toDest = (msg) => { + let message = JSON.stringify(msg); DESTINATIONS.forEach((DESTINATION) => { if (DESTINATION?.readyState === WebSocket.OPEN) { - DESTINATION.send(JSON.stringify(msg)); + DESTINATION.send(message); } }); }; +// Send a message to all the connected Users const broadcast = (msg) => { let message = JSON.stringify(msg); for (const user of USERS.values()) { @@ -76,18 +100,21 @@ const broadcast = (msg) => { } }; +// Websocket events listener wss.on("connection", (ws) => { USERS.add(ws); ws.send(JSON.stringify({ type: "theme", theme: theme })); ws.on("message", (data) => { + // Parse the incoming data safely let message; try { message = JSON.parse(data); } catch (e) {} + // Call the message processor, eventually falling back to use the default function if the type is not defined. if (message) { - (messageProcessor[message.type] || messageProcessor.default)(ws, message); + (messageProcessor[message?.type] || messageProcessor.default)(ws, message); } });