Nodejs pass server data to the socket? - javascript

I created a user login with nodejs and mongodb. I have a router. I stripped some of the code out below, but the main routes I am concerned with in this question are the /profile and /logout routes.
Basically I want to pass the req or res data to the socket when the route is made. Since the routes are on the server I am not sure how to emit the data. I mean typically you emit from client to server and the other way around, not server to server.
So maybe I am being blind or am not knowledgeable enough, but my question is how can I pass that data to my socket.
module.exports.initialize = function(app, passport) {
app.get('/profile', isLoggedIn, function(req, res) {
res.render('profile', { user : req.user });
//socket.emit('user loggedIn', { user : req.user })
});
app.get('/logout', function(req, res) {
req.logout();
res.redirect('/');
});
// route middleware to make sure a user is logged in
function isLoggedIn(req, res, next) {
if (req.isAuthenticated())
return next();
res.redirect('/');
}
};
Side Note: My socket code sits in my server.js so example.
var users = {};
io.sockets.on('connection', function (socket) {
// Listen to a new user then emit the user to all clients
socket.on('user loggedIn', function (data) {
socket.userName = data;
users[socket.userName] = socket;
io.sockets.emit('user name', Object.keys(users));
console.log(users);
});
});
Basically I want to just store all the logged in users inside an object and emit to the client all the logged in users and allow them to have a live chat.
It appears all I need to do is pass that data to my socket, so I am sure passing that data is simple, but I DONT KNOW HOW!
Thanks!

