Cannot set headers after they are sent to the client - Node.js - node.js

Basically, I get an error saying "Cannot set headers after they are sent to the client". i tried redoing everything.
this is the form code:
const loader = document.querySelector('.loader');
// selecionar inputs
const submitBtn = document.querySelector('.submit-btn');
const name = document.querySelector('#name');
const email = document.querySelector('#email');
const password = document.querySelector('#password');
const number = document.querySelector('#number');
const tac = document.querySelector('#terms-and-cond');
const notification = document.querySelector('#notifications');
submitBtn.addEventListener('click', () => {
if(name.value.length < 3){
showAlert('name must be 3 letters long');
} else if (!email.value.length){
showAlert('enter your email');
} else if (password.value.length < 8){
showAlert('password should be 8 letters long');
} else if (!number.value.length){
showAlert('enter your phone number');
} else if (!Number(number.value) || number.value.length < 10){
showAlert('invalid number, please enter valid one');
} else if (!tac.checked){
showAlert('You must agree to our terms and conditions');
} else{
// para dar submit
loader.style.display = 'block';
sendData('signup', {
name: name.value,
email: email.value,
password: password.value,
number: number.value,
tac: tac.checked,
notification: notification.checked,
seller: false
})
}
})
// mandar a função dos dados
const sendData = (path, data) => {
fetch(path, {
method: 'post',
headers: new Headers({'Content-Type': 'application/json'}),
body: JSON.stringify(data)
}).then((res) => res.json())
.then(response => {
processData(response);
})
}
const processData = (data) => {
loader.style.display = null;
if(data.alert){
showAlert(data.alert);
}
}
const showAlert = (msg) => {
let alertBox = document.querySelector('.alert-box');
let alertMsg = document.querySelector('.alert-msg');
alertMsg.innerHTML = msg;
alertBox.classList.add('show');
setTimeout(() => {
alertBox.classList.remove('show');
}, 3000);
}
again, and I still get the same error...
and this is the server code:
// importar os packages
const express = require('express')
const admin = require('firebase-admin');
const bcrypt = require('bcrypt');
const path = require('path');
// setup da firebase - admin
let serviceAccount = require("./haza---pap-firebase-adminsdk-lrx0l-0da425a226.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
let db = admin.firestore();
// declarar um caminho estatico
let staticPath = path.join(__dirname, "public");
// inicializar o express.js
const app = express();
//middlewares
app.use(express.static(staticPath));
app.use(express.json());
// routes
// home route
app.get("/", (req, res) => {
res.sendFile(path.join(staticPath, "index.html"));
})
//route do signup
app.get('/signup', (req, res) =>{
res.sendFile(path.join(staticPath, "signup.html"));
})
app.post('/signup', (req, res) => {
let {name, email, password, number, tac, notification } = req.body;
// validações do form
if (name.length < 3){
return res.json({'alert': 'name must be 3 letters long'});
} else if (!email.length){
return res.json({'alert': 'enter your email'});
} else if (password.length < 8){
return res.json({'alert' :'password should be 8 letters long'});
} else if (!number.length){
return res.json({'alert' : 'enter your phone number'});
} else if (!Number(number) || number.length < 10){
return res.json({'alert' :'invalid number, please enter valid one'});
} else if (!tac){
return res.json({'alert' : 'You must agree to our terms and conditions'});
} else {
// guardar utilizadores na base de dados
db.collection('users').doc(email).get()
.then(user => {
if(user.exists){
return res.json({'alert': 'email already exists'});
} else{
// encriptar a password antes de guarda-la.
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(password, salt, (err, hash) => {
req.body.password = hash;
db.collection('users').doc(email).set(req.body)
.then(data => {
res.json({
name: req.body.name,
email: req.body.email,
seller: req.body.seller,
})
})
})
})
}
})
}
res.json('data recieved');
})
//route do 404
app.get('/404', (req, res) => {
res.sendFile(path.join(staticPath, "404.html"));
})
app.use((req, res) => {
res.redirect('/404');
})
app.listen(3000, () => {
console.log('listening on port 3000.......');
})

