Express Can't set headers after they are sent - json

I've tried to find similar questions with this problem and while there are lots with the same error I don't think I'm getting the error for the same reason.
I have a route, POST /api/servers which creates a server listing in the database. I don't know if it is wrong to include so much logic in an express route but it seems to be working up until this error I keep getting.
Basically, if creating a server that already exists (address + port combo) then the route sends back an error with 'SERVER ALREADY EXISTS' as the message in JSON, but when ever this happens if I try to create a server that already exists in postman I do get the JSON back as expected but my server crashes with 'Cant set headers after they are sent'
router.post('/servers', passport.authenticate('jwt', { session: false }), function(req, res) {
//This is very lazy validation, to do in future version, create error object
// which accumulates the problems with the form and can return all problems at
// once to user
if (!req.body.name) {
return res.json({success: false, message: 'NAME REQUIRED'})
}
if (!req.body.address) {
return res.json({success: false, message: 'ADDRESS REQUIRED'})
}
if (!req.body.image) {
return res.json({success: false, message: 'IMAGE REQUIRED'})
}
if (!req.body.description) {
return res.json({success: false, message: 'DESCRIPTION REQUIRED'})
}
var newServer = new Server();
newServer.name = req.body.name;
newServer.address = req.body.address;
//if a custom port is specified, uses it otherwise defaults to 25565
if (req.body.port) {
newServer.port = req.body.port;
} else {
newServer.port = 25565;
}
//Check if server with address and port not already exists
Server.findOne({ address: newServer.address, port: newServer.port }, function(err, server) {
if (err) {
return res.json({success: false, message: 'Internal Error'});
} else if (server) {
return res.json({success: false, message: 'SERVER ALREADY EXISTS'});
}
});
newServer.image = req.body.image;
newServer.description = req.body.description;
newServer.owner = req.user._id; // Gives the current user id
if (req.body.votifier_enabled) {
//If votifier is enabled on this server
newServer.votifier.enabled = true;
if (!req.body.votifier_address) {
return res.json({success: false, message: 'VOTIFIER ADDRESS REQUIRED'})
}
//if custom votifier port specified, it is used otherwise defaults to 8192
if (req.body.votifier_port) {
newServer.votifier.port = req.body.votifier_port;
} else {
newServer.votifier.port = 8192;
}
if (!req.body.votifier_pubkey) {
return res.json({success: false, message: 'VOTIFIER PUBLIC KEY REQUIRED'})
}
newServer.votifier.address = req.body.votifier_address;
newServer.votifier.pubkey = req.body.votifier_pubkey;
//Check if votifier server with address and port not already exists
Server.findOne({ votifier: { address: newServer.votifier.address, port: newServer.votifier.port } }, function(err, server) {
if (err) {
return res.json({success: false, message: 'Internal Error'});
} else if (server) {
return res.json({success: false, message: 'VOTIFIER ALREADY EXISTS'});
}
});
};
if(!req.body.hidden) {
newServer.hidden = false;
} else {
newServer.hidden = req.body.hidden;
};
console.log('passed all verification');
//Lastly: Verify that the server really does belong to this user,
//checks that string config.VERIFY_OWNERSHIP_STRING is in servers motd
mcping(req.body.address, req.body.port, function(err, stats) {
if (err) {
//Problem connecting to server
return res.json({success: false, message: 'SERVER CONNECTION FAILED: Is your server online?'})
} else {
console.log('pinged server successfully')
var status = stats;
if (status.description.text.includes(config.VERIFY_OWNERSHIP_STRING)){
newServer.status.online = true;
newServer.status.motd = status.description.text;
newServer.status.currPlayers = status.players.online;
newServer.status.maxPlayers = status.players.max;
//Verification successful, server does belong to this user
//Validation complete and newServer object created! Now to save it to database
newServer.save(function(err, server) {
console.log('attempted to save the server');
if (err) {
//Theoretically - this should not happen unless theres an internal error
console.log(err.errors);
return res.json({success: false, message: err});
} else if (server) {
console.log('save successful')
return res.json({success: true, _id: server._id});
}
});
} else {
return res.json({success: false, message: "SERVER VALIDATION FAILED: Is '" + config.VERIFY_OWNERSHIP_STRING + "' in your motd?"});
}
}
}, 3000);
})
This is the route that is causing the problem, I think the problem is because even though the server already exists so the JSON message is being sent back to say SERVER ALREADY EXISTS all the console.logs below still are called, including in the mongoose save function.
I thought that res.json is supposed to end the request response cycle? So I was under the impression it's okay to have so many res.jsons called but now I'm thinking this is the problem
Thanks for any help and sorry for the confusing code!

