Node.JS server crashes - node.js

I have a problem, I'm creating app where you can buy tickets for cultural events. The problem is the server crashes when I try buying tickets when there is no tickets left. It gives this message: "[nodemon] app crashed - waiting for file changes before starting...".
try {
const { email, firstName, lastName, phoneNumber } = req.body;
const id = req.query.id;
let eventFound: any;
const event = await Event.findById(id, (error, result) => {
if(!error) eventFound = result;
else throw new Error(error.message);
});
Ticket.find({ eventId: event?.id }, (error, tickets) => {
if (!error) {
if (event?.toJSON().maxTicketsAmount - 1 < tickets.length) {
throw new Error("Not enough tickets");
}
}
});
const paymentIntent = await stripe.paymentIntents.create({
amount: eventFound.ticketPrice, // NEEDS TO BE ABOVE SOME VALUE!!!!!!!
currency: "pln",
payment_method_types: ["card"],
receipt_email: email,
metadata: { integration_check: "accept a payment" },
});
console.log(paymentIntent);
const ticket = new Ticket({
email: email,
firstName: firstName,
lastName: lastName,
phoneNumber: phoneNumber,
eventId: eventFound.id,
purchaseDate: new Date(),
});
ticket.save((error) => {
if (error) {
throw new Error(error.message);
}
});
res.status(200).send(paymentIntent.client_secret);
} catch (error) {
res.status(500).json({ statusCode: 500, message: error.message });
}