You are setting some data in response obj after send is being processed by node js.
Try using: res.end() after your all res.send.. events
Ref: https://www.geeksforgeeks.org/express-js-res-end-function/#:~:text=end()%20Function,-Last%20Updated%20%3A%2005&text=The%20res.,Node%20core%2C%20specifically%20the%20response.

You are sending response twice in /signup route.
Remove res.json('data recieved'); and it should work fine

That Error happens when you res. and then res. again to the client. Responding to the client several times is erroneous in node.
Check your route req,res cycles. Remember middleware .use() execute sequentially, and routes execute usually in between so...good luck

Related

"Cannot set headers after they are sent to the client" - node.js, express.js

I am trying to implement a reset password flow in my application. In this post request, I am first checking if the database has the "linkPass" for the passed email which stores the expiration of the link. If not first check that the email is associated with a user account. If yes it generates a link and sends it to their email. If not (and that is where the problem occurs) it sends the client back to the form and displays a message.
So as you can see every condition redirects you to the same page, but if the user does not exist I get the cannot set headers error. I know what is the problem just don't know how to resolve it. Tried to return the redirections but didn't help...
app.post(
"/requireNewPassword",
tryCatch(async (req, res) => {
const { email, modulName, continueUrl } = req.body;
const ipAddress = await axios
.get("https://api.ipify.org")
.then((res) => {
return res.data;
})
.catch((err) => console.log(err));
const [linkPass] = await getLinkPasses(email, "last");
let linkAvailable = false;
//check if linkPass is available and expired
if (!linkPass || linkPass.expiration < Date.now()) {
linkAvailable = false;
const user = await admin
.auth()
.getUserByEmail(email)
.then(async (user) => {
return user;
})
.catch((err) => {
//if user does not exist
console.log(err.message);
if (err.code === "auth/user-not-found") {
linkAvailable = true;
console.log(`${email} does not exist!`);
return res.redirect(
`/?email=${email}&linkAvailable=${linkAvailable}`
);
}
});
//if user exits
if (user) {
const link_id = await generateLinkPass(email, 5, true);
//get firebase oobCode
const pwdResetLink = await admin
.auth()
.generatePasswordResetLink(email)
.then((link) => {
const queryParams = new URLSearchParams(link);
const oobCode = queryParams.get("oobCode");
const apiKey = queryParams.get("apiKey");
const recoveryLink = `${URL_PWD}/resetPassword/? link_id=${link_id}&email=${email}&apiKey=${apiKey}&oobCode=${oobCode}&continueUrl=${continueUrl}`;
console.log("Password recovery link has been created");
return recoveryLink;
})
.catch((err) => {
//if cannot generate pwdResetLink
console.log(
JSON.parse(
err.errorInfo.message.match(/Raw server response: "(.*)"/)[1]
).error.message
);
});
if (pwdResetLink) {
axios
.post(`${URL_EFDS}/sendEmailTemplate`, {
recipient: {
name: user.displayName || "John Doe",
email: email,
ip_address: ipAddress,
},
recoveryLink: pwdResetLink,
moduleName: modulName,
apiKey: EFDS_APIKEY,
templateType: "forgottenPassword",
})
.then((res) => console.log(res.data))
.catch((err) => console.log(err.data));
console.log(`Password recovery email has been sent to ${email}`);
return res.redirect(
`/?email=${email}&linkAvailable=${linkAvailable}`
);
} else {
//if pwdResetLink does not exsist
console.log("pwdResetLink does not exsist");
return res.redirect("partials/error", { err });
}
} else {
console.log("user does not exist");
const link_id = await generateLinkPass(email, 0, false);
return res.redirect(`/?email=${email}&linkAvailable=${linkAvailable}`); //this line causing the error
}
} else {
console.log("Valid link is already available");
linkAvailable = true;
//hiaba allitom true-ra a linkAvailablet, a get requestnel figyeli hogy az emailhez letrajott e linkPass, de nem jott letre, igy nem mutat uzenetet
return res.redirect(`/?email=${email}&linkAvailable=${linkAvailable}`);
}
})
);

