feature/IO-2742-redis - Checkpoint, Final cleanup of server.js

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-09-11 20:52:49 -04:00
parent 4486858a86
commit 51c181dab7

View File

@@ -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<void>}
* 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<void>}
*/
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);