254 lines
9.1 KiB
JavaScript
254 lines
9.1 KiB
JavaScript
/**
|
|
* 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 };
|