socket.emit not firing inside post request - node.js

I wrapped io.on('connection', function (socket) { in a post request. I then call socket.emit('news', 'username taken'); within the post request. For some reason when I make this call it sends nothing to the client. When I change the emit to io.emit('connection', function (socket)) It works and sends the data to the client. My problem with that solution is using io.emit would send the data to all the sockets that are connected. My question is how can I use socket.emit within this post request.
io.on('connection', onConnection);
function onConnection(sock) {
sock.emit('news', 'username is taken');
}
app.post('/signup', function(req, res) {
var userDetails = User({
firstname: req.body.firstname,
username: req.body.username,
email: req.body.email,
password: bcrypt.hashSync(req.body.password1, bcrypt.genSaltSync(10))
});
User.findOne({
$or: [ { 'username': req.body.username}, {'email': req.body.email}]
}, function(err, user) {
if (user) {
if(user.username === req.body.username){
onConnection();
console.log('username is taken');
} else {
}
if(user.email === req.body.email){
console.log('email is taken')
} else {
}
} else {
userDetails.save(function(err) {
if (err) throw err;
});
res.redirect('/');
console.log('change to login')
}
if (err) {
return done(err);
}
});
});
});

The usual way you would approach this is you would use session middleware that would establish a user session when the first page on the site was loaded. Then, when the user connects with socket.io, you would put the socket.id into the session for that user. Then, when you get the app.post() and you want to send to their socket.io connection, you would look in the session for that user, get the socket.id for that user and then look up the socket using that. Once you have the socket, you can send a message to them.
You use the session to connect the socket.io connection and the app.post(). no event handlers are set inside another event handler.

I just fixed my problem. What I did was create a variable called socket1. Then I assigned the socket parameter to socket1 within the io.on annonymous function. I then have socket as a universal variable that I can call wherever I want in my code. I'm not sure if this is programatically correct but it works.
var SOCKET_LIST = {};
var socket1;
io.on('connection', function (socket) {
SOCKET_LIST[socket.id] = socket;
socket1 = socket;
socket.emit('news', 'username taken');
});
app.post('/signup', function(req, res) {
var userDetails = User({
firstname: req.body.firstname,
username: req.body.username,
email: req.body.email,
password: bcrypt.hashSync(req.body.password1, bcrypt.genSaltSync(10))
});
User.findOne({
$or: [ { 'username': req.body.username}, {'email': req.body.email}]
}, function(err, user) {
if (user) {
if(user.username === req.body.username){
socket1.emit('news', 'username taken');
console.log('username is taken');
} else {
}
if(user.email === req.body.email){
io.emit('news', 'email taken');
console.log('email is taken')
} else {
}
} else {
userDetails.save(function(err) {
if (err) throw err;
});
res.redirect('/');
console.log('change to login')
}
if (err) {
return done(err);
}
});
});

Related

Unable to verify hashed password

Hi All,
I am authenticating my user using bcrypt module.
I am able to do perform the Registration process, but facing problem during Login process.
User Model:
var userSchema = new Schema({
email: {type: String, required: true},
password: {type: String,
});
Hashing methods:
userSchema.methods.encryptPassword = function (password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(5), null)
};
userSchema.methods.validPassword = function (password) {
return bcrypt.compareSync(password, this.password);
};
Sign in:
module.exports.login = function (user, callback) {
User.findOne({'email': user.email, 'password': user.validPassword(this.password)}, callback);
};
Login Route
router.post('/login', function (req, res) {
var user = req.body;
User.login(user, function (err, user) {
if (err) {
throw err;
}
if (!user) {
res.sendStatus(404);
return;
}
res.json(user.id);
});
});
While executing am getting this error: TypeError:user.validPassword is not a function
Please Help.
Your mistake is that the user being provided to your login method is not a Mongoose DB object. Instead, your login function should look something like this:
module.exports.login = function (request, callback) {
User.findOne({'email': request.email }, function(err, user) {
if (err) return callback(err);
if(!user || !user.validPassword(request.password)) return callback();
return callback(null, user);
});
};
This will ensure that user is a valid Mongoose object before you attempt to verify the password.
One other possible solution, if you'd prefer to avoid checking that the password is valid in your data layer, is to simply fetch the user document based on its email and then check the password in the login route.
router.post('/login', function (req, res) {
var user = req.body;
User.findOne(user, function (err, user) {
if (err) {
throw err;
}
if (!user) {
res.sendStatus(404);
return;
}
if (!user.validPassword(req.body.password)) {
res.sendStatus(401);
return;
}
res.json(user.id);
});
});
In Login Route, you need to instantiate the Schema:
router.post('/login', function (req, res) {
var user = new User(req.body);
User.login(user, function (err, user) {
if (err) {
throw err;
}
if (!user) {
res.sendStatus(404);
return;
}
res.json(user.id);
});
});

Node.js - Check if user exists

