/** * Apply Redis helper functions * @param pubClient * @param app * @param logger */ const applyRedisHelpers = ({ pubClient, app, logger }) => { // Store session data in Redis const setSessionData = async (socketId, key, value) => { try { await pubClient.hset(`socket:${socketId}`, key, JSON.stringify(value)); // Use Redis pubClient } catch (error) { logger.log(`Error Setting Session Data for socket ${socketId}: ${error}`, "ERROR", "redis"); } }; // Retrieve session data from Redis const getSessionData = async (socketId, key) => { try { const data = await pubClient.hget(`socket:${socketId}`, key); return data ? JSON.parse(data) : null; } catch (error) { logger.log(`Error Getting Session Data for socket ${socketId}: ${error}`, "ERROR", "redis"); } }; // Clear session data from Redis const clearSessionData = async (socketId) => { try { await pubClient.del(`socket:${socketId}`); } catch (error) { logger.log(`Error Clearing Session Data for socket ${socketId}: ${error}`, "ERROR", "redis"); } }; // Store multiple session data in Redis const setMultipleSessionData = async (socketId, keyValues) => { try { // 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()); } catch (error) { logger.log(`Error Setting Multiple Session Data for socket ${socketId}: ${error}`, "ERROR", "redis"); } }; // Retrieve multiple session data from Redis const getMultipleSessionData = async (socketId, keys) => { try { 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])); } catch (error) { logger.log(`Error Getting Multiple Session Data for socket ${socketId}: ${error}`, "ERROR", "redis"); } }; const setMultipleFromArraySessionData = async (socketId, keyValueArray) => { try { // Use Redis multi/pipeline to batch the commands const multi = pubClient.multi(); keyValueArray.forEach(([key, value]) => { multi.hset(`socket:${socketId}`, key, JSON.stringify(value)); }); await multi.exec(); // Execute all queued commands } catch (error) { logger.log(`Error Setting Multiple Session Data for socket ${socketId}: ${error}`, "ERROR", "redis"); } }; // Helper function to add an item to the end of the Redis list const addItemToEndOfList = async (socketId, key, newItem) => { try { await pubClient.rpush(`socket:${socketId}:${key}`, JSON.stringify(newItem)); } catch (error) { logger.log(`Error adding item to the end of the list for socket ${socketId}: ${error}`, "ERROR", "redis"); } }; // Helper function to add an item to the beginning of the Redis list const addItemToBeginningOfList = async (socketId, key, newItem) => { try { await pubClient.lpush(`socket:${socketId}:${key}`, JSON.stringify(newItem)); } catch (error) { logger.log(`Error adding item to the beginning of the list for socket ${socketId}: ${error}`, "ERROR", "redis"); } }; // Helper function to clear a list in Redis const clearList = async (socketId, key) => { try { await pubClient.del(`socket:${socketId}:${key}`); } catch (error) { logger.log(`Error clearing list for socket ${socketId}: ${error}`, "ERROR", "redis"); } }; // Add methods to manage room users const addUserToRoom = async (room, user) => { try { await pubClient.sadd(room, JSON.stringify(user)); } catch (error) { logger.log(`Error adding user to room ${room}: ${error}`, "ERROR", "redis"); } }; const removeUserFromRoom = async (room, user) => { try { await pubClient.srem(room, JSON.stringify(user)); } catch (error) { logger.log(`Error removing user to room ${room}: ${error}`, "ERROR", "redis"); } }; const getUsersInRoom = async (room) => { try { const users = await pubClient.smembers(room); return users.map((user) => JSON.parse(user)); } catch (error) { logger.log(`Error getting users in room ${room}: ${error}`, "ERROR", "redis"); } }; const addUserSocketMapping = async (email, socketId) => { // Using a Redis set allows a user to have multiple active socket ids. console.log(`Adding socket ${socketId} to user ${email}`); return pubClient.sadd(`user:${email}:sockets`, socketId); }; const removeUserSocketMapping = async (email, socketId) => { console.log(`Removing socket ${socketId} from user ${email}`); return pubClient.srem(`user:${email}:sockets`, socketId); }; const getUserSocketMapping = async (email) => { const key = `user:${email}:sockets`; try { return await pubClient.smembers(key); } catch (error) { console.error(`Error retrieving socket IDs for ${email}:`, error); throw error; } }; const api = { setSessionData, getSessionData, clearSessionData, setMultipleSessionData, getMultipleSessionData, setMultipleFromArraySessionData, addItemToEndOfList, addItemToBeginningOfList, clearList, addUserToRoom, removeUserFromRoom, getUsersInRoom, addUserSocketMapping, removeUserSocketMapping, getUserSocketMapping }; Object.assign(module.exports, api); app.use((req, res, next) => { req.sessionUtils = api; next(); }); // Demo to show how all the helper functions work // const demoSessionData = async () => { // const socketId = "testSocketId"; // // // 1. Test setSessionData and getSessionData // await setSessionData(socketId, "field1", "Hello, Redis!"); // const field1Value = await getSessionData(socketId, "field1"); // console.log("Retrieved single field value:", field1Value); // // // 2. Test setMultipleSessionData and getMultipleSessionData // await setMultipleSessionData(socketId, { field2: "Second Value", field3: "Third Value" }); // const multipleFields = await getMultipleSessionData(socketId, ["field2", "field3"]); // console.log("Retrieved multiple field values:", multipleFields); // // // 3. Test setMultipleFromArraySessionData // await setMultipleFromArraySessionData(socketId, [ // ["field4", "Fourth Value"], // ["field5", "Fifth Value"] // ]); // // // Retrieve and log all fields // const allFields = await getMultipleSessionData(socketId, ["field1", "field2", "field3", "field4", "field5"]); // console.log("Retrieved all field values:", allFields); // // // 4. Test list functions // // Add item to the end of a Redis list // await addItemToEndOfList(socketId, "logEvents", { event: "Log Event 1", timestamp: new Date() }); // await addItemToEndOfList(socketId, "logEvents", { event: "Log Event 2", timestamp: new Date() }); // // // Add item to the beginning of a Redis list // await addItemToBeginningOfList(socketId, "logEvents", { event: "First Log Event", timestamp: new Date() }); // // // Retrieve the entire list // const logEventsData = await pubClient.lrange(`socket:${socketId}:logEvents`, 0, -1); // const logEvents = logEventsData.map((item) => JSON.parse(item)); // console.log("Log Events List:", logEvents); // // // 5. Test clearList // await clearList(socketId, "logEvents"); // console.log("Log Events List cleared."); // // // Retrieve the list after clearing to confirm it's empty // const logEventsAfterClear = await pubClient.lrange(`socket:${socketId}:logEvents`, 0, -1); // console.log("Log Events List after clearing:", logEventsAfterClear); // Should be an empty array // // // 6. Test clearSessionData // await clearSessionData(socketId); // console.log("Session data cleared."); // // // 7. Test room functions // const roomName = "testRoom"; // const user1 = { id: 1, name: "Alice" }; // const user2 = { id: 2, name: "Bob" }; // // // Add users to room // await addUserToRoom(roomName, user1); // await addUserToRoom(roomName, user2); // // // Get users in room // const usersInRoom = await getUsersInRoom(roomName); // console.log(`Users in room ${roomName}:`, usersInRoom); // // // Remove a user from room // await removeUserFromRoom(roomName, user1); // // // Get users in room after removal // const usersInRoomAfterRemoval = await getUsersInRoom(roomName); // console.log(`Users in room ${roomName} after removal:`, usersInRoomAfterRemoval); // // // Clean up: remove remaining users from room // await removeUserFromRoom(roomName, user2); // // // Verify room is empty // const usersInRoomAfterCleanup = await getUsersInRoom(roomName); // console.log(`Users in room ${roomName} after cleanup:`, usersInRoomAfterCleanup); // Should be empty // }; // if (process.env.NODE_ENV === "development") { // demoSessionData(); // } return api; }; module.exports = { applyRedisHelpers };