Related

SQL Query not Giving a Result

I am trying to login my user and i need to search whether the user exists in the db or not. My db is ClearDB using MySQL on Heroku. I am using node.js. This is my code:
if (req.body.isAdmin === 1) {
connection.query(
`SELECT * FROM admin WHERE username='${req.body.username}' AND password='${req.body.password}'`,
function (err, rows) {
if (!err) {
console.log(rows);
res.status(201).json({
success: true,
message: "Admin Logged In!",
});
} else {
res
.status(404)
.json({ success: false, message: "Admin Not Found!" });
}
}
);
} else {
connection.query(
`SELECT * FROM guard WHERE username='${req.body.username}' AND password='${req.body.password}'`,
function (err, rows) {
if (!err) {
console.log(rows);
res.status(201).json({
success: true,
message: "Guard Logged In!",
});
} else {
res
.status(404)
.json({ success: false, message: "Guard Not Found!" });
}
}
);
}
} catch (error) {
res.status(500);
throw new Error(error);
}
In the above code, i first check whether the user is an admin or not, then i execute the respective query. The db connects properly i.e., there is no issue with the db connection.
The issue is that there is no output for any of the queries i.e., rows variable is empty. Even if the data is false and doesn't match the data available, it doesn't give an error and also doesn't give an output. I have double-checked the connection and the query and they seem fine. I don't get where the issue is. Please help!

How to show user not found error using node.js and mysql

I am implementing registration API, in this registration, I have email as unique and I want to check if email exists or not in db if exists it has to show email already exists message else it has to register.everything works fine up to this.Now I want to check if an email does not exist in the database it has to show user not found the error but I am not able to display this message.
Here is my code
createUser: function(req, res, next) {
UserModel.getByEmail(req.body.email,function(err, user) {
if(err) {
res.json({ 'valid': false, 'message': err.message });
} else {
if(!user) {
res.json({ 'valid': false, 'message': 'User not exists'}); //this message is not showing
} else {
if(user[0].id) {
console.log('hi');
res.json({ 'valid': false, 'message': 'Email already exists'});//works only if i use user[0].id instead of user
} else {
UserModel.addUser(req, token, function(err, data) {
//console.log(data);
if (err) {
res.json({ 'valid': false, 'message': err.message });
} else {
console.log('Message sent: ' + info.response);
res.json({Error: info.response});
res.json({ 'valid': true, 'message': 'User added succesfully', req:req.body, data : data });
}
});
}
}
}
});},
In getByEmail method
getByEmail: function(email, rows){
var sql = "SELECT * FROM sbt_users WHERE email = ?";
var fields = [email];
return db.query(sql, fields, rows);}
if(!user) is not working.Can anyone explain how to show that message.
user is an array of records. If the array is empty, it's length will be 0.
So you can check if (user.length === 0)

Google Authentication using Sails.js

