Node Express server not send messages with whatsapp-web-js - node.js

i have a problem with my custom server, i'm trying to setup an api that send whatsapp messages in react as a frontend.
So, i have actually a route that send the QR to the frontend (working fine), a route that handle the authentication event (working fine) and
PROBLEM HERE:
...a route that send a message to a specific number (NOT WORK)
here my server code...what i'm doing wrong? if i launch a POST request to the endpoint on postman, i get an infinite loading (no errors).
const express = require('express')
const router = express.Router()
const fs = require('fs')
const { Client, LocalAuth } = require('whatsapp-web.js')
const authStrategy = new LocalAuth({
clientId: 'adminSession',
})
const worker = `${authStrategy.dataPath}/session-admin/Default/Service Worker`
if (fs.existsSync(worker)) {
fs.rmdirSync(worker, { recursive: true })
}
const client = new Client({
takeoverOnConflict: true,
authStrategy,
})
const sessionData = {
client: 'admin',
session: true,
qrCodeScanned: true,
}
client.on('authenticated', (session) => {
fs.writeFile(
'waSession.json',
JSON.stringify(sessionData),
'utf-8',
(err) => {
if (!err) {
console.log('Session saved on disk...')
}
}
)
})
router.get('/whatsapp/auth', async (req, res) => {
const dir = './waSession.json'
fs.readFile(dir, (err, data) => {
if (data.length === 0) {
return res.status(200).json({
message: 'You need to login first',
})
} else {
return res.status(200).json({
message: 'You are logged in.',
})
}
})
})
router.get('/whatsapp', async (req, res) => {
try {
client.on('qr', (qr) => {
res.status(200).send({
message: 'Connect whatsapp with this qr-code',
qrCode: qr,
})
})
await client.initialize()
res.status(404)
} catch (err) {
res.send(err)
}
})
router.post('/whatsapp/send', async (req, res) => {
const { phoneNumber, message } = req.body
try {
client.on('ready', async () => {
const number = phoneNumber
const text = message
const chatId = number.substring(1) + '#c.us'
await client.sendMessage(chatId, text)
})
await client.initialize()
res.json('Messaggio inviato')
} catch (err) {
res.status(404).send(err)
await client.destroy()
}
})
module.exports = router

A Client represents one authenticated WhatsApp user, and you have only one global variable client. This implies that all incoming requests will represent the same WhatsApp user, even if several different real users send requests to your server in parallel. This is probably not what you intend.
I suggest that you use express-session to associate every client with a session. Then a user needs to create a client and authenticate it only once during a session. All subsequent requests in the same session will re-use that client, and the client.on(...) and client.initialize() commands will not be repeated.

Related

req.body undefined on get but not on post using axios and multer

