I work at a chat application. I want to store messages in an object like this :
{
'room1': ['msg1', 'msg2', ...]
'room2': ['msg3', 'msg4', ...]
...
}
I defined a variable in my socket.io server roomMessages
the problem is when I want to add a message in a specific room using roomMessages[room].push(data); it shows me this error: TypeError: Cannot read properties of undefined (reading 'push')
this is my socket.io code:
const roomMessages = {};
roomsNamespace.use((socket, next) => {
const token = socket.handshake.query.token;
console.log(token);
if (!token) return next(new Error("not auth"));
next();
});
roomsNamespace.on("connection", (socket) => {
const token = socket.handshake.query.token;
const apiService = new ApiService(process.env.BE_URL, token);
const room = socket.handshake.query.room;
socket.on("join-room", async () => {
socket.join(room);
await apiService.joinRoom(room);
const usersInThisRoom = await apiService.getUsersInRoom(room);
roomsNamespace.in(room).emit("all-users", usersInThisRoom);
socket.emit("room-messages", roomMessages[room]);
});
socket.on("chat-message", (data) => {
// console.log(data);
if (roomMessages[room]) roomMessages[room].push(data);
roomsNamespace.in(room).emit("chat-message", data);
});
basically, I want to send previous messages when user joins the chat
How to fix this error?
you can do something like this
const roomMessages = {};
const getMessages = (room) => roomMessages[room] || []
const addMessage = (room, message) => {
roomMessages[room] = [...getMessages(room), message]
}
roomsNamespace.use((socket, next) => {
const token = socket.handshake.query.token;
console.log(token);
if (!token) return next(new Error("not auth"));
next();
});
roomsNamespace.on("connection", (socket) => {
const token = socket.handshake.query.token;
const apiService = new ApiService(process.env.BE_URL, token);
const room = socket.handshake.query.room;
socket.on("join-room", async () => {
socket.join(room);
await apiService.joinRoom(room);
const usersInThisRoom = await apiService.getUsersInRoom(room);
roomsNamespace.in(room).emit("all-users", usersInThisRoom);
socket.emit("room-messages", getMessages(room));
});
socket.on("chat-message", (data) => {
// console.log(data);
addMessage(room, data)
roomsNamespace.in(room).emit("chat-message", data);
});
in this way if you want later you can change your implementation of messageRepository storing data in redis or wherever you want
Related
I am creating a blog application. In every parameterized route I get the details of that specific user.For eg: /profile/#randomuser get the details of #randomusers, /profile/#robert get the details of #robert. I get the details of parameterized toute users but not the user who log in to the blog application.
Everything is imported correctly.
<Route path="/profile/:profile" element={\<Profile /\>}
Profile.jsx
const [user, getUser] = useState([])
const [glbUser, setUser] = useContext(UserContext)
const match = useParams().profile
const match = useMatch('/profile/:profile')
const userMatch = match.params.profile
console.log(" usematchis ", userMatch)
const userParams = useParams().profile
useEffect(() => {
async function fetchGlbUser() {
const loggedInUser = window.localStorage.getItem('userToken')
if (loggedInUser) {
const user = JSON.parse(loggedInUser)
loginServices.setToken(user.token)
// loginServices.createToken()
const config = { headers: { Authorization: user.token } }
const glbUser = await axios.get("${postUrl}", config)
setUser(glbUser.data.glbUserToken)
return glbUser.data.glbUserToken
}
}
fetchGlbUser()
}, [])
// console.log("Match is", match)
// console.log("Type of Match is", typeof match.params.profile)
useEffect(() =\> {
axios.get("http://localhost:9000/api/users/${userMatch}", { params: { profile: userMatch } })
.then(res =\> {
console.log(res.data)
getUser(res.data)
// getUser(res.data.newUser)
})
.catch(err => console.log(err))
// const getUserData = async () => {
// const res = loginServices.getProfile(`http://localhost:9000/api/users/${userMatch}`, {params:{profile:userMatch}})
// return res.data
// }
// getUserData()
}, [])
Backend.js
router.get('/:profile', async (req, res) =\> {
console.log(req)
const username = req.params.profile
const decodedToken = jwt.verify(getToken(req), process.env.SECRET_KEY)
// console.log(username)
// console.log(typeof username)
try {
const newUser = await User.findOne({ username })
const glbUserToken = await User.findById(decodedToken.id)
// console.log(newUser)
res.status(200).json({ newUser, glbUserToken })
} catch (err) {
console.log(err)
}
})
const getToken = req =>
const auth = req.get('authorization')
if (auth && auth.startsWith(`bearer `))
return auth.replace('bearer ', '') }
return null
}
I am using Nest.Js and Socket.io on a server and React with Next in order to make a chat app.
But the problem is when i try to get some messages from the server i need to reconnect (If I connect for the first time handlers do not emit anything). And I think the problem is in the server (because I tried it also in Postman and the same problem is there).
Here is the code
async handleConnection(socket: Socket) {
this.server.once('connection', async (socket) => {
const token = socket.handshake.auth.token;
if (!token) {
socket.disconnect();
return;
}
const user = await this.authService.verifyAndReturnUser(token);
if (!user) {
console.log('USER IS NOT VALID');
socket.disconnect();
return;
}
// Set a userId in socket data
socket.data.userId = user.id;
// Get the rooms (chats) of the user
const userChats = await this.chatService.getUserChats(user.id);
//Emitting the rooms (chats)
socket.emit('getChats', userChats);
socket.on('joinRoom', async (data: { user: string; item?: string }) => {
// This code does not work on first connect (even the client commits)
const forwardedId = Number(data.user);
const forwardedItemId = Number(data.item);
const isForwardedNaN = Number.isNaN(forwardedId);
const isItemNan = Number.isNaN(forwardedItemId);
if (forwardedId == user.id) {
console.log('disconnect ID IS THE SAME');
socket.disconnect();
return;
}
if (!forwardedId || isForwardedNaN) {
console.log(forwardedId);
socket.disconnect();
return;
}
socket.data.forwardedId = forwardedId;
// Get forwarded info info
const getUser = await this.userService.getProfile(forwardedId);
// Get a time info
if (!getUser) {
socket.disconnect();
return;
}
const getCurrentRoom = await this.chatService.getCurrentRoom(
socket.data.forwardedId,
user.id,
);
if (getCurrentRoom) {
if (!getUser) {
socket.disconnect();
return;
}
}
socket.data.room = getCurrentRoom;
// Disconnect from all previous rooms
socket.rooms.forEach(async (room) => {
if (room) {
await socket.leave(room);
}
});
await socket.join(String(getCurrentRoom));
console.log(forwardedItemId, isItemNan);
// Set item to room
if (data.item) {
if (isItemNan) {
socket.disconnect();
return;
}
const room = await this.chatService.setItemToRoom(
getCurrentRoom,
forwardedId,
forwardedItemId,
);
// If there is no room updated disconnect
if (!room) {
socket.disconnect();
return;
}
socket.emit('getItem', room.item);
}
// Set message to seen when second user connected to socket
await this.chatService.markSeen(getCurrentRoom, user.id);
const roomMessages = await this.chatService.getRoomMessages(
getCurrentRoom,
);
// Get the user count in room
this.clientSize = (
await this.server.of('/').in(String(getCurrentRoom)).allSockets()
).size;
// Get the previous chat messages
this.server
.in(String(getCurrentRoom))
.emit('getRoomMessages', roomMessages);
// Get info about forwarded user in a room
socket.emit('getUser', getUser);
});
});
}
I want to populate my database with some random data. I have used Faker.js for generating that data. I'm using MongoDB on my localhost and all the data is properly following all the validation rules from the schema. I'm having problem with the closing connection of my connection after insertion of data. I want to close the connection soon after the data is populated. I'm using async function to be aware of all the things but something is not going right.
Here is my code seeds.js which is the script im using to populate database
const path = require("path");
require("dotenv").config({ path: path.resolve(__dirname, "../.env") });
var mongoose = require("mongoose");
mongoose.connect(process.env.MONGODB_URI);
require("../models/User");
require("../models/Item");
require("../models/Comment");
var Item = mongoose.model("Item");
var Comment = mongoose.model("Comment");
var User = mongoose.model("User");
const ItemData = require("../data/item.json");
const CommentData = require("../data/comment.json");
const UserData = require("../data/user.json");
async function InsertData() {
ItemData.forEach(async (item) => {
item.seller = item.seller.$oid;
const oldItem = await Item.find({ title: item.title });
if (!oldItem.length) {
var newItem = new Item(item);
await newItem.save();
} else {
console.log(item.slug);
}
});
UserData.forEach(async (user) => {
const oldUser = await User.find({ username: user.username });
if (!oldUser.length) {
var user = new User(user);
await user.save();
} else {
console.log(user.username);
}
});
CommentData.forEach(async (comment) => {
comment.item = comment.item.$oid;
comment.seller = comment.seller.$oid;
var newComment = new Comment(comment);
const oldComment = await Comment.find({ _id: newComment.id });
if (!oldComment.length) {
await newComment.save();
} else {
console.log(comment.body);
}
});
}
async function cleanup() {
await Item.deleteMany({}, () => console.log("Data Cleared Item"));
await Comment.deleteMany({}, () => console.log("Data Cleared Comment"));
await User.deleteMany({}, () => console.log("Data Cleared User"));
}
async function main() {
InsertData().then(async () => {
console.debug('Data Inserted. Closing connection.');
await mongoose.connection.close();
});
}
main();
Here is the stack trace of the error
/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/core/connection/pool.js:841
cb(new MongoError('pool destroyed'));
^
MongoError: pool destroyed
at Pool.write (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/core/connection/pool.js:841:8)
at _command (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/core/wireprotocol/command.js:120:10)
at command (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/core/wireprotocol/command.js:28:5)
at Object.query (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/core/wireprotocol/query.js:66:3)
at Server.query (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/core/topologies/server.js:644:16)
at FindOperation.execute (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/operations/find.js:38:12)
at /Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/operations/execute_operation.js:144:17
at Server.selectServer (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/core/topologies/server.js:832:3)
at Server.selectServer (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/topologies/topology_base.js:342:32)
at executeWithServerSelection (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/operations/execute_operation.js:131:12)
at /Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/operations/execute_operation.js:70:9
at maybePromise (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/utils.js:685:3)
at executeOperation (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/operations/execute_operation.js:34:10)
at Cursor._initializeCursor (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/core/cursor.js:534:7)
at Cursor._initializeCursor (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/cursor.js:186:11)
at nextFunction (/Users/karnikkanojia/Desktop/Anythink-Market-21cto/backend/node_modules/mongodb/lib/core/cursor.js:737:10)
error Command failed with exit code 1.
index.js file
var users = [];
let addUser = (userId, socketId) => {
!users.some((user) => user.userId === userId) &&
users.push({ userId, socketId });
};
let removeUser = (socketId) => {
users = users.filter((item) => item.socketId !== socketId);
};
const getUser = (userId) => {
console.log("inside function", users);
return users.find((item) => item.userId === userId);
};
io.on("connection", (socket) => {
socket.on("addUser", async (userId) => {
await addUser(userId, socket.id);
io.emit("getUsers", users);
console.log(users) // print array of users like this
// [{userId:'userId',socketId: 'socket id'}]
});
socket.on("disconnect", () => {
removeUser(socket.id);
io.emit("getUsers", users);
});
});
const socketIoObject = io;
const usersInObject = users;
module.exports.ioObject = { socketIoObject, usersInObject };
controller file
exports.createNotifications = async (req, res) => {
try {
const { userId, title, type = "default", isPublic } = req.body;
if (!title) {
return res.status(401).send("Data is required");
}
const notification = await notificationsModel.create({
userId,
title,
type,
isPublic: userId ? false : true,
});
console.log("socket", socket.ioObject.usersInObject); // return empty
// array [] !!!!
return res.status(200).send("sent");
} catch (err) {
return res.status(400).send(err.message);
}
};
why I can't get the users list in the controller, I got an empty array !!
I need to share the users list in all files to can get the user by function getUser to get the socketId of a specific user to can send a notification to him
Maybe, you import socket in controller file incorrect
FIXED: USER storageEngine: "wiredTiger"
I use Mocha / Chai / Supertest and Mongodb-Memory-Server to test my app. But's I received error: Transaction numbers are only allowed on storage engines that support document-level locking
In real database and test by postman, it's working well.
My code:
In database.js
const mongoose = require('mongoose')
const { MongoMemoryReplSet } = require('mongodb-memory-server')
mongoose.set('useFindAndModify', false);
const connect = async () => {
try {
let url = process.env.MONGO_URL
let options = {
//Something
}
if (process.env.NODE_ENV === 'test') {
const replSet = new MongoMemoryReplSet();
await replSet.waitUntilRunning();
const uri = await replSet.getUri();
await mongoose.connect(uri, options)
//log connected
} else {
await mongoose.connect(url, options)
//log connected
}
} catch (error) {
//error
}
}
I have two model: Company and User. I made a function to add a member to company with used transaction. My code
const addMember = async (req, res, next) => {
const { companyId } = req.params
const { userId } = req.body
const session = await mongoose.startSession()
try {
await session.withTransaction(async () => {
const [company, user] = await Promise.all([
Company.findOneAndUpdate(
//Something
).session(session),
User.findByIdAndUpdate(
//Something
).session(session)
])
//Something if... else
return res.json({
message: `Add member successfully!`,
})
})
} catch (error) {
//error
}
}
Here's router:
router.post('/:companyId/add-member',
authentication.required,
company.addMember
)
Test file:
const expect = require('chai').expect
const request = require('supertest')
const app = require('../app')
describe('POST /company/:companyId/add-member', () => {
it('OK, add member', done => {
request(app).post(`/company/${companyIdEdited}/add-member`)
.set({ "x-access-token": signedUserTokenKey })
.send({userId: memberId})
.then(res => {
console.log(res.body)
expect(res.statusCode).to.equals(200)
done()
})
.catch((error) => done(error))
})
})
And i received error: Transaction numbers are only allowed on storage engines that support document-level locking'
How can I fix this?
Add retryWrites=false to your database uri. Example below:
mongodb://xx:xx#xyz.com:PORT,zz.com:33427/database-name?replicaSet=rs-xx&ssl=true&retryWrites=false