I'm using NodeJS and passport to let users create an account before they can see results of a quiz they've just taken. My challenge is I need to confirm the username is available before the page refreshes because the user will lose their results if this happens.
Again: I need to verify the username is not taken prior to refreshing.
I think I'm close but it is not working. How would I change my code to handle this challenge?
Currently if the user name is taken it returns an error on trying to create an account and the user ends up on the /failpage as shown below.
app.post('/quiz', usernameToLowerCase, emailToLowerCase, function(req, res) {
User.findOne({
username: req.body.username
}, function(err, user) {
if (err) {
alert(err)
if (user) {
alert('this username is already taken. Please choose another.')
console.log('there was a user');
return false;
}
}
});
var user = new User({
username: req.body.username,
email: req.body.email,
password: req.body.password,
})
user.save(function(err) {
console.log('this is the problem' + ' ' + err)
if (err) {
return res.redirect('/failpage')
}
req.logIn(user, function(err) {
if (err) {
console.log(err);
}
console.log('all looks good')
res.redirect('/results');
});
});
});
Solved it with this if anyone else is trying to do the same thing:
in app.js
app.get('/usercheck', function(req, res) {
User.findOne({username: req.query.username}, function(err, user){
if(err) {
console.log(err);
}
var message;
if(user) {
console.log(user)
message = "user exists";
console.log(message)
} else {
message= "user doesn't exist";
console.log(message)
}
res.json({message: message});
});
});
In js
$('#usercheck').on('change', function() {
$.get('/usercheck?username='+$('#usernameValue').val().toLowerCase(), function(response) {
$('#usernameResponseHidden').text(response.message)
if ($('#usernameResponseHidden').html() === "user exists"){
$('#usernameResponse').text('That username is taken. Please pick another')
}
To solve your problem I think you need to routes. At least a app.get('/quiz') which returns a boolean on if the user exists or not. The section User.findOne can be sent in that route instead. You just need to make a request using ajax when he looses focus of the username field of your form, and display a notification if the name is available or not.

Node application crash if i use send or json

I am creating a IOS Application and in background we have used Node.js and Mongodb. I have create node application for creating user and send error and success response by jSon but if use res.send my node application will crash.
I have tried to found issue but not get yet and positive response. Below my all code.
Controller :
const nodemailer = require('nodemailer');
const passport = require('passport');
const User = require('../../models/User');
exports.ManuallySave = function(req,res)
{
if(req.body.my_token !== '')
{
User.findOne({ email: req.body.email }, (err, existingUser) => {
if (existingUser)
{
//res.setHeader('Content-Type', 'text/plain');
res.json({"status":'error',"msg":'Email address already exists.'});
}
});
User.findOne({ username: req.body.username }, (err, existingUserName) => {
if (existingUserName)
{
res.send({"status":'error',"msg":'Username already exists.'});
}
});
/* Save Action perform */
}
else
{
res.send({"status":'error',"msg":'Token is not available.'});
}
}
Console Error.
/var/www/node/MyApplication/node_modules/mongodb/lib/utils.js:98
process.nextTick(function() { throw err; });
^
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:344:11)
at ServerResponse.header (/var/www/node/MyApplication/node_modules/express/lib/response.js:718:10)
at ServerResponse.send (/var/www/node/MyApplication/node_modules/express/lib/response.js:163:12)
at ServerResponse.json (/var/www/node/MyApplication/node_modules/express/lib/response.js:249:15)
at ServerResponse.send (/var/www/node/MyApplication/node_modules/express/lib/response.js:151:21)
at /var/www/node/MyApplication/controllers/apis/userAppController.js:55:13
at model.<anonymous> (/var/www/node/MyApplication/node_modules/mongoose/lib/document.js:1875:20)
at next_ (/var/www/node/MyApplication/node_modules/hooks-fixed/hooks.js:89:34)
at fnWrapper (/var/www/node/MyApplication/node_modules/hooks-fixed/hooks.js:186:18)
at /var/www/node/MyApplication/node_modules/mongoose/lib/model.js:226:5
at /var/www/node/MyApplication/node_modules/mongoose/lib/model.js:135:7
at /var/www/node/MyApplication/node_modules/mongodb/lib/collection.js:504:5
at /var/www/node/MyApplication/node_modules/mongodb/lib/collection.js:666:5
at handleCallback (/var/www/node/MyApplication/node_modules/mongodb/lib/utils.js:96:12)
at /var/www/node/MyApplication/node_modules/mongodb/lib/bulk/unordered.js:473:9
at handleCallback (/var/www/node/MyApplication/node_modules/mongodb/lib/utils.js:96:12)
at resultHandler (/var/www/node/MyApplication/node_modules/mongodb/lib/bulk/unordered.js:420:5)
at /var/www/node/MyApplication/node_modules/mongodb-core/lib/wireprotocol/2_4_support.js:544:17
at nextTickCallbackWith0Args (node.js:420:9)
at process._tickCallback (node.js:349:13)
I have used res.send and res.json both but in both condition my application will crash.
That error is thrown when you are trying to call methods on res when you already called res.send. There is no guarantee that it res.send only will be called once in your code, which there must be. The block
User.findOne({ email: req.body.email }, (err, existingUser) => {
if (existingUser)
{
//res.setHeader('Content-Type', 'text/plain');
res.json({"status":'error',"msg":'Email address already exists.'});
}
});
User.findOne({ username: req.body.username }, (err, existingUserName) => {
if (existingUserName)
{
res.send({"status":'error',"msg":'Username already exists.'});
}
});
will call res.send twice if you already have a user with both that email address and that username. You will have to do the other call within the first callback.
User.findOne({ email: req.body.email }, (err, existingUser) => {
if (existingUser)
{
//res.setHeader('Content-Type', 'text/plain');
res.json({"status":'error',"msg":'Email address already exists.'});
} else {
checkUsername();
}
});
function checkUsername() {
User.findOne({ username: req.body.username }, (err, existingUserName) => {
if (existingUserName)
{
res.send({"status":'error',"msg":'Username already exists.'});
} else {
// save action
}
});
}
May I suggest you look into promise chains to handle the inevitable upcoming callback nesting?
You should just use the or operator.
User.findOne({ $or: [ { email: req.body.email }, { username: req.body.username } ] }, (err, existingUser) => {
if (existingUser)
{
//res.setHeader('Content-Type', 'text/plain');
res.json({"status":'error',"msg":'Email address already exists.'});
}
});
Than you just send one response.
You're getting this error because your code allows for a res.send() after a res.json() has already been issues.
Your two validation checks both execute asynchronously, and if both validation conditions are met, then both res.json and res.send will be executed.
One solution is to embed the second check within the callback of the first like this:
const nodemailer = require('nodemailer');
const passport = require('passport');
const User = require('../../models/User');
exports.ManuallySave = function(req,res){
if(req.body.my_token !== ''){
User.findOne({ email: req.body.email }, (err, existingUser) => {
if (existingUser) {
res.json({"status":'error',"msg":'Email address already exists.'});
} else {
User.findOne({ username: req.body.username }, (err, existingUserName) => {
if (existingUserName) {
res.send({"status":'error',"msg":'Username already exists.'});
} else {
/* Save Action perform */
}
});
}
});
} else {
res.send({"status":'error',"msg":'Token is not available.'});
}
}
I'd also recommend checking err in both of those callbacks and handling them.

Issues with req.flash within a post request

I check whether or not an email or username is taken. I then use then use flash to send a message to the client using req.flash('messages', 'That username is taken.'); The only problem is I can't call a request command within a function. I can send the message when I put it right after app.post('/signup', function(req, res) { How could I check if a username is taken and then send the req.flash command once it has been determined. I tried to create an if statement but because node is run asynchronously by the time the username is found and the variable is set to true, the if statement has been called. How could I use req.flash to send a message to the client within this post request.
app.post('/signup', function(req, res) {
var userDetails = User({
firstname: req.body.firstname,
username: req.body.username,
email: req.body.email,
password: bcrypt.hashSync(req.body.password1, bcrypt.genSaltSync(10))
});
User.findOne({
$or: [ { 'username': req.body.username}, {'email': req.body.email}]
}, function(err, user) {
if (user) {
if(user.username === req.body.username){
console.log('that username is taken');
req.flash('messages', 'that username is taken');
} else {
}
if(user.email === req.body.email){
console.log('that email is already in use');
req.flash('messages', 'that email is already in use');
} else {
}
} else {
userDetails.save(function(err) {
if (err) throw err;
});
console.log('change to login')
}
if (err) {
return done(err);
}
});
res.redirect('/');
});
It should be no problem to call req in a other function if req is defined in a higher level. I am not sure if your flash is session stored, if it not, the reasen why the messages is not flash is your redirection to route.
You redirect to root without waiting to the end of database request. I think you will only redirect if user is found? Include your redirect to the callback function.

Getting out of sync server response

On a successful signup of a user I am currently seeing a mostly empty page with the text undefined. Redirecting to /app at the top.
UPDATE: I should also mention that after form submittal I am redirected to /users. So on /users I see the text mentioned above.
I think it is because of the req.redirect call being within the user.save callback but I am not sure what the fix is.
I am using mongoose for the ORM.
var User = require('../models/user');
module.exports = function(app) {
app.post('/users', function(req, res, next) {
var user = new User({
email: req.body.email,
password: req.body.password
});
user.save(function(err) {
if (err)
res.send(412, {message: err});
else
req.login(user, function(err) {
if (err !== undefined) return next(err);
res.redirect('/app', {
email: user.email,
id: user._id
});
});
});
});
};
It turns out that the req.login call has to be contained in a password.authenticate callback. The example on the site left that part out.
user.save(function(err) {
if (err)
res.send(412, {message: err});
else
passport.authenticate('local', function(err, user) {
if (err) { return next(err) }
if (!user) { return res.redirect('/login') }
req.login(user, function(err) {
if (err) { return next(err); }
return res.redirect('/app', { email:user.email, id:user._id });
});
})(req, res, next);
});

Resources