210 lines
7.2 KiB
JavaScript
210 lines
7.2 KiB
JavaScript
const { admin } = require("../firebase/firebase-handler");
|
|
|
|
const redisSocketEvents = ({
|
|
io,
|
|
redisHelpers: {
|
|
setSessionData,
|
|
clearSessionData,
|
|
addUserSocketMapping,
|
|
removeUserSocketMapping,
|
|
getUserSocketMapping
|
|
}, // Note: Used if we persist user to Redis
|
|
ioHelpers: { getBodyshopRoom, getBodyshopConversationRoom },
|
|
logger
|
|
}) => {
|
|
// Logging helper functions
|
|
const createLogEvent = (socket, level, message) => {
|
|
logger.log("ioredis-log-event", level, socket?.user?.email, null, { wsmessage: message });
|
|
};
|
|
|
|
// Socket Auth Middleware
|
|
const authMiddleware = async (socket, next) => {
|
|
if (!socket.handshake.auth.token) {
|
|
return next(new Error("Authentication error - no authorization token."));
|
|
}
|
|
try {
|
|
const user = await admin.auth().verifyIdToken(socket.handshake.auth.token);
|
|
socket.user = user;
|
|
// Persist the user data in Redis for this socket
|
|
await setSessionData(socket.id, "user", user);
|
|
// Store a mapping from the user's email to the socket id
|
|
// await addUserSocketMapping(user.email, socket.id);
|
|
next();
|
|
} catch (error) {
|
|
next(new Error(`Authentication error: ${error.message}`));
|
|
}
|
|
};
|
|
|
|
// Register Socket Events
|
|
const registerSocketEvents = (socket) => {
|
|
// Uncomment for further testing
|
|
// createLogEvent(socket, "debug", `Registering RedisIO Socket Events.`);
|
|
|
|
getUserSocketMapping(socket.user.email).then((socketIds) => {
|
|
console.log(socketIds);
|
|
});
|
|
|
|
// Token Update Events
|
|
const registerUpdateEvents = (socket) => {
|
|
let latestTokenTimestamp = 0;
|
|
|
|
const updateToken = async (newToken) => {
|
|
const currentTimestamp = Date.now();
|
|
latestTokenTimestamp = currentTimestamp;
|
|
|
|
try {
|
|
const user = await admin.auth().verifyIdToken(newToken, true);
|
|
if (currentTimestamp < latestTokenTimestamp) {
|
|
createLogEvent(socket, "warn", "Outdated token validation skipped.");
|
|
return;
|
|
}
|
|
socket.user = user;
|
|
// Update the session data in Redis with the new token info
|
|
// await setSessionData(socket.id, "user", user);
|
|
// Update the mapping with the user's email
|
|
// await addUserSocketMapping(user.email, socket.id);
|
|
createLogEvent(socket, "debug", `Token updated successfully for socket ID: ${socket.id}`);
|
|
socket.emit("token-updated", { success: true });
|
|
} catch (error) {
|
|
if (error.code === "auth/id-token-expired") {
|
|
createLogEvent(socket, "warn", "Stale token received, waiting for new token");
|
|
socket.emit("token-updated", { success: false, error: "Stale token." });
|
|
return;
|
|
}
|
|
createLogEvent(socket, "error", `Token update failed for socket ID: ${socket.id}, Error: ${error.message}`);
|
|
socket.emit("token-updated", { success: false, error: error.message });
|
|
socket.disconnect();
|
|
}
|
|
};
|
|
socket.on("update-token", updateToken);
|
|
};
|
|
|
|
// Room Broadcast Events
|
|
const registerRoomAndBroadcastEvents = (socket) => {
|
|
const joinBodyshopRoom = (bodyshopUUID) => {
|
|
try {
|
|
const room = getBodyshopRoom(bodyshopUUID);
|
|
socket.join(room);
|
|
// createLogEvent(socket, "debug", `Client joined bodyshop room: ${room}`);
|
|
} catch (error) {
|
|
createLogEvent(socket, "error", `Error joining room: ${error}`);
|
|
}
|
|
};
|
|
|
|
const leaveBodyshopRoom = (bodyshopUUID) => {
|
|
try {
|
|
const room = getBodyshopRoom(bodyshopUUID);
|
|
socket.leave(room);
|
|
createLogEvent(socket, "debug", `Client left bodyshop room: ${room}`);
|
|
} catch (error) {
|
|
createLogEvent(socket, "error", `Error joining room: ${error}`);
|
|
}
|
|
};
|
|
|
|
const broadcastToBodyshopRoom = (bodyshopUUID, message) => {
|
|
try {
|
|
const room = getBodyshopRoom(bodyshopUUID);
|
|
io.to(room).emit("bodyshop-message", message);
|
|
// We do not need this as these can be debugged live
|
|
// createLogEvent(socket, "debug", `Broadcast message to bodyshop ${room}`);
|
|
} catch (error) {
|
|
createLogEvent(socket, "error", `Error getting room: ${error}`);
|
|
}
|
|
};
|
|
|
|
socket.on("join-bodyshop-room", joinBodyshopRoom);
|
|
socket.on("leave-bodyshop-room", leaveBodyshopRoom);
|
|
socket.on("broadcast-to-bodyshop", broadcastToBodyshopRoom);
|
|
};
|
|
|
|
// Disconnect Events
|
|
const registerDisconnectEvents = (socket) => {
|
|
const disconnect = async () => {
|
|
// Remove session data from Redis
|
|
// await clearSessionData(socket.id);
|
|
|
|
// Remove the mapping from user email to this socket id, if available
|
|
// if (socket.user?.email) {
|
|
// await removeUserSocketMapping(socket.user.email, socket.id);
|
|
// }
|
|
|
|
// Leave all joined rooms
|
|
const rooms = Array.from(socket.rooms).filter((room) => room !== socket.id);
|
|
for (const room of rooms) {
|
|
socket.leave(room);
|
|
}
|
|
};
|
|
|
|
socket.on("disconnect", disconnect);
|
|
};
|
|
|
|
// Messaging Events
|
|
const registerMessagingEvents = (socket) => {
|
|
const joinConversationRoom = async ({ bodyshopId, conversationId }) => {
|
|
try {
|
|
const room = getBodyshopConversationRoom({ bodyshopId, conversationId });
|
|
socket.join(room);
|
|
} catch (error) {
|
|
logger.log("Failed to Join Conversation Room", "error", "io-redis", null, {
|
|
bodyshopId,
|
|
conversationId,
|
|
error: error.message,
|
|
stack: error.stack
|
|
});
|
|
}
|
|
};
|
|
const leaveConversationRoom = ({ bodyshopId, conversationId }) => {
|
|
try {
|
|
const room = getBodyshopConversationRoom({ bodyshopId, conversationId });
|
|
socket.leave(room);
|
|
} catch (error) {
|
|
logger.log("Failed to Leave Conversation Room", "error", "io-redis", null, {
|
|
bodyshopId,
|
|
conversationId,
|
|
error: error.message,
|
|
stack: error.stack
|
|
});
|
|
}
|
|
};
|
|
|
|
const conversationModified = ({ bodyshopId, conversationId, ...fields }) => {
|
|
try {
|
|
// Retrieve the room name for the conversation
|
|
const room = getBodyshopRoom(bodyshopId);
|
|
// Emit the updated data to all clients in the room
|
|
io.to(room).emit("conversation-changed", {
|
|
conversationId,
|
|
...fields
|
|
});
|
|
} catch (error) {
|
|
logger.log("Failed to handle conversation modification", "error", "io-redis", null, {
|
|
bodyshopId,
|
|
conversationId,
|
|
fields,
|
|
error: error.message,
|
|
stack: error.stack
|
|
});
|
|
}
|
|
};
|
|
|
|
socket.on("conversation-modified", conversationModified);
|
|
socket.on("join-bodyshop-conversation", joinConversationRoom);
|
|
socket.on("leave-bodyshop-conversation", leaveConversationRoom);
|
|
};
|
|
|
|
// Call Handlers
|
|
registerRoomAndBroadcastEvents(socket);
|
|
registerUpdateEvents(socket);
|
|
registerMessagingEvents(socket);
|
|
registerDisconnectEvents(socket);
|
|
};
|
|
|
|
// Associate Middleware and Handlers
|
|
io.use(authMiddleware);
|
|
io.on("connection", registerSocketEvents);
|
|
};
|
|
|
|
module.exports = {
|
|
redisSocketEvents
|
|
};
|