I'm using mongodb and next.js to check if a user exists in a database array, then add a like and increment a database element if this is true. The database is structured liked this:
likes -> by -> [array of users]
My code to find if a user exists in the database array is this:
db.collection('quotes').findOne({
likes: {
by: cookies.get('user')
}
}, async function(liked) {
if (!liked) {
//...Add like
However, for some reason the like just keeps getting added, regardless of the check if the user exists. I also get this error for some reason:
API resolved without sending a response for /api/like, this may result in stalled requests.
Here's my full code for api/like
import clientPromise from "../../lib/mongodb";
const ObjectId = require('mongodb').ObjectId;
import Cookies from 'cookies'
export default async function handler(req, res) {
switch (req.method) {
case 'POST': {
return like(req, res);
}
}
}
async function like(req, res) {
const cookies = new Cookies(req, res)
const client = await clientPromise;
const db = client.db("the-quotes-place");
if (cookies.get('user') != null) {
db.collection('quotes').findOne({
likes: {
by: cookies.get('user')
}
}, async function(liked) {
if (!liked) {
await db.collection('quotes').updateOne(
{ _id: new ObjectId(req.body.id) },
{
$inc: {
"likes.num": 1
},
$push: {
"likes.by": cookies.get('user')
}
})
res.status(200).json({
message: 'Succesfully added like',
success: true
})
}
})
} else {
res.status(403).json({
message: 'Error: Cookies not found',
success: true
})
}
}
What's going on here?
Thanks for any help.
Related
I am working on adding an express router for something like profile address.
The profile address is a unique name comes after the main URL.
For instance: www.domain.com/superCat
But the problem is, this route will destroy all other routes.
Here is what I have done:
router.get('/:profileAddress', userController.profileAddress);
router.param("profileAddress", async (req, res, next, val) => {
// check the parameter if exists in the database
const account = await Account.findOne({
where: {profileAddress: val}
});
try {
if(account && account !== ""){
// render the user profile
const accountId = account.accountId;
const userData = await Account.findOne({ where: { accountId: accountId } });
const results = await Calenders.findAll({ where: { accountId: accountId } });
const avatar = await Avatars.findOne({ where: { accountId: accountId } });
return res.render("user/userProfile", {
title: "User Profile",
data: results,
userData: userData,
avatar: avatar,
});
}else{
console.log("Yes it works");
// if not exists in the database, go to next process
return next();
}
} catch (error) {
console.log(" ********** Error ", error)
}
});
// inside the controller
exports.profileAddress = async (req, res, next) => {
console.log("Yes it works, controller");
next();
}
But it will go to not found route, if the parameter not found in the database.
But I do not want something like adding more specific router, because it is the client requirements. I am looking for any solution like if I can add a middleware or regex.
Node.js CODE
exports.user = async (req, res) => {
try {
const { wallet } = req.body;
if (!wallet) {
res.status(400).json({ error: "Not logged in" });
return;
} else {
user = User.findone(wallet);
// if user is not found then create a new user and mark as loggged In
if (!user) {
User.create({
user: wallet,
});
}
// if user found then create a session token and mark as logged
in
res.send({
user: wallet,
});
}
} catch (error) {
console.log(`ERROR::`, error);
}
};
REACTJs CODE
// post call/update
const axiosCall = async () => {
// core login will give a unique username by fulling a transcation
// core.login i dont have any control
const userAccount = await core.login();
try {
const res = await Axios.post(`${API}/user`, userAccount, dataToken);
setData({
...data,
error: "",
success: res.data.message,
});
} catch (error) {
setData({
...data,
error: error.response.data.error,
});
}
};
Now here the problem occurs when some one could modify userAccount in the front-end or someone could send a body with wallet: anything to my route localhost:3000/api/user
There is no option for me to check if some actually used core.login(); to get the wallet address.
So is there any solution?
I was thinking to allow only my server IP or localhost to hit the route localhost:3000/api/user and is that even possible?
Also there is another issue anyone could modify userAccount in front-end.
i created a controller that can delete but it is difference because it is not only delete records according ID, it also update the record after the API of delete is call.
Let me demonstrate my code below:
// delete user
exports.delete = (req, res) => {
const user_id = req.params.user_id;
// Find record by it is id to DELETE
User.findByPk(user_id).then((num) => {
if (num == 1) {
res.status(200).send({
message: "User was deleted successfully!",
data: null,
});
} else {
res.status(500).send({
message: `Cannot delete User with id=${user_id}. Maybe User was not found!`,
data: null,
});
}
});
//After delete must update the is_active field
User.update(
{
is_active: 0,
},
{
where: { is_active: null },
}
);
};
Now i have problem that i do not know how to make my API to work. So, i hope someone can help me to deal with this problem that how to update is_active MUST BE 0 after DELETE. I am appreciate it.
You just need to use async/await instead of then to wait for then result of findByPk before calling update:
exports.delete = async (req, res) => {
const user_id = req.params.user_id;
// Find record by it is id to DELETE
const user = await User.findByPk(user_id)
if (user) {
//After delete must update the is_active field
await User.update(
{
is_active: 0,
},
{
where: { is_active: null },
}
);
res.status(200).send({
message: "User was deleted successfully!",
data: null,
});
} else {
res.status(500).send({
message: `Cannot delete User with id=${user_id}. Maybe User was not found!`,
data: null,
});
}
};
This way you have a straight forward code like you usually write when it's synchronous.
I am working on a Next.js project, in which I have included a login system with NextAuth.
Everything was working fine at the beginning, but recently I keep getting an error every time I try to get the session.
The Error:
https://pastebin.com/Mh624N3c
StackOverflow doesn't let me post the whole error, so I had to use Pastebin.
This is the first time I encounter such an error, and I can't seem to be able to find a solution. I am using JWT as the session strategy, if that has to do anything with the issue.
This is the code I use for handling the authentication & session:
await NextAuth(req, res, {
adapter: MongoDBAdapter(clientPromise),
pages: {
signIn: "/login"
},
providers: [
CredentialsProvider({
name: "credentials",
credentials: {
email: { label: "Email", type: "email", placeholder: "example#email.com" },
password: { label: "Password", type: "password" }
},
async authorize(credentials, req) {
const account = await Accounts.exists(credentials.email)
const cryptr = new Cryptr(Config.secret)
const encEmail = cryptr.encrypt(credentials.email)
const url = process.env.NODE_ENV == "development" ? "http://localhost:3000/" : Config.url
if (account) {
const password = cryptr.decrypt(account.password)
if (credentials.password == password) {
return {
...account,
_id: null
}
} else {
return res.redirect("/login?error=true")
}
} else {
const code = await Accounts.requestVerification(credentials.email, password)
const message = {
to: credentials.email,
from: "noreply#bytestobits.dev",
subject: "BytesToBits API - Create Your Account",
html: emailStyle(credentials?.email, url, code),
}
SendGrid.send(message).then(() => console.log("Email Sent"))
return res.redirect("/verify?email=" + encEmail)
}
}
})
],
jwt: {
secret: Config.secret,
encryption: true
},
secret: Config.secret,
session: {
strategy: "jwt"
},
callbacks: {
async jwt({ token, user }) {
if (user) {
token.user = user
}
return token
},
async session({ session, token }) {
let data = token.user
if (data) {
if (await Accounts.exists(data.email)) {
data.token = await Accounts.getToken(data.email)
}
data.tokenInfo = await Accounts.tokenInfo(data.token)
}
return data
}
}
})
This happens every time I try to fetch the session or authenticate.
When the user authenticates, a session must be formed, which can be fetched from the client for usage. However, whenever I try to authenticate of fetch the session, a "Parse Error: Header overflow" occurs.
I managed to fix the issue! Turns out the Session object was way too long and caused this error.
Basically in the data's tokenInfo field, it had a really long array. So removing that specific field fixed the issue!
So basically, I'm trying to separate my code that handles data (mongoose) from my express Router code, since I might want to use it elsewhere too.
The first thing I did was, I got rid of the res.json() calls, since I don't want the code to only work returning a http response. I want it to return data, so I can then return that data from my router as a http response, but still use it as regular data elsewhere.
Here is a function I wrote to get data from mongoose.
module.exports.user_login = data => {
console.log(data);
ModelUser.findOne({email: data.email}).then(user => {
if(!user){
console.log({email: 'E-mail address not found'});
return {
status: response_code.HTTP_404,
response: {email: 'E-mail address not found'}
}
}
bcrypt.compare(data.password, user.password).then(isMatch => {
if(!isMatch){
console.log({password: 'Invalid password'});
return {
status: response_code.HTTP_400,
response: {password: 'Invalid password'}
}
}
const payload = {
id: user.id,
email: user.email
};
jwt.sign(
payload,
config.PASSPORT_SECRET,
{
expiresIn: "1h"
},
(err, token) => {
console.log({
status: response_code.HTTP_200,
response: {
success: true,
token: token
}
});
return {
status: response_code.HTTP_200,
response: {
success: true,
token: token
}
}
}
);
});
});
};
When this code gets executed in my route like so:
router.post("/login", (req, res) => {
const { errors, isValid } = validateLogin(req.body);
if(!isValid) return res.status(400).json(errors);
console.log("ret", dm_user.user_login(req.body));
});
The log says the return value of user_login() is undefined, even though right before the return statement in user_login() I am logging the exact same values and they are getting logged.
Before I changed it to a log, I tried to store the return value in a variable, but obviously that remained undefined as well, and I got the error: 'Cannot read propery 'status' of undefined' when trying to use the value.
I am definitely missing something..
Well you have an small callback hell here. It might be a good idea to go with async / await and splitting up your code into smaller chunks instead of putting everyhing in 1 file.
I rewrote your user_login function:
const { generateToken } = require("./token.js");
module.exports.user_login = async data => {
let user = await ModelUser.findOne({ email: data.email });
if (!user) {
console.log({ email: "E-mail address not found" });
return {
status: response_code.HTTP_404,
response: { email: "E-mail address not found" }
};
}
let isMatch = await bcrypt.compare(data.password, user.password);
if (!isMatch) {
console.log({ password: "Invalid password" });
return {
status: response_code.HTTP_400,
response: { password: "Invalid password" }
};
}
const payload = {
id: user.id,
email: user.email
};
let response = await generateToken(
payload,
config.PASSPORT_SECRET,
response_code
);
return response;
};
I have moved your token signing method into another file and promisfied it:
module.exports.generateToken = (payload, secret, response_code) => {
return new Promise((res, rej) => {
jwt.sign(
payload,
secret,
{
expiresIn: "1h"
},
(err, token) => {
if (err) {
rej(err);
}
res({
status: response_code.HTTP_200,
response: {
success: true,
token: token
}
});
}
);
});
};
Now you need to change your router function into an async:
router.post("/login", async (req, res) => {
const { errors, isValid } = validateLogin(req.body);
if(!isValid) return res.status(400).json(errors);
let result = await dm_user.user_login(req.body);
console.log(result);
});
In addition: You get undefined because you return your value to an callback function
I also would seperate your routes from your controllers instead of writing your code inside an anonymous function
Please notice that whenever you are trying to return any value you are always present in the callback function and that is definitely not going to return any value to its intended place.
There are a couple of things you can improve about your code :
1.Donot use jwt inside your code where you are making database calls, instead move it where your routes are defined or make a separate file.
2.If you are intending to re-use the code, I would suggest you either use async-await as shown in the answer above by Ifaruki or you can use something like async.js. But the above shown approach is better.
Also always use 'error' field when you are making db calls like this:
ModelUser.findOne({email: data.email}).then((error,user) => {