I am sending user login and registration data with axios to my backend as get and post requests respectively, but I can't figure out why for the login (get) request, the req.body in express is undefined even though everything seems perfectly identical
In the react app I sent axios requests as shown:
const axiosConfig = {
headers: { "Content-Type": "multipart/form-data" }, // commenting this out and using defaults does nothing
};
function submitHandler(e) {
e.preventDefault();
const axiosUser = axios.create({ baseURL: "http://localhost:3100" });
let userData = new FormData();
userData.append("username", usernameRef.current.value);
userData.append("password", passwordRef.current.value);
if (formState === "login") {
for (let pair of userData.entries()) {
console.log(pair[0] + ", " + pair[1]); // username, x // password y as expected
}
console.log("LOGIN");
axiosUser
.get("/u", userData, axiosConfig)
.then((res) => console.log("success:", res))
.catch((err) => console.log("error:", err))
.then(() => {
navigate("/");
});
} else {
for (let pair of userData.entries()) {
console.log(pair[0] + ", " + pair[1]); // username, x // password y as expected
}
console.log("REGISTER");
axiosUser
.post("/u", userData, axiosConfig)
.then((res) => console.log("success:", res))
.catch((err) => console.log("error:", err))
.then(() => {
navigate("/");
});
}
}
In express, I parse the formData using multer upload.none() as my other routes do have image uploads to cloudinary:
const upload = multer({ storage }) // cloudinary storage
app.post(
"/u",
upload.none(),
asyncErrorWrapper(async function (req, res) {
const result = await User.findOne({ username: req.body.username });
console.log(result);
if (result) {
console.log(req.body.username);
return res.status(400).send("username already exists");
}
const hashedPw = bcrypt.hashSync(req.body.password, 10);
const newUser = new User({
username: req.body.username,
password: hashedPw,
});
await newUser.save();
console.log(newUser);
console.log(` > new user "${req.body.username}" created`);
res.status(200).send("user created");
})
);
app.get(
"/u",
upload.none(),
asyncErrorWrapper(async function (req, res) {
console.log("LOGIN");
console.log(req.body); // ! undefined, {} if parsed with bodyParser
console.log(req.body.username); // error, undefined with bodyParser
console.log(req.body.password); // error, undefined with bodyParser
res.send(req.body);
})
);
I have tried removing axiosConfig which did not change anything, and using another parser like bodyParser in place of multer logs req.body as an empty object.
Get Requests usually do not have bodies. Only Post/Put etc Requests have some. While some implementations would theoretically support Get requests with bodies, it is not recommended to do so and not within the HTTP specification.
If you take a look at the axios documentation it does not specify the possibility to add a body to axios.get only the request config.
You should also use a Post request for your login function.

Cookies save and work fine in Insomnia app, but they do not work in my react application