When I have tried to implement Google authentication in my site, using sails JavaScript, and MySQL getting error. I have using passport and passport-Google-auth Strategy. Problem is not getting data to my site from Google
My Express Config(express.js) file is like below,
var passport = require('passport')
, GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
var verifyHandler = function(token, tokenSecret, profile, done) {
process.nextTick(function() {
console.log(profile)
User.findOne({uid: profile.id}, function(err, user) {
if (user) {
return done(null, user);
} else {
var data = {
provider: profile.provider,
uid: profile.id,
name: profile.displayName
};
if (profile.emails && profile.emails[0] && profile.emails[0].value) {
data.email = profile.emails[0].value;
}
if (profile.name && profile.name.givenName) {
data.firstname = profile.name.givenName;
}
if (profile.name && profile.name.familyName) {
data.lastname = profile.name.familyName;
}
User.create(data, function(err, user) {
return done(err, user);
});
}
});
});
};
passport.serializeUser(function(user, done) {
console.log(user)
done(null, user.uid);
});
passport.deserializeUser(function(uid, done) {
User.findOne({uid: uid}, function(err, user) {
done(err, user);
});
});
module.exports.http = {
customMiddleware: function(app) {
passport.use(new GoogleStrategy({
clientID: 'Client Id here',
clientSecret: 'Secret key here',
callbackURL: 'http://localhost:1337/auth/google/callback'
}, verifyHandler));
app.use(passport.initialize());
app.use(passport.session());
}
};
module.exports.cache = {
// The number of seconds to cache files being served from disk
// (only works in production mode)
maxAge: 31557600000
};
module.exports.userlogin = {
userModel: 'user'
};
And My Auth Controller I have added code like below,
google: function(req, res) {
passport.authenticate('google',{
failureRedirect: '/login', scope: ['profile', 'email']
}, function(err, user) {
req.logIn(user, function(err) {
if (err) {
console.log(err);
res.view('500');
return;
}
res.redirect('/');
return;
});
})(req, res);
},
You didn't post your code, so we can't find the exact problem :/
I usually use this method for google/facebook authentication with sails.js.
I follow at first this documentation to add the authentication buttons in the frontend:
https://developers.google.com/identity/sign-in/web/sign-in
Then I post the token that I got from google/facebook to the backend where I can check if the user is banned or whatever... If everything is correct, I create an account for him in the database, I send him his password to his email and finally authenticate him using sessions
(req.session.userId = createdUser.id)
In the next time the user can log in using his email and password or just using google. And both options lead him to the same account :D
My Sails.js function in the authentication controller:
googleAuth: function(req, res) {
if (_.isUndefined(req.param('googleToken'))) {
return res.json({
success: false,
msg: 'Error! Please post your google token'
});
}
var urlToRq = "https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=" + req.param('googleToken');
// Get information about the google user with the specified access token.
request.get({url: urlToRq}, function(err, response, body) {
if(err) {
return res.json({
success: false,
msg: 'Server Error'
});
}
var receivedData = JSON.parse(body);
var userId = receivedData.sub;
var userEmail = receivedData.email;
var emailVerified = receivedData.email_verified;
var userName = receivedData.name;
var userPicture = receivedData.picture;
if (emailVerified == false) {
return res.json({
success: false,
msg: 'Your email is not verified'
});
}
else {
// AUTHENTICATION VERIFIED, YOU CAN SAVE THE CONNECTED USER IN A SESSION, OR ADD HIM TO THE DATABASE AS A NEW ACCOUNT, OR CHECK IF HE HAS A PREVIOUS ACCOUNT OR WHATEVER YOU WANT...
}
});
},
Of course don't forget to run npm install request --save
If anyone needs the facebookAuth function just tell me :D I will post it for you :)

Querying a MySQL database from a NodeJS AWS Lambda Function

