From 51c181dab792a1599c1b6ab1cc816bdc1bd2ab39 Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Wed, 11 Sep 2024 20:52:49 -0400 Subject: [PATCH] feature/IO-2742-redis - Checkpoint, Final cleanup of server.js Signed-off-by: Dave Richer --- server.js | 98 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 68 insertions(+), 30 deletions(-) diff --git a/server.js b/server.js index d0efb579a..b9f32f213 100644 --- a/server.js +++ b/server.js @@ -5,17 +5,11 @@ const path = require("path"); const compression = require("compression"); const cookieParser = require("cookie-parser"); const http = require("http"); -const https = require("https"); -const fs = require("fs"); const { Server } = require("socket.io"); const { createClient } = require("redis"); const { createAdapter } = require("@socket.io/redis-adapter"); const logger = require("./server/utils/logger"); -// This file offers the following exports -// redisClient: Redis client for external use -// io: Socket.IO server for external use - // Load environment variables require("dotenv").config({ path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`) @@ -96,19 +90,11 @@ const applyRoutes = (app) => { }; /** - * Main function to start the server - * @returns {Promise} + * Apply Redis to the server + * @param server + * @param app */ -const main = async () => { - const app = express(); - const port = process.env.PORT || 5000; - - let server; - - // In production or default, use HTTP - server = http.createServer(app); - logger.log(`[${process.env.NODE_ENV}] Attempting to start process on port ${port}`, "INFO", "api"); - +const applySocketIO = async (server, app) => { // Redis client setup for Pub/Sub and Key-Value Store const pubClient = createClient({ url: process.env.REDIS_URL || "redis://localhost:6379" }); const subClient = pubClient.duplicate(); @@ -128,7 +114,7 @@ const main = async () => { process.exit(0); }); - exports.io = new Server(server, { + const io = new Server(server, { path: "/ws", adapter: createAdapter(pubClient, subClient), cors: { @@ -139,39 +125,54 @@ const main = async () => { } }); - exports.redisClient = pubClient; + app.use((req, res, next) => { + req.pubClient = pubClient; + req.io = io; + next(); + }); + Object.assign(module.exports, { io, pubClient }); + + return { pubClient, io }; +}; + +/** + * Apply Redis helper functions + * @param pubClient + * @param app + */ +const applyRedisHelpers = (pubClient, app) => { // Store session data in Redis - exports.setSessionData = async (socketId, key, value) => { + const setSessionData = async (socketId, key, value) => { await pubClient.hSet(`socket:${socketId}`, key, JSON.stringify(value)); // Use Redis pubClient }; // Retrieve session data from Redis - exports.getSessionData = async (socketId, key) => { + const getSessionData = async (socketId, key) => { const data = await pubClient.hGet(`socket:${socketId}`, key); return data ? JSON.parse(data) : null; }; // Clear session data from Redis - exports.clearSessionData = async (socketId) => { + const clearSessionData = async (socketId) => { await pubClient.del(`socket:${socketId}`); }; // Store multiple session data in Redis - exports.setMultipleSessionData = async (socketId, keyValues) => { + const setMultipleSessionData = async (socketId, keyValues) => { // keyValues is expected to be an object { key1: value1, key2: value2, ... } const entries = Object.entries(keyValues).map(([key, value]) => [key, JSON.stringify(value)]); await pubClient.hSet(`socket:${socketId}`, ...entries.flat()); }; // Retrieve multiple session data from Redis - exports.getMultipleSessionData = async (socketId, keys) => { + const getMultipleSessionData = async (socketId, keys) => { const data = await pubClient.hmGet(`socket:${socketId}`, keys); // Redis returns an object with null values for missing keys, so we parse the non-null ones return Object.fromEntries(keys.map((key, index) => [key, data[index] ? JSON.parse(data[index]) : null])); }; - exports.setMultipleFromArraySessionData = async (socketId, keyValueArray) => { + const setMultipleFromArraySessionData = async (socketId, keyValueArray) => { // Use Redis multi/pipeline to batch the commands const multi = pubClient.multi(); @@ -183,7 +184,7 @@ const main = async () => { }; // Helper function to add an item to the end of the Redis list - exports.addItemToEndOfList = async (socketId, key, newItem) => { + const addItemToEndOfList = async (socketId, key, newItem) => { try { await pubClient.rPush(`socket:${socketId}:${key}`, JSON.stringify(newItem)); } catch (error) { @@ -192,7 +193,7 @@ const main = async () => { }; // Helper function to add an item to the beginning of the Redis list - exports.addItemToBeginningOfList = async (socketId, key, newItem) => { + const addItemToBeginningOfList = async (socketId, key, newItem) => { try { await pubClient.lPush(`socket:${socketId}:${key}`, JSON.stringify(newItem)); } catch (error) { @@ -200,6 +201,31 @@ const main = async () => { } }; + Object.assign(module.exports, { + setSessionData, + getSessionData, + clearSessionData, + setMultipleSessionData, + getMultipleSessionData, + setMultipleFromArraySessionData, + addItemToEndOfList, + addItemToBeginningOfList + }); + + app.use((req, res, next) => { + req.sessionUtils = { + setSessionData, + getSessionData, + clearSessionData, + setMultipleSessionData, + getMultipleSessionData, + setMultipleFromArraySessionData, + addItemToEndOfList, + addItemToBeginningOfList + }; + next(); + }); + // // Demo to show how all the helper functions work // const demoSessionData = async () => { // const socketId = "testSocketId"; @@ -251,11 +277,23 @@ const main = async () => { // }; // // if (process.env.NODE_ENV === "development") { - // await demoSessionData(); + // demoSessionData(); // } +}; +/** + * Main function to start the server + * @returns {Promise} + */ +const main = async () => { + const app = express(); + const port = process.env.PORT || 5000; + + const server = http.createServer(app); + + const { pubClient } = await applySocketIO(server, app); + applyRedisHelpers(pubClient, app); require("./server/web-sockets/web-socket"); - applyMiddleware(app); applyRoutes(app);