I have a login page and a register page. When I register a user from the frontend form, it creates a new user. But when I try to log in to the user, I get the 200 status and my data back, but I guess it doesn't set the cookie. When I try to go to a protected route that only a logged-in user can access, I get the error I made from the backend which is "You are unauthenticated". How do I send or set the cookie in the front end too? Everything really works fine in the Insomnia app, the cookies get set.
this is how I'm making the post-login request
const submit = async (e) => {
e.preventDefault();
const data = { username, password };
try {
await axios.post(path, data).then((res) => {
console.log(res);
});
} catch (err) {
setLoading(false);
setError(err.message);
}
this is the login controller in the server side.
const login = async (req, res) => {
try {
const oneUser = await Users.findOne({ username: req.body.username });
if (!oneUser) {
return res.status(403).json("No such user in the database");
}
const isPassword = bcryptjs.compare(req.body.password, oneUser.password);
if (!isPassword) {
return res.status(500).json(`${req.body.username} Password is incorrect`);
}
const token = jwt.sign(
{
id: oneUser._id,
isAdmin: oneUser.isAdmin,
},
process.env.jwt
);
const { password, ...others } = oneUser._doc;
res
.cookie("access_token", token, {
httpOnly: true,
})
.status(200)
.json({ ...others });
} catch (err) {
res.status(500).json(err);
}
};

How can I make a post request inside another request using node js, express

I made an application to make push notifications and I succeeded in sending notifications. But I have a problem, which is that I want to save any notification that I send in my database,
Here is the code,
var FCM = require("fcm-node");
const express = require("express");
const mongoose = require("mongoose");
require("dotenv/config");
const app = express();
app.use(express.json());
const notificationSchema = mongoose.Schema({
name: String,
});
const NotificationModel = mongoose.model("Notification", notificationSchema);
app.post("/fcm", async (req, res, next) => {
try {
let fcm = new FCM(process.env.SERVER_KEY);
let message = {
to: req.body.token,
notification: {
title: req.body.title,
body: req.body.body,
},
};
fcm.send(message, function (err, response) {
if (err) {
next(err);
} else {
// res.json(response);
// res.send(message.notification.body);
app.post("/notfs", async (req, res) => {
let newNotf = new NotificationModel({
name: message.notification.body,
});
newNotf = await newNotf.save();
res.send(newNotf);
});
}
});
} catch (error) {
next(error);
}
});
app.get("/notfs", async (req, res) => {
const notfs = await NotificationModel.find();
res.send(notfs);
});
mongoose
.connect(process.env.CONNECTION_STRING)
.then(() => {
console.log("connected");
})
.catch((err) => {
console.log(err);
});
app.listen(3000, () => {
console.log("listened");
});
Why doesn't it save notifications in the database?
Another question
Please if there is a better way than this please leave it and thank you٫
Thanks in advance
use axios package, which is recommended by nodejs official.
Its simple like jquery ajax call

Handling Server-Side Errors on React: Formik & Yup

I've been trying to handle error from serverside (i.e. email already exists)
Summary of what I have tried so far:
Asynchronously create User Model with email being unique field upon POST /register request.
If there's an Error, I will send client side an error using "res.send(e)".
await Axios request saving it to variable response under try.
On the catch block, set error to field with name 'email'
Finally set submitting to false so user can fix the error.
Result: No console.log or error message from the server.
My questions are as follows:
How do I specify which error I am getting from server-side? (I've tried console logging error blocks by trying to access "e.detail" but I will get undefined.
How to handle this on client side using Formik and Yup? (What's the best practice?)
Below are my code set up.
My server router setup.
const handleErrors = (e) => {
console.log(Object.values(e));
res.send(e)
};
module.exports.register_post = async (req, res) => {
const { email, password, name, gender, social, about, interests } = req.body;
try {
const user = await User.create({
email,
password,
name,
gender,
twitter: social.twitter,
instagram: social.instagram,
about,
interests,
});
const token = createToken(user.id);
console.log(user);
res.cookie('jwt', token, { httpOnly: true, maxAge: maxAge * 1000 });
res.send(user);
} catch (e) {
handleErrors(e);
}
};
My Front-End React(with Formik) side set up
const onSubmit = async (values, { setSubmitting, setFieldError }) => {
const filteredValues = _pickBy(values, (_, key) => key !== 'password2');
try {
const response = await axios.post('http://localhost:8080/register', {
...filteredValues,
});
console.log(response);
history.push('/register/verification');
} catch (e) {
setFieldError('email', e);
} finally {
setSubmitting(false);
}
};

How to use JWT with stripe to retrieve a paymentIntent?

I have a MEVN stack application that uses JWT for auth and that can take stripe payments.
Upon payment, I need to retrieve the payment intent object for that charge and send it to the front end to validate payment and serve up a PDF. My Question is, how can I make sure that the customer only had access to the charge created by that particular user by using the Json web token.
My current node.js code for stripe (without JWT)
const express = require("express");
const router = express.Router();
const endpointSecret = process.env.WEBHOOK_SECRET;
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
let Intent;
router.post("/", async (req, res) => {
const session = await stripe.checkout.sessions.create(
{
success_url: "http://localhost:8080/#/success",
cancel_url: "http://localhost:8080/#/cancel",
payment_method_types: ["card"],
line_items: [
{
price: "price_1H0up7Kc91wTjOOikyrKImZs",
quantity: 1,
},
],
mode: "payment",
},
function (err, session) {
if (err) {
console.log(err);
res.status(500).send({ success: false, reason: "session didnt work" });
} else {
console.log(session);
Intent = session.payment_intent;
console.log(Intent);
res.json({ session_id: session.id });
// res.status(200).send({ success: true });
}
}
);
});
router.get("/confirm", async (req, res) => {
const intentObject = await stripe.paymentIntents.retrieve(Intent, function (
err,
paymentIntent
) {
if (err) {
console.log(err);
res
.status(500)
.send({ success: false, reason: "cannot retrieve payment" });
} else {
console.log(paymentIntent);
res.status(200).json({ status: paymentIntent.status });
setTimeout(() => (intent = ""), 10);
}
});
});
module.exports = router;
You can't use Intent the way you're using it there; it won't persist between requests.
You might want to consider using something like this: https://github.com/auth0/express-jwt

Resources