I'm having a problem querying my MySQL database (hosted remotely from AWS) inside of my AWS Lambda function.
This is my code except for the parts I need for the rest of Lambda function (which is being called for an Alexa Skill):
var mysql = require('mysql');
var connection = mysql.createConnection({
host : '<myserver>',
user : '<myusername>',
password : '<mypw>',
database : '<mydatabase>'
});
connection.connect(function(err){
if(!err) {
console.log("Database is connected ... nn");
}
else {
console.log("Error connecting database ... nn");
}
});
connection.query("INSERT INTO Users (user_id) VALUES ('TESTNAME')");
connection.end();
This works just fine when I run it with node from my command prompt:
node index.js
I'm using the "mysql" module installed via npm in the directory with index.js and zip it and upload it to my Lambda function.
Again, this works on my development machine, but gives no indicator when testing my Lambda function as to why it doesn't effect my database at all.
My question extends into Alexa and Lambda as much as it does the proper usage of the mysql Node.JS module.
Here's my current code for my Lambda, and the problem here, of course, is still that my test value -> a username called "TESTNAME" doesn't get added to my MySQL database.
I put the query into the connect callback as the first comment suggests, and I'm putting my new code instead of updating my old code above just to keep a record of what how I think the code should transition to being in my Alexa's Lambda function:
Updated code:
var mysql = require('mysql');
var connection = mysql.createConnection({
host : '<myserver>',
user : '<myusername>',
password : '<mypw>',
database : '<mydatabase>'
});
exports.handler = (event, context) => {
try {
if (event.session.new) {
// New Session
console.log("NEW SESSION");
}
switch (event.request.type) {
case "LaunchRequest":
// Launch Request
console.log(`LAUNCH REQUEST`);
context.succeed(
generateResponse({},
buildSpeechletResponse("Welcome to an Alexa Skill, this is running on a deployed lamda function", true)
)
);
break;
case "IntentRequest":
// Intent Request
console.log(`Intent Request`);
console.log('Then run MySQL code:');
connection.connect(function(err) {
console.log('Inside connection.connect() callback');
if (!err) {
console.log("Database is connected ... ");
connection.query("INSERT INTO Users (user_id) VALUES ('TESTNAME')",
function(err, result) {
console.log("Inside connection.query() callback")
if (!err) {
console.log("Query Successful! Ending Connectection.");
connection.end();
} else {
console.log("Query error!");
}
});
} else {
console.log("Error connecting database ..." + err.message);
}
});
context.succeed(
generateResponse({},
buildSpeechletResponse("Welcome to the incredible intelligent MySQLable Alexa!", true)
)
);
break;
case "SessionEndedRequest":
// Session Ended Request
console.log(`SESSION ENDED REQUEST`);
break;
default:
context.fail(`INVALID REQUEST TYPE: ${event.request.type}`);
}
} catch (error) {
context.fail(`Exceptiodn: ${error}`)
}
};
//Helpers
buildSpeechletResponse = (outputText, shouldEndSession) => {
return {
outputSpeech: {
type: "PlainText",
text: outputText
},
shouldEndSession: shouldEndSession
};
};
generateResponse = (sessionAttributes, speechletResponse) => {
return {
version: "1.0",
sessionAttributes: sessionAttributes,
response: speechletResponse
};
};
And my console output:
START RequestId: 5d4d17a7-0272-11e7-951c-b3d6944457e1 Version: $LATEST
2017-03-06T13:39:47.561Z 5d4d17a7-0272-11e7-951c-b3d6944457e1 Intent Request
2017-03-06T13:39:47.562Z 5d4d17a7-0272-11e7-951c-b3d6944457e1 Then run MySQL code:
END RequestId: 5d4d17a7-0272-11e7-951c-b3d6944457e1
REPORT RequestId: 5d4d17a7-0272-11e7-951c-b3d6944457e1 Duration: 82.48 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 14 MB
The problem was that I needed to put my context.succeed inside of my callbacks. Many thanks to sqlbot, as his talk of callbacks led me to study where things were actually ending their execution.
So apparently when using AWS Lambda, if the "context" ends before your callbacks get called, you don't get your callbacks. So even though I had placed all of my callbacks like so: connect -> query -> end, the first callback of the chain from connect never gets called because "context.succeed" was getting called right afterwards, which ended execution.
Here's my code as of now (getting a proper query happening now):
var mysql = require('mysql');
var connection = mysql.createConnection({
...
});
exports.handler = (event, context) => {
try {
if (event.session.new) {
// New Session
console.log("NEW SESSION");
}
switch (event.request.type) {
case "LaunchRequest":
// Launch Request
console.log(`LAUNCH REQUEST`);
context.succeed(
generateResponse({},
buildSpeechletResponse("Welcome to an Alexa Skill, this is running on a deployed lamda function", true)
)
);
break;
case "IntentRequest":
// Intent Request
console.log(`Intent Request`);
console.log('Then run MySQL code:');
connection.connect(function(err) {
console.log('Inside connection.connect() callback');
if (!err) {
console.log("Database is connected ... ");
connection.query("INSERT INTO Users (user_id) VALUES ('TESTNAME')",
function(err, result) {
console.log("Inside connection.query() callback")
if (!err) {
console.log("Query Successful! Ending Connection.");
connection.end();
} else {
console.log("Query error!");
}
});
} else {
console.log("Error connecting database ..." + err.message);
}
context.succeed(
generateResponse({},
buildSpeechletResponse("Welcome to the incredible intelligent MySQLable Alexa!", true)
)
);
});
break;
case "SessionEndedRequest":
// Session Ended Request
console.log(`SESSION ENDED REQUEST`);
break;
default:
context.fail(`INVALID REQUEST TYPE: ${event.request.type}`);
}
} catch (error) {
context.fail(`Exceptiodn: ${error}`)
}
};
//Helpers
buildSpeechletResponse = (outputText, shouldEndSession) => {
return {
outputSpeech: {
type: "PlainText",
text: outputText
},
shouldEndSession: shouldEndSession
};
};
generateResponse = (sessionAttributes, speechletResponse) => {
return {
version: "1.0",
sessionAttributes: sessionAttributes,
response: speechletResponse
};
};