Clint side code
but username should be unique
socket.emit('myname',{username:'uniqueid});
server side code
var onlineuser=[];
socket.on('myname',function(data){
socket.username=data.username;
onlineuser.push(data.username);
io.sockets.emit('onlineuser',onlineuser);
});
socket.on('disconnect',function()
{
var i=onlineuser.indexOf(socket.username);
onlineuser.splice(i,1);
io.sockets.emit('onlineuser',onlineuser);
});

Related

How do I pass the user's server side request to the client side?

I don't know if I phrased the title correctly, so sorry if it confused you.
I have two servers running (using different ports), one is my client side running on React, and the other one is my server side running on Express.
I'm trying to have the user log in via Steam, so I'm using the passport-steam library. Basically, when the user logs in on steam, it passes the user data to the server-side req session, but I need it to be on the client-side so it can access the data and load it on the website.
Here's some code if that helps
// /routes/auth.js
// /AUTH ROUTER
// GET Request the CLIENT will send to get their info
router.get("/user", ensureAuthenticated, function (req, res) {
res.json(req.session.user);
// returns undefined because their session is saved on the server side
});
// GET Requests used to sign in and authenticate
router.get("/steam", passport.authenticate("steam", { failureRedirect: "http://localhost:3000/login" }), function (req, res) {
res.redirect("http://localhost:3000");
});
router.get("/steam/return", passport.authenticate("steam", { failureRedirect: "http://localhost:3000/login" }), async function (req, res) {
res.redirect("http://localhost:3000");
});

Express JS/ MongoDB Post not working

I started working on a MERN App today and am trying to write a restful api. First I am using mlab to store my mongodb database. I have succesfully connected to this database after creating a user. I can manually create a collection and inject some data into this collection. From my server.js file I can then get the data stored in here.
MongoClient.connect(db_url, (err, database) => {
if (err) return console.log(err);
var collection = database.collection('memories'); // Collection called memories
app.listen(3000, () => {
console.log("Listening on 3000");
});
});
Thats all fine and dandy but I want to take it to the next level. I want to write a CRUD api for the collection Memory. Coming from django, I would like to create my model first. Therefore, in my models/memory.js:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var MemorySchema = new Schema({
name: String,
description: String
});
module.exports = mongoose.model('Memory', MemorySchema);
Then I went ahead and started working on my routes/api/api.js:
let router = require('express').Router();
let Memory = require('../../../models/memories');
router.use(function (req, res, next) {
console.log("Something is happening");
next(); // Request stops at middleware without next()
});
router.route('/memory')
.post(function (req, res) {
let memory = new Memory();
memory.name = req.body.name;
memory.description = req.body.description;
memory.save(function (err) {
if (err) {
res.send(err);
}
res.json({message: 'Memory Created'});
});
})
.get(function (req, res) {
res.json({message: 'First memory'});
});
module.exports = router;
And in my server.js I call this module:
const apiRoutes = require('./routes/api/api');
app.use('/api/', apiRoutes);
However, after testing the post api with postman, it the POST request just takes forever before showing up as Could not get any response. However, the GET request works. What am I missing?
EDIT: So the post function is having trouble saving the model instance...
Try adding results as the first parameter in the callback of the save function, then res.json(results, { message: "Memory Created" }) to see if you are returned anything.
The main difference between the post and the get method is that the post method uses Mongoose, while the get doesn't. If you fail to connect to the database then the response can time out due to memory.save(...) not working as it should. And there are no responses sent outside the callback to save, so if your program never enter it, you will never send a response. The request will time out eventually.
In your model file you register a model on the following line:
module.exports = mongoose.model('Memory', MemorySchema);
Mongoose will then look for data in the memorys collection. If you change it to
module.exports = mongoose.model('Memory', MemorySchema, 'memories');
it will use the memories collection instead. This will make it consistent with the connection-to-db snippet you posted. I don't know if that will fix your issue though. I would suggest changing the connection code to
mongoose.connect(dburl, {
useMongoClient: true
});
instead of the native mongo client. You can add these lines too
mongoose.connection.on('connected', function () {
console.log('Mongoose connected');
});
mongoose.connection.on('error', function (err) {
console.log('Mongoose connection error: ' + err);
});
mongoose.connection.on('disconnected', function () {
console.log('Mongoose disconnected');
});
right after the connection code to help with debugging. Make sure you get connected when starting the app.
If you see an error similar to this Error: Can't set headers after they are sent. in the node terminal window, it might be because you are sending two responses in the post function. If an error occurs while saving it will enter the if(err) block, send a response async then go to the res.json(...) response and send that too.
So you have to return after sending the response to exit the function. Either like this
res.send(err);
return;
or like this
return res.send(err);
Same for the json response.
If that doesn't fix the problem you should either fire up the debugger (node --inspect or nodemon --inspect), or insert a console.log('inside post'); inside the post function to see that you're actually entering it.

What is the difference between redirect and next in NodeJs?

I quite don't understand the difference between these two:
app.get('*', function(req, res, next) {
next();
//ROUTE 1
});
app.get('*', function(req, res) {
res.redirect('/some');
//ROUTE 2
});
app.get('/some', function(req, res) {
res.send("success");
//ROUTE 3
});
When I try making request to ROUTE 1, I get response success but ROUTE 2 doesn't show this response. Why is that?
What I want to do is:
Every request should pass from ROUTE 1 and the control should be handled to a SPECIFIC route, which I would write in it ROUTE if-else statement (not like next(), which sends control to next MATCHING route).
For example:
app.get('*', function(req, res, next) {
if(x==y){
//call SPECIFIC route 3
} else {
// call SPECIFIC route 4 (another route)
//ROUTE 1
});
I tried to do it with redirect but it's not working.
Thank you.
EDIT:
Routes would be: /checkIfSession exists. I would use express-session to check if user's username exists or not in session.
If exists, I want to send control to if otherwise else.
Assume the requests are:
http://198.168.43.200:3000/checkIfSession
http://198.168.43.200:3000/some
(I will call only 1st request).
EDIT 2: I tried following but I don't get any response when I request:
app.use(function (req, res, next) {
if(2==2){
res.redirect("/session");
} else {
res.end("else");
}
});
app.get("/session", function(req, res){
res.write("session");
res.end();
});
app.get("/some", function(req, res){
res.write("some");
res.end();
});
Request: /some
I suppose if you want your routes to go through some kind of authentication first you can use middleware in your routes.
Below is sample code:
app.get('/some', checkSession, function(req, res) {
res.send("success");
});
// this is the middleware function
function checkSession(req, res, next) {
// do your checking here
if (x===y) {
next();
//continue users access to protected API
}
else {
res.redirect('/');
// redirect user to the default login page
}
}
In this above example there are 2 Cases
Case1:
x === y as from your given example I'am assuming users is logged in, so when the user is accessing /some section of your website he will receive Success from the server.
This is the use of your next() function i.e. it continues the execution of that api or sends the data whatever the user is requesting. Something similar to continue in your programming.
Case2:
x!==y now this will be the case where user is not authenticated or logged in and user is still trying to access the /some section of your website. In this case user will be redirected to login page of whatever you have designed for your website for him/her to re-enter his/her credentials.
Here your redirect function redirects the user without sending any data. Something similar to the break.

PassportJS - Getting payload data before it is passed to passport.authenticate as request parameter

There is a passport.js implementation which is being used for LDAP-auth which works. Now the next step is to encrypt the password on the client-side using Crypto-js as follows:
Client-side angular-js controller
$scope.authenticate = function () {
var auth = new login();
auth.username = $scope.username;
auth.password = CryptoJS.AES.encrypt($scope.password); //// HERE
auth.$save(function (response){
console.log(response);
},function(err){
console.log(err);
});
}
Server-side service
.....
.....
app.post('/login', passport.authenticate('ldapauth'), (req, res) => {
console.log("req.user: ",req.user);
req.session.username = req.user[ldap.username];
req.session.userModel = req.user;
res.status(200).send({"success": 'success'});
});
.....
On the server-side service before calling passport.authenticate with the request 'req' the aes encrypted password needs to be decrypted. How can that be implemented here? (The question is not about encryption but how to get data before it gets passed to passport.authenticate as request)
#Abhijay Ghildyal I don't think they understood your question. It is indeed possible to intercept the request before it gets passed to passport.authenticate(). What you'd want to do is to add this passage of code to your express.js or whichever file you did your express server implementation in. Also I am decrypting the request.body here instead of req.user since at that point of time the user is not yet logged in, however if it's different in your case then that's fine you can decrypt req.user the same way. (The variable app here is the name of your server i.e var app = express();)
app.use(function(req, res, next) {
if(req.url === '/login'){
//CryptoJS.AES.decrypt() is Assumed to be the decrypter function here.
req.body = CryptoJS.AES.decrypt(req.body);
console.log(req.body); //To view decrypted body
}
next();
});
That is it. This middleware function will be reached first before the passport.authenticate() function. Just make sure if you're applying this to req.body you add these lines of codes first, after importing the bodyParser (bodyParser = require('body-parser');) before the passage above.
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());

socket.io fails to emit in express app.post callback

I have an express application that opens up a socket connection with my frontend React application.
I want to emit an event to the connected socket whenever someone sends a post request to my server with json data but socket.emit doesn't fire in a.post callback. io.sockets.emit however works fine but is of no use to me in this case.
This fails:
io.on('connection', (socket) => {
app.post('/event', (req, res) => {
socket.emit('someevent', req.body);
res.sendStatus(200);
}
}
But this passes:
io.on('connection', (socket) => {
app.post('/event', (req, res) => {
io.sockets.emit('someevent', req.body);
res.sendStatus(200);
}
}
SLaks's comment is correct but it did not help me solve my problem.
What I ended up doing is:
In the post request coming to /event from the client, I added a hidden input field containing the ID of socket connection.
Then in my /event route handler, I did:
app.post('/event', function(req, res) {
io.sockets.in(req.body.socketId).emit('someevent', req.body);
res.sendStatus(200);
});
This works because by default each socket connection joins a room identified with its socket-id and messages can be sent to rooms using the io object iteself (no need to obtain a socket object from io.on('connection', socket =>{})

Categories