the issue is because you are throwing the error:
Ticket.find({ eventId: event?.id }, (error, tickets) => {
if (!error) {
if (event?.toJSON().maxTicketsAmount - 1 < tickets.length) {
throw new Error("Not enough tickets");
}
}
and then it is getting "catched" in the try/catch statement. As a result you are manually crashing the server. Instead you should:
Ticket.find({ eventId: event?.id }, (error, tickets) => {
if (!error) {
if (event?.toJSON().maxTicketsAmount - 1 < tickets.length) {
res.status(404).send("no more tickets")
}
}

Related

TypeError: Cannot read properties of undefined (reading 'buffer')

In the front-end use form-data to send a post/put method to save data and edit data in the server. In the front-end have a problem with the image field. when new data is added. the new data will be saved. but when they try to edit data, the data changes made will not be saved, only saved if the image field is changed. (this problem only occurs in ios).
in IOS when they try to update data without changing the image field server will be crashed
router.put("/:id", authenticate, async (req, res) => {
uploads(req, res, function (err) {
if (err instanceof multer.MulterError) {
return send(res, RESPONSE.FILE_TOO_LARGE);
} else if (err) {
return send(res, RESPONSE.UNKNOWN_ERROR);
}
const params = {
Bucket: process.env.AWS_BUCKET_NAME,
Key: `uploads/${uuid()}`,
Body: req.file.buffer,
};
s3.upload(params, async (error, data) => {
if (error) {
send(res, RESPONSE.UNKNOWN_ERROR);
}
try {
const id = req.params.id;
const updatedData = {
first_name: req.body.first_name,
last_name: req.body.last_name,
phone: req.body.phone,
email: req.body.email,
image: data.Key,
};
const options = { new: true };
await SomeModel.findByIdAndUpdate(id.trim(), updatedData, options);
return send(res, RESPONSE.SUCCESS);
} catch (err) {
return res.status(400).send(err.message);
}
});
Sounds like you should only do the S3 upload and set image if req.file is present
router.put("/:id", authenticate, (req, res) => {
uploads(req, res, async (err) => {
if (err instanceof multer.MulterError) {
return send(res, RESPONSE.FILE_TOO_LARGE);
} else if (err) {
return send(res, RESPONSE.UNKNOWN_ERROR);
}
const id = req.params.id;
const updatedData = {
first_name: req.body.first_name,
last_name: req.body.last_name,
phone: req.body.phone,
email: req.body.email,
};
try {
if (req.file) {
const { Key } = await s3
.upload({
Bucket: process.env.AWS_BUCKET_NAME,
Key: `uploads/${uuid()}`,
Body: req.file.buffer,
})
.promise();
updatedData.image = Key;
}
await SomeModel.findByIdAndUpdate(id.trim(), updatedData, { new: true });
return send(res, RESPONSE.SUCCESS);
} catch (err) {
return res.status(400).send(err.message);
}
});
});

Cannot set headers after they are sent to the client error is showing

Error is showing if a user is already exiting. The error is not showing if the user does not exist in the database
Plz tell me if there is any better way to check the username thank you in advance
exports.updateUser = async(req, res) => {
if (req.body.userId === req.params.id) {
if (req.body.userName || req.body.email) {
await User.findOne({
$or: [{
email: req.body.email
}, {
username: req.body.userName
}]
}).exec((err, user) => {
if (err) {
return res.status(500).json(err);
} else if (user) {
if (user.username === req.body.userName) {
return res.status(400).json({ error: "User Name already exists" });
//err = "User Name already exists";
} else {
return res.status(400).json({ error: "Email already exists" });
}
}
})
}
if (req.body.password) {
const salt = await bcrypt.genSalt(10);
req.body.password = await bcrypt.hash(req.body.password, salt);
}
await User.findByIdAndUpdate(
req.params.id, { $set: req.body }, { new: true }
).exec((err, user) => {
if (err) {
return res.status(500).json(err);
}
if (user) {
return res.status(200).json("User Updated successfully!"); // here error is showing.
}
});
} else {
res.status(401).json("You can update only your account!");
}
};
Don't send response inside exec, because if after sending response it'll execute rest of the code and try to send a response but response is already sent.
exports.updateUser = async (req, res) => {
try {
if (req.body.userId === req.params.id) {
if (req.body.userName || req.body.email) {
await User.findOne({
$or: [
{
email: req.body.email,
},
{
username: req.body.userName,
},
],
}).exec((err, user) => {
if (err) {
throw {
code: 400,
error: err,
};
} else if (user) {
if (user.username === req.body.userName) {
throw {
code: 400,
error: { error: "User Name already exists" },
};
}
throw {
code: 400,
error: "Email already exists",
};
}
});
}
if (req.body.password) {
const salt = await bcrypt.genSalt(10);
req.body.password = await bcrypt.hash(req.body.password, salt);
}
await User.findByIdAndUpdate(
req.params.id,
{ $set: req.body },
{ new: true }
).exec((err, user) => {
if (err) {
throw {
code: 500,
error: err,
};
}
if (user) {
return res.status(200).json("User Updated successfully!"); // here error is showing.
}
});
} else {
throw {
code: 401,
error: "You can update only your account!",
};
}
} catch (e) {
res.status(e.code).json(e.error);
}
};

aws-sdk SNS on NodeJs don't catches error

I try to implement a phone number verification by SMS with aws-sdk SNS and Mongoose,
But when I test it with a wrong phone number, like, some gibberish instead of real phone,
the error goes to the console, but then it won't get caught by catch and just crashes it all.
Here is my code
// requires, etc is above here
const verifyPhone = async (req, res) => {
const { phoneNumber } = req.body;
const { userId } = req.user;
let code = generateCode(6);
const session1 = await mongoose.startSession();
try {
session1.startTransaction();
const newCode = await PhoneCode.create(
[
{
userId,
code,
},
],
{ session: session1 }
);
sns.publish(
{
MessageAttributes: {
"AWS.SNS.SMS.SenderID": {
DataType: "String",
StringValue: "Testing",
},
"AWS.SNS.SMS.SMSType": {
DataType: "String",
StringValue: "Promotional",
},
},
Message: "Your code: " + code,
PhoneNumber: "65f76fguyg",
},
(err, result) => {
if (!err) {
session1.commitTransaction().then(() => {
console.log("transaction committed");
res.status(200).json({ message: "SMS sent", result });
});
} else {
console.log(err);
throw new Error(err);
}
}
);
} catch (e) {
await session1.abortTransaction();
res.status(500).json({ message: "Something went wrong!", e });
} finally {
session1.endSession();
}
};
module.exports = verifyPhone;
I suspect that the issue is in throwing the error from inside of sns.publish 's callback, but I don't understand, how else I could do that
Also, if I enter right phone number, the SMS gets delivered, I get the response, but then
mongoose crashes, saying
UnhandledPromiseRejectionWarning: MongoTransactionError: Cannot call commitTransaction after calling abortTransaction
I've just got to a solution. The solution for me was to promisify the sns.publish, after that all went like a charm.
const promisifySNS = (args) => {
return new Promise((resolve, reject) => {
sns.publish(args, (err, result) => {
if (err) {
return reject(err);
}
resolve(result);
});
});
};
Funny enough, if you use more generic promisify, for any function
const promisify = (fn, ...args) => {
return new Promise((resolve, reject) => {
fn(...args, (err, result) => {
if (err) {
return reject(err);
}
resolve(result);
});
});
};
await promisify(sns.publish, opts);
that wouldn't work for some reason.

Try/catch sends email and creates DB entry even though it shouldn't

I have a problem regarding try/catch in Node.JS, to be more exact in Express.
I have an app for selling tickets for festivals, concerts etc. and there is limited number of tickets for each event. However when my server detects that there are no tickets left, creates one anyway and sends mail with the ticket.
app.post(
"/api/tickets",
[
body("email").trim().isEmail().isLength({ min: 8 }).normalizeEmail(),
body("firstName")
.trim()
.isString()
.isLength({ min: 2 })
.matches(/^[^0-9_!¡?÷?¿/\\+=##$%ˆ&*(){}|~<>;:[\]]{2,}$/),
body("lastName")
.trim()
.isString()
.isLength({ min: 2 })
.matches(/^[^0-9_!¡?÷?¿/\\+=##$%ˆ&*(){}|~<>;:[\]]{2,}$/),
body("phoneNumber")
.trim()
.isString()
.isLength({ min: 8 })
.matches(
/^((?<!\w)(\(?(\+|00)?48\)?)?[ -]?\d{3}[ -]?\d{3}[ -]?\d{3}(?!\w))$/
),
],
async (req: any, res: any, next: any) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
let err = new StatusError("Error while validating body", 400);
next(err);
}
const { id, email, firstName, lastName, phoneNumber } = req.body;
let eventFound: any;
const event = await Event.findById(id, (error, result) => {
if (!error) eventFound = result;
else {
let err = new StatusError("No event found", 404);
next(err);
}
});
Ticket.find({ eventId: event?.id }, (error, tickets) => {
if (!error) {
if (event?.toJSON().maxTicketsAmount - 1 < tickets.length) {
let err = new StatusError("No tickets left", 403);
next(err);
}
} else {
let err = new StatusError("No tickets found", 404);
next(err);
}
});
const paymentIntent = await stripe.paymentIntents.create({
amount: eventFound.ticketPrice, // NEEDS TO BE ABOVE SOME VALUE!!!!!!!
currency: "pln",
payment_method_types: ["card"],
receipt_email: email,
metadata: { integration_check: "accept a payment" },
});
if (!paymentIntent) {
let err = new StatusError("Creating payment intent failed", 400);
next(err);
}
const ticket = new Ticket({
email: email.trim(),
firstName: firstName.trim(),
lastName: lastName.trim(),
phoneNumber: phoneNumber.trim(),
eventId: eventFound.id,
purchaseDate: new Date(),
});
ticket.save((error) => {
if (error) {
let err = new StatusError("Error while saving to DB", 500);
next(err);
}
});
const qr = await toDataURL(ticket.id);
console.log(ticket.id);
if (!qr) {
let err = new StatusError("Error while creating QR Code", 400);
next(err);
}
const mailTemplate = `
<h1>Hello ${firstName} ${lastName}</h1>
<p>Thanks for buying ticket for ${eventFound.nameOfEvent}, in ${eventFound.place}, taking place on ${eventFound.dateOfEvent}</p>
<img src="${qr}">
`;
let message = {
from: env.email,
to: email,
subject: `Ticket for ${eventFound.nameOfEvent}`,
html: mailTemplate,
};
transporter.sendMail(message, (error, info) => {
if (error) {
return res.status(500).json({ error: error });
} else console.log("Mail sent:", info.response);
});
return res.status(200).send(paymentIntent.client_secret);
} catch (error) {
next(error);
}
}
);
Middleware for error handling:
app.use(function (err: any, req: any, res: any, next: any) {
console.error(err.message);
if (!err.statusCode) err.statusCode = 500;
res.status(err.statusCode).send(err.message);
});
Custom error class:
class StatusError extends Error {
code: number;
constructor(message: string, code: number) {
super();
this.message = message;
this.code = code;
}
}
export default StatusError;
The problem is that you your checking of tickets is async, so the rest of the function will execute no matter what. You need to wait for the result before you can decide what to do. The easiest is to nest the remainder of the route handler in the callback of the Ticket.find:
async (req: any, res: any, next: any) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
let err = new StatusError("Error while validating body", 400);
next(err);
}
const { id, email, firstName, lastName, phoneNumber } = req.body;
let eventFound: any;
const event = await Event.findById(id, (error, result) => {
if (!error) eventFound = result;
else {
let err = new StatusError("No event found", 404);
next(err);
}
});
Ticket.find({ eventId: event?.id }, (error, tickets) => {
if (!error) {
if (event?.toJSON().maxTicketsAmount - 1 < tickets.length) {
let err = new StatusError("No tickets left", 403);
next(err);
return;
}
} else {
let err = new StatusError("No tickets found", 404);
next(err);
return;
}
const paymentIntent = await stripe.paymentIntents.create({
amount: eventFound.ticketPrice, // NEEDS TO BE ABOVE SOME VALUE!!!!!!!
currency: "pln",
payment_method_types: ["card"],
receipt_email: email,
metadata: { integration_check: "accept a payment" },
});
if (!paymentIntent) {
let err = new StatusError("Creating payment intent failed", 400);
next(err);
}
const ticket = new Ticket({
email: email.trim(),
firstName: firstName.trim(),
lastName: lastName.trim(),
phoneNumber: phoneNumber.trim(),
eventId: eventFound.id,
purchaseDate: new Date(),
});
ticket.save((error) => {
if (error) {
let err = new StatusError("Error while saving to DB", 500);
next(err);
}
});
const qr = await toDataURL(ticket.id);
console.log(ticket.id);
if (!qr) {
let err = new StatusError("Error while creating QR Code", 400);
next(err);
}
const mailTemplate = `
<h1>Hello ${firstName} ${lastName}</h1>
<p>Thanks for buying ticket for ${eventFound.nameOfEvent}, in ${eventFound.place}, taking place on ${eventFound.dateOfEvent}</p>
<img src="${qr}">
`;
let message = {
from: env.email,
to: email,
subject: `Ticket for ${eventFound.nameOfEvent}`,
html: mailTemplate,
};
transporter.sendMail(message, (error, info) => {
if (error) {
return res.status(500).json({ error: error });
} else console.log("Mail sent:", info.response);
});
return res.status(200).send(paymentIntent.client_secret);
});
} catch (error) {
next(error);
}
}

MongoDB find always returning true when something doesnt exist (even on an empty database)

app.post('/sign-up', function (req, res) {
let emailValid = validator.validate(req.body.email);
let consent = req.body.consent ? true:false
if(emailValid && consent) {
const user = new UserModel({
name: req.body.firstName,
surname: req.body.surname,
email: req.body.email
})
UserModel.find({'email': req.body.email}, function(notFound, found) {
if(notFound) {
user.save().then(item => {
console.log('Saved successfully!');
res.render('submitSuccess', {data: req.body});
}).catch(err => {
res.status(400).render('404');
})
} else if(found) {
console.log('Exists');
res.status(404).render('submitSuccess', {data:req.body});
}
else {
res.status(404).render('404');
}
});
}
});
The intended functionality here is that if someone submits an email to the database that already exists, it does not then save a duplicate. However, it seems that found is returning true everytime, therefore nothing is getting saved.
Run this code:
app.post('/sign-up', function (req, res) {
let emailValid = validator.validate(req.body.email);
let consent = req.body.consent ? true : false
if (emailValid && consent) {
const user = new UserModel({
name: req.body.firstName,
surname: req.body.surname,
email: req.body.email
})
UserModel.find({ 'email': req.body.email }, function (err, found) {
if (err) {
console.error(err);
res.status(500).end();
return;
}
if (found.length == 0) {
user.save().then(item => {
console.log('Saved successfully!');
res.render('submitSuccess', { data: req.body });
}).catch(err => {
res.status(400).render('404');
})
} else {
console.log('Exists');
res.status(404).render('submitSuccess', { data: req.body });
}
});
}
});
What's in the err?

Resources