Sails passport js integration using sails as REST API

I am trying to use passport js for authentication in my local mysql database. I am using postman extension to test the application.
I am sending two fields i.e. username and password for authentication. When any one of the field is blank then response is shown in json format as
{
"message": "Missing credentials",
"user": false
}
But when I pass values for both the fields I get 500 internal server error.
error: Sending 500 ("Server Error") response:
TypeError: Cannot read property 'message' of undefined
at d:\Test\api\controllers\AuthController.js:25:23
at Strategy.strategy.error (d:\Test\node_modules\passport\lib\middleware\authenticate.js:333:18)
at Strategy.authenticate (d:\Test\node_modules\passport-local\lib\strategy.js:94:17)
at attempt (d:\Test\node_modules\passport\lib\middleware\authenticate.js:341:16)
at authenticate (d:\Test\node_modules\passport\lib\middleware\authenticate.js:342:7)
at Object.module.exports.login (d:\Test\api\controllers\AuthController.js:37:7)
at bound (C:\Users*\AppData\Roaming\npm\node_modules\sails\node_modules\lodash\dist\lodash.js:729:21)
at routeTargetFnWrapper (C:\Users*\AppData\Roaming\npm\node_modules\sails\lib\router\bind.js:179:5)
at callbacks (C:\Users*\AppData\Roaming\npm\node_modules\sails\node_modules\express\lib\router\index.js:164:37)
at param (C:\Users*\AppData\Roaming\npm\node_modules\sails\node_modules\express\lib\router\index.js:138:11)
at pass (C:\Users*\AppData\Roaming\npm\node_modules\sails\node_modules\express\lib\router\index.js:145:5)
at nextRoute (C:\Users*\AppData\Roaming\npm\node_modules\sails\node_modules\express\lib\router\index.js:100:7)
at callbacks (C:\Users*\AppData\Roaming\npm\node_modules\sails\node_modules\express\lib\router\index.js:167:11)
at C:\Users*\AppData\Roaming\npm\node_modules\sails\lib\router\bind.js:187:7
at alwaysAllow (C:\Users*\AppData\Roaming\npm\node_modules\sails\lib\hooks\policies\index.js:207:11)
at routeTargetFnWrapper (C:\Users*\AppData\Roaming\npm\node_modules\sails\lib\router\bind.js:179:5) [TypeError: Cannot read property 'message' of undefined]**
Below is my AuthController
var passport=require('passport');
login:function(req,res){
passport.authenticate('local', function(err, user, info) {
if ((err) || (!user)) {
return res.send({
message:info.message,
user: user
});
}
req.logIn(user, function(err) {
if (err) res.send(err);
return res.send({
message:"User Loged In",//info.message,
user: user
});
});
})(req, res);
}
};
I am using the below model for testing
module.exports = {
tableName: 'users',
connection:'TestDB',
autoCreatedAt:false,
autoUpdatedAt:false,
attributes: {
username:{
type:'string',
required:true
},
password:{
type:'string',
required:true
},
toJSON: function() {
var obj = this.toObject();
delete obj.password;
return obj;
}
}
};
The table which contains the username and password also has other fields like country. Is there any way I can authenticate it using passport.
did you do the sixth step of this link
module.exports.http = {
middleware: {
passportInit : require('passport').initialize(),
passportSession : require('passport').session(),
order: [
'startRequestTimer',
'cookieParser',
'session',
'passportInit',
'passportSession',
'myRequestLogger',
'bodyParser',
'handleBodyParserError',
'compress',
'methodOverride',
'poweredBy',
'router',
'www',
'favicon',
'404',
'500'
],
}
};
maybe you want to see sails-hook-sanpassport, is easy and fast