My usecase of socket is not for chats, it's specifically to tell the front-end what BullMQ queue events happened, with a progress bar and telling when a job is done or failed. Currently when I'm emitting events, it's going for all users, I tried to use the socket.to(socket.id).emit({ myEvent: example }) but didn't work at all. I'm storing session on Redis.
Socket
this.redisClient = new Redis(`${process.env.REDIS_URL}`);
this.sessionStore = new RedisSessionStore(this.redisClient);
this.pubClient = this.redisClient;
this.subClient = this.pubClient.duplicate();
this.instance.adapter(createAdapter(this.pubClient, this.subClient));
} catch (err) {
console.log("Error on Socket Controller: ", err.message);
}
this.instance.use(async (socket, next) => {
const sessionID = socket.handshake.auth.sessionID;
if (sessionID) {
const session = await this.sessionStore.findSession(sessionID);
if (session) {
socket.sessionID = sessionID;
socket.userID = session.userID;
socket.username = session.username;
return next();
}
}
const username = socket.handshake.auth.username;
if (!username) {
return next(new Error("invalid username"));
}
socket.sessionID = uuidv4();
socket.userID = uuidv4();
socket.username = username;
console.log(socket.sessionID, socket.userID, socket.username);
next();
});
this.instance.on("connection", async (socket) => {
// Assign socket to the class
this.socket = this.socket == null ? socket : this.socket;
let connectedUsersCount =
Object.keys(this.instance.sockets.sockets).length + 1;
let oneUserLeft = connectedUsersCount - 1;
console.log(`New client connected`, connectedUsersCount);
try {
this.sessionStore.saveSession(this.socket.sessionID, {
userID: this.socket.userID,
username: this.socket.username,
connected: true
});
// emit session details
this.socket.emit("session", {
sessionID: this.socket.sessionID,
userID: this.socket.userID
});
// join the "userID" room
this.socket.join(this.socket.userID);
const users = [];
const sessions = await this.sessionStore.findAllSessions();
sessions.forEach((session) => {
users.push({
userID: session.userID,
username: session.username,
connected: session.connected
});
});
this.socket.emit("users", users);
// notify existing users
this.socket.broadcast.emit("user connected", {
userID: this.socket.userID,
username: this.socket.username,
connected: true,
messages: []
});
integrationQueueEvents.on("progress", async (job: any) => {
try {
console.log("Job Progressing", job);
const payload = {
status: true,
data: job.data,
jobId: job.jobId,
to: this.socket.userID
};
console.log("integration progress payload: ", payload);
this.socket.emit("integrationProgress", payload);
} catch (error) {
console.log(error);
}
// this.socket.to(this.socket.id).emit("integrationProgress", payload);
});
Session Store
findSession(id) {
return this.redisClient
.hmget(`session:${id}`, "userID", "username", "connected")
.then(mapSession);
}
saveSession(id, { userID, username, connected }) {
this.redisClient
.multi()
.hset(
`session:${id}`,
"userID",
userID,
"username",
username,
"connected",
connected
)
.expire(`session:${id}`, SESSION_TTL)
.exec();
}
async findAllSessions() {
const keys = new Set();
let nextIndex = 0;
do {
const [nextIndexAsStr, results] = await this.redisClient.scan(
nextIndex,
"MATCH",
"session:*",
"COUNT",
"100"
);
nextIndex = parseInt(nextIndexAsStr, 10);
results.forEach((s) => keys.add(s));
} while (nextIndex !== 0);
const commands = [];
keys.forEach((key) => {
commands.push(["hmget", key, "userID", "username", "connected"]);
});
return await this.redisClient
.multi(commands)
.exec()
.then((results) => {
return results
.map(([err, session]) => (err ? undefined : mapSession(session)))
.filter((v) => !!v);
});
}