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
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 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
I have been getting this error and I tried all I can To solve it but none. I feel the User is not being fetched from Users.js file. here is Index.js file
const socketio = require("socket.io");
const router = require("./router");
const http = require("http");
const cors = require("cors");
const port = process.env.PORT || 5000;
const app = express();
const server = http.createServer(app);
const { addUser, removeUser, getUser, getUserInRoom } = require("./users");
const io = socketio(server);
app.use(cors());
// app.use(router);
io.on("connection", (socket) => {
console.log("We have a new connection!");
socket.on("join", ({ name, room }, callback) => {
const { user } = addUser({ id: socket.id, name, room });
console.log(user);
// if (error) return callback(error);
socket.emit("message", {
user: "admin",
text: `${user.name}, welcome to the room ${user.room}.`,
});
socket.broadcast
.to(user.room)
.emit("message", { user: "admin", text: `${user.name} has joined` });
socket.join(user.room);
callback();
});
socket.on("sendMessage", (message, callback) => {
const user = getUser(socket.id);
console.log(user);
io.to(user.room).emit("message", { user: user.name, text: message });
callback();
});
socket.on("disconnect", () => {
console.log("User had just left!");
});
});
server.listen(port, () => console.log(`Server has started on port ${port}`));
Here is My Users.js file
const users = [];
const addUser = ({ id, name, room }) => {
name = name.trim().toLowerCase();
room = room.trim().toLowerCase();
const existingUser = users.find((user) => {
user.room === room && user.name === name;
});
if (existingUser) {
return { error: "Username is taken" };
}
const user = { id, name, room };
users.push(user);
return { user };
};
const removeUser = (id) => {
const index = users.findIndex((user) => {
user.id === id;
});
if (index !== -1) {
return users.splice(index, 1)[0];
}
};
const getUser = (id) => {
users.find((user) => user.id === id);
};
const getUserInRoom = (room) => {
users.filter((user) => user.room === room);
};
module.exports = { addUser, removeUser, getUser, getUserInRoom };
I have searched through for any possible misplaced variable but i found none.
And Lastly, My client side
import "./Chat.css";
import queryString from "query-string";
import io from "socket.io-client";
import { InfoBar } from "../InfoBar/InfoBar";
import { Input } from "../Input/Input";
import { Messages } from "../Messages/Messages";
let socket;
export const Chat = ({ location }) => {
const [name, setName] = useState("");
const [message, setMessage] = useState("");
const [messages, setMessages] = useState([]);
const [room, setRoom] = useState("");
const ENDPOINT = "localhost:5000";
// ("ws://localhost:5000", { transports: ["websocket", "polling"] });
useEffect(() => {
socket = io(ENDPOINT);
const { name, room } = queryString.parse(location.search);
// console.log(name, room);
setName(name);
setRoom(room);
// console.log(socket);
socket.emit("join", { name, room }, () => {});
return () => {
socket.emit("disconnect");
socket.off();
};
}, [ENDPOINT, location.search]);
//UseEffect for the messages
useEffect(() => {
socket.on("message", (message) => {
setMessages(messages=>[...messages, message]);
});
}, []);
const sendMessage = (event) => {
event.preventDefault();
if (message) {
socket.emit("sendMessage", message, () => setMessage(""));
}
};
console.log(message, messages);
return (
<div className="outerContainer">
<div className="container">
<InfoBar room={room} />
<Input
setMessage={setMessage}
message={message}
sendMessage={sendMessage}
/>
<Messages messages={messages} />
{/* <input
value={message}
onChange={(event) => setMessage(event.target.value)}
onKeyPress={(event) =>
event.key === "Enter" ? sendMessage(event) : null
}
/> */}
</div>
</div>
);
};
I will appreciate every available help
You don't have a variable named user, thus user is undefined and you can't get a property of an undefined object.
const { user } = addUser({ id: socket.id, name, room });
does not create a user variable, but an unnamed object, which has a property named user.
I don't know, why your addUser function does not just return the created user object but encapsulates it in an object. Maybe you could just do
const addUser = ({ id, name, room }) => {
...
const user = { id, name, room };
users.push(user);
return user;
}
and then
const user = addUser(....)
If that is not possible and the return type of addUser can't be changed, you could do the following
const user = addUser(...).user;
socket.emit("message", {
user: "admin",
text: `${user.name}, welcome to the room ${user.room}.`,
});
You do not really need the {} around the return
const addUser = ({ id, name, room }) => {
name = name.trim().toLowerCase();
room = room.trim().toLowerCase();
const existingUser = users.find((user) => {
user.room === room && user.name === name;
});
if (existingUser) {
return { error: "Username is taken" };
}
const user = { id, name, room };
users.push(user);
return { user };
};
Instead do it like this:
const addUser = ({ id, name, room }) => {
name = name.trim().toLowerCase();
room = room.trim().toLowerCase();
const existingUser = users.find((user) => {
user.room === room && user.name === name;
});
if (existingUser) {
return { error: "Username is taken" };
}
const user = { id, name, room };
users.push(user);
return user;
};
I'm trying to delete all the users in my auth and database using firebase functions. Here's my code for that:
const admin = require("firebase-admin");
admin.initializeApp({
credential: admin.credential.applicationDefault(),
databaseURL: "----"
});
export const listenToAdminCommands = functions.firestore.document('collection/{docUid}')
.onWrite((change, context) =>
{
const pass: string = '--';
// const before = change.before.exists? change.before.data() : null;
const after = change.after.exists? change.after.data() : null;
if(after !== null && after !== undefined) {
const adminCommandType: string = after['type'];
const adminCommandPass: string = after['pass'];
if(adminCommandType === 'deleteAll' && adminCommandPass === pass) {
adminDeleteAllUsers();
}
}
});
//Admin control
function adminDeleteAllUsers() {
deleteAllUsers(' ');
return null;
}
function deleteAllUsers(nextPageToken: any) {
admin.auth().listUsers(1000, nextPageToken)
.then((listUsersResult: any) => {
//go through each one and check last time signed in
listUsersResult.users.forEach((userRecord: any) => {
const user: any = userRecord.toJSON();
const userUid = user['uid'];
console.log('Deleting user for data delete uid = ', userUid);
admin.auth().deleteUser(userUid)
.then(() => {
console.log('Successfully deleted user', userUid);
})
.catch((error: any) => {
console.log('Error deleting user:', error);
});
db.collection('users').doc(userUid).delete();
});
if (listUsersResult.pageToken) {
// List next batch of users.
listAllUsers(listUsersResult.pageToken);
}
})
.catch((error: any) => {
console.log('Error listing users:', error);
});
}
When the function get executed, no user is deleted. It's like the function never worked. Am I missing something?
Update:
I'm not sure if this is the way to do it, but it's still not working. I tried to handle promises correctly, but I'm not sure if what I'm doing is correct or not.
export const listenToAdminCommands = functions.firestore.document('collection/{docUid}')
.onWrite((change, context) =>
{
const pass: string = '---';
// const before = change.before.exists? change.before.data() : null;
const after = change.after.exists? change.after.data() : null;
if(after !== null && after !== undefined) {
const adminCommandType: string = after['type'];
const adminCommandPass: string = after['pass'];
if(adminCommandType === 'deleteAll' && adminCommandPass === pass) {
return adminDeleteAllUsers();
}
return;
}
return;
});
//Admin control
function adminDeleteAllUsers() {
return deleteAllUsers(' ');
}
function deleteAllUsers(nextPageToken: any) {
return admin.auth().listUsers(1000, nextPageToken)
.then((listUsersResult: any) => {
//go through each one and check last time signed in
listUsersResult.users.forEach((userRecord: any) => {
const user: any = userRecord.toJSON();
const userUid = user['uid'];
console.log('Deleting user for data delete uid = ', userUid);
return admin.auth().deleteUser(userUid)
.then(() => {
console.log('Successfully deleted user', userUid);
return db.collection('users').doc(userUid).delete();
})
.catch((error: any) => {
console.log('Error deleting user:', error);
return;
});
});
if (listUsersResult.pageToken) {
// List next batch of users.
listAllUsers(listUsersResult.pageToken);
}
return;
})
.catch((error: any) => {
console.log('Error listing users:', error);
return;
});
}
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