Fastify-passport req.user is null, deserializer never gets called nor does the serializer

i've been struggling a lot with fastify-passport library, mainly because it seems that nobody uses it and there aren't any good examples or issues related to it
anyhow, i have some routes defined like this:
const adminRoutes = [
{
handler: (req,res) => {console.log(req.user) },
url: "/logout",
method: "GET"
}
]
this route is then registered by fastify like this (do note that there are more routes, however, this is just a code snippet)
adminRoutes.forEach((route, index) => {
fastify.route(route)
})
i am using passport local strategy to autenticate, it's configured like this
fastifyPassport.use('login', new passportLocal(async function (username, password, done) {
try {
let data = await dbQuery(`SELECT * FROM \`users\` WHERE username="${username}"`)
if (data[0].length > 0) {
data = data[0][0]
if (username === data.username && bCrypt.compareSync(password, data.hashedPassword)) {
return done(null, username)
} else return done(null, false)
}
} catch (error) {
console.log(error)
done(error);
}
}))
this seems to work, in all my tests, the strategy did what it had to do, when the right user and password is passed all the checks seems to pass and gets all the way down to return done(null, username)
this is my serializer and deserializer
fastifyPassport.registerUserSerializer(async (user, request) => {
return user
});
fastifyPassport.registerUserDeserializer(async (username, request) => {
let data = await dbQuery(`SELECT * FROM users WHERE username="${username}"`);
return data[0][0]
});
i've checked with both a debugger and console logs, they don't seem to ever get called
in fact when i go to /logout my console throws a null
also no session cookie get generated (uncertain of this, sometimes it seems to generate, other times it doesn't)
the complete code is quite long, however, it's probably necessary to see whats the issue
so here is it
this is the server
require('dotenv').config()
const fastify = require('fastify')({ logger: false })
const fastifyPassport = require('fastify-passport')
const fastifySecureSession = require('fastify-secure-session')
const passportLocal = require('passport-local').Strategy
const BannedEverywhere = ["DROP", "CREATE"]
const bCrypt = require('bcryptjs')
const fs = require('fs')
const path = require('path')
const port = process.env.PORT || 3000
const routes = require('./routes')
const mysql = require('mysql2/promise');
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
password: process.env.DB_PASSWD,
database: 'users',
port: 3306
});
console.log("Server Connected!")
function dbQuery(dataExpression) {
if (BannedEverywhere.some(i => dataExpression.includes(i))) {
return "invalid"
}
return pool.query(dataExpression)
}
fastify.register(require('fastify-cors'), {
origin: (origin, cb) => {
cb(null, true);
return
}
})
fastify.register(fastifySecureSession, { key: fs.readFileSync(path.join(__dirname, 'secret-key'))})
fastify.register(fastifyPassport.initialize())
fastify.register(fastifyPassport.secureSession())
fastifyPassport.registerUserSerializer(async (user, request) => {
return user
});
fastifyPassport.registerUserDeserializer(async (username, request) => {
let data = await dbQuery(`SELECT * FROM users WHERE username="${username}"`);
return data[0][0]
});
fastifyPassport.use('login', new passportLocal(async function (username, password, done) {
try {
let data = await dbQuery(`SELECT * FROM \`users\` WHERE username="${username}"`)
if (data[0].length > 0) {
data = data[0][0]
if (username === data.username && bCrypt.compareSync(password, data.hashedPassword)) {
console.log("got here")
return done(null, username)
} else return done(null, false)
}
} catch (error) {
console.log(error)
done(error);
}
}))
const postData = async (req, reply, err, user, info, status) => {
if (err !== null) { console.warn("ah habido un error: " + err) }
else if (user) {
const params = req.body
const newData = {
universidad: params.universidad,
facultad: params.facultad,
nombreExamen: params.nombreExamen,
fechaExamen: params.fechaExamen,
convocatoriaEspecial: params.convocatoriaEspecial,
convocatoriaExtraordinaria: params.convocatoriaExtraordinaria,
curso: params.curso
}
const response = await dbQuery('INSERT INTO `examenes` VALUES("'
+ newData.universidad + '","' + newData.facultad + '","'
+ newData.nombreExamen + '","' + newData.fechaExamen + '",'
+ newData.convocatoriaEspecial + ',' + newData.convocatoriaExtraordinaria + ','
+ newData.curso + ")")
return reply.send({ status: 200, newData, response })
}
}
const deleteData = async (req, reply, err, user, info, status) => {
if (err !== null) { console.warn("ah habido un error: " + err) }
else if (user) {
const { universidad, facultad, nombreExamen, fechaExamen, curso } = req.body
const response = await dbQuery('DELETE FROM `examenes` WHERE universidad="' + universidad + '" and facultad="' + facultad + '" and nombreExamen="' + nombreExamen + '" and date(fechaExamen)="' + fechaExamen + '" and curso=' + curso)
return reply.send({ status: 200, response })
}
}
const logout = async (req, reply, err, user, info, status) => {
if (err !== null) { console.warn("ah habido un error: " + err) }
console.log(req)
console.log("--------------------------------")
console.log(user)
console.log("--------------------------------")
console.log(info)
console.log("--------------------------------")
console.log(status)
}
fastify.get(
"/login",
(req, reply) => {
return reply.sendFile('./login/index.html')
}
)
fastify.post(
"/login",
{preValidation: fastifyPassport.authenticate('login',(req, reply)=>{
reply.send({redirect: "/"})
})},
() => {}
)
const adminRoutes = [
{
handler: () => {},
preValidation: fastifyPassport.authenticate("login", deleteData),
url: '/api/deleteData',
method: 'POST'
},
{
handler: () => {},
preValidation: fastifyPassport.authenticate("login", postData),
url: '/api/postData',
method: 'POST'
},
{
handler: () => {},
preValidation: fastifyPassport.authenticate("login", (req, reply) => { return reply.sendFile('./entry/index.html') }),
url: '/entry',
method: 'GET'
},
{
handler: (req,res) => {console.log(req.user) },
url: "/logout",
method: "GET"
}
]
const start = async () => {
try {
await fastify.listen(port)
} catch (err) {
console.error(err)
process.exit(1)
}
}
fastify.register(require('fastify-static'), {
root: __dirname,
prefix: '/', // optional: default '/'
})
routes.forEach((route, index) => {
fastify.route(route)
})
adminRoutes.forEach((route, index) => {
fastify.route(route)
})
start()
before you comment, i know cors shouldn't be left like that to go in production, don't worry, i know
also logout function was just a test, to try solve this very issue
this is the code that calls the login post request
const HOST = location.origin;
const axiosApp = axios.create({
baseURL: HOST,
});
document
.querySelector("#submit")
.addEventListener("click", async function () {
let response = await axiosApp.post(`${HOST}/login`, {
username: document.querySelector("#username").value,
password: document.querySelector("#password").value,
});
console.log(response);
if(response.status == 200) {
document.location.href = response.data.redirect
}
});
unrelated, bannedEverywhere is just a rudimentary "security" check, i do plan to improve it, for now it's just a better than nothing, and, i do plan to change all the var + string + var chains with template strings
answering my own question:
when you add a callback like that in the preValidation
{preValidation: fastifyPassport.authenticate('login',(req, reply)=>{
reply.send({redirect: "/"})
})}
fastify-passport no longer handles serialization and deserialization on his own, that comes with... a lot more issues, changing the prevalidation to
{preValidation: fastifyPassport.authenticate('login'{successRedirect:"/"})
makes you have to handle 302 codes in the browser, which can be problematic to say the least

Error: connect ECONNREFUSED 127.0.0.1:5000

I´ve the file cases.js to create the route I want:
const express = require("express");
const { requireSignin } = require("../controllers/auth");
const { getCases } = require("../controllers/cases");
const { scrapingData } = require("../scrapingData");
const router = express.Router();
router.get("/cases", requireSignin, scrapingData, getCases);
module.exports = router;
requireSignin from controllers/auth looks like this:
exports.requireSignin = expressJwt({
secret: process.env.JWT_SECRET,
userProperty: "auth",
});
scrapingData as middleware I have:
const updateMongoRecords = async () => {
mongoose.Promise = global.Promise;
mongoose.set("debug", true);
Case.deleteMany({}, (err, result) => {
if (err) {
console.log(err);
} else {
console.log("Successfully deleted all records");
}
});
const dataPath = Path.join(__dirname, "files", "covid-data.csv");
try {
let headers = Object.keys(Case.schema.paths).filter(
(k) => ["_id", "__v"].indexOf(k) === -1
);
await new Promise((resolve, reject) => {
let buffer = [],
counter = 0;
let stream = fs
.createReadStream(dataPath)
.pipe(csv())
.on("error", reject)
.on("data", async (doc) => {
stream.pause();
buffer.push(doc);
counter++;
// log(doc);
try {
if (counter > 10000) {
await Case.insertMany(buffer);
buffer = [];
counter = 0;
}
} catch (e) {
stream.destroy(e);
}
stream.resume();
})
.on("end", async () => {
try {
if (counter > 0) {
await Case.insertMany(buffer);
buffer = [];
counter = 0;
resolve();
}
} catch (e) {
stream.destroy(e);
}
});
});
} catch (e) {
console.error(e);
} finally {
process.exit();
}
};
exports.scrapingData = async (req, res, next) => {
const url = "https://covid.ourworldindata.org/data/owid-covid-data.csv";
const path = Path.resolve(__dirname, "files", "covid-data.csv");
const response = await Axios({
method: "GET",
url: url,
responseType: "stream",
});
response.data.pipe(fs.createWriteStream(path));
return new Promise((resolve, reject) => {
response.data.on("end", () => {
resolve(updateMongoRecords());
next();
});
response.data.on("error", (err) => {
reject(err);
});
});
};
And getCases.js inside controllers/cases I have:
const Case = require("../models/case");
exports.getCases = async (req, res) => {
const cases = await Case.find().then((cases) => res.json(cases));
};
With this code I am able to fetch in the route /cases all the cases in the client side (like postman) and it shows all of them. But the problem is that I can´t make any other requests (like signing out, which works fine if I don´t make the get request for the cases like before) afterwards because client (postman) gives the error: GET http://localhost:5000/signout
Error: connect ECONNREFUSED 127.0.0.1:5000
in case you want to see the code for signout is like this:
const express = require("express");
const { signup, signin, signout } = require("../controllers/auth");
const router = express.Router();
router.post("/signup", userSignupValidator, signup);
router.post("/signin", userSigninValidator, signin);
router.get("/signout", signout);
inside controllers/auth.js:
exports.signout = (req, res) => {
res.clearCookie("t");
return res.json({ message: "Signout successfully done" });
};
Any help on this??

node js cant post unauthorised

i have been facing this error of 401 unauthorized error when i tried to mount my isLoggedinMiddlware.js, and even when i can manage to print the token stored, it stil says its
authorised. Any advice or help would be appreciated! Have a nice day.
this is my isLoggedinMiddleware.js
const jwt = require("jsonwebtoken");
const JWT_SECRET = process.env.JWT_SECRET;
module.exports = (req, res, next) => {
const authHeader = req.headers.authorization;
if (authHeader === null || authHeader === undefined || !authHeader.startsWith("Bearer ")) {
res.status(401).send();
return;
}
const token = authHeader.replace("Bearer ", "");
jwt.verify(token, JWT_SECRET, { algorithms: ["HS256"] }, (error, decodedToken) => {
if (error) {
res.status(401).send();
return;
}
req.decodedToken = decodedToken;
next();
});
};
this is my post api
app.post("/listings/",isLoggedInMiddleware,(req,res)=>{
listings.insert(req.body,(error,result)=>{
if(error){
console.log(error)
console.log(req.body)
console.log(isLoggedInMiddleware)
res.status(500).send('Internal Server Error')
return;
}
console.log(result)
res.status(201).send({"Listing Id":result.insertId})
})
})
This is my front end
const baseUrl = "http://localhost:3000";
const loggedInUserID = parseInt(localStorage.getItem("loggedInUserID"));
const token = localStorage.getItem("token")
console.log(token)
if(token === null || isNaN(loggedInUserID)){
window.location.href = "/login/"
}else{
$('#logoff').click(function(){
event.preventDefault();
localStorage.removeItem('token')
localStorage.removeItem('loggedInUserID')
window.alert('Logging out now')
window.location.href = "/login/"
})
$(document).ready(function () {
$('#submitbtn').click((event) => {
const loggedInUserID = parseInt(localStorage.getItem("loggedInUserID"));
// middleware = {headers:{'Authorization':'Bearer '+token},data:{id: loggedInUserID}}
event.preventDefault();
const itemName = $("#itemName").val();
const itemDescription = $("#itemDescription").val();
const price = $('#price').val();
const image = $('#image').val();
const requestBody = {
itemName: itemName,
itemDescription: itemDescription,
price: price,
fk_poster_id: loggedInUserID,
imageUrl: image
}
console.log(requestBody);
axios.post(`${baseUrl}/listings/`,{headers:{'Authorization':'Bearer '+token},data:{id: loggedInUserID}}, requestBody)
.then((response) => {
window.alert("successfully Created")
})
.catch((error) => {
window.alert("Error")
console.log(requestBody)
})
})
})
}
i can managed to get the token i stored when i log in, however, it stills say 401 unauthorised.

NodeJS mongoose bug in if statement

The code should work like that: If target username and logged in user username is in the same fight then the fight should not be created and user should be redirected to /arena. But the problem is that even though when I finish the fight then fast hit F5 the new fight with the same user and same target creates again. What could cause this problem ?
app.get('/arena/fight/user/:username', async (req, res) => {
User.findById(req.session.userId).then((user) => {
if(req.params.username === user.username) {
return res.redirect('/arena');
}
});
const target = await User.findOne({username: req.params.username});
const user = await User.findById(req.session.userId);
const fight = await TempFight.findOne({user: user.username});
if(fight && target.username === fight.target) {
return res.redirect('/arena');
} else {
const battleId = uuid4();
const newtempFight = TempFight({
target: target.username,
user: user.username,
code: battleId
});
await newtempFight.save();
}
if(req.session.userId) {
res.render(__dirname + '/views/arena/battle', {messages, user, enemy, userItems, enemyItems, liveitems, fights, lostFights});
} else {
return res.redirect('/');
}
});
Try this
app.get('/arena/fight/user/:username', async (req, res) => {
const user = await User.findById(req.session.userId);
if(req.params.username === user.username) {
return res.redirect('/arena');
}
const target = await User.findOne({username: req.params.username});
const fight = await TempFight.findOne({user: user.username});
if(fight && target.username === fight.target) {
return res.redirect('/arena');
} else {
const battleId = uuid4();
const newtempFight = TempFight({
target: target.username,
user: user.username,
code: battleId
});
await newtempFight.save();
}
if(req.session.userId) {
res.render(__dirname + '/views/arena/battle', {messages, user,
enemy, userItems, enemyItems, liveitems, fights, lostFights});
} else {
return res.redirect('/');
}
});

Resources