I am creating the backend of an ecommerce store and I have to validate the photos. I started with an idea, but my teacher has made me change and now I don't know how to combine what I have done with what he asks of me. Let me explain the situation: I have created a special path for uploading the image to cloudinary. This is the code:
const router = require('express').Router()
const cloudinary = require('cloudinary')
const auth = require('./../middleware/auth')
const authAdmin = require('./../middleware/authAdmin')
const fs = require('fs-extra')
// we will upload image on cloudinary
cloudinary.config({
cloud_name: process.env.CLOUD_NAME,
api_key: process.env.CLOUD_API_KEY,
api_secret: process.env.CLOUD_API_SECRET
})
// Upload image only admin can use
router.post('/upload', auth, authAdmin, (req, res) =>{
try {
if(!req.files || Object.keys(req.files).length === 0)
return res.status(400).json({msg: 'No files were uploaded.'})
const file = req.files.file;
if(file.size > 1024*1024) {
removeTmp(file.tempFilePath)
return res.status(400).json({msg: "Size too large"})
}
if(file.mimetype !== 'image/jpeg' && file.mimetype !== 'image/png'){
removeTmp(file.tempFilePath)
return res.status(400).json({msg: "File format is incorrect."})
}
cloudinary.v2.uploader.upload(file.tempFilePath, {folder: "ecommerce"}, async(err, result)=>{
if(err) throw err;
removeTmp(file.tempFilePath)
res.json({public_id: result.public_id, url: result.secure_url})
})
} catch (err) {
return res.status(500).json({msg: err.message})
}
})
// Delete image only admin can use
router.post('/destroy',auth , authAdmin, (req, res) =>{
try {
const {public_id} = req.body;
if(!public_id) return res.status(400).json({msg: 'No images Selected'})
cloudinary.v2.uploader.destroy(public_id, async(err, result) =>{
if(err) throw err;
res.json({msg: "Deleted Image"})
})
} catch (err) {
return res.status(500).json({msg: err.message})
}
})
const removeTmp = (path) =>{
fs.unlink(path, err=>{
if(err) throw err;
})
}
module.exports = router
I have a product model made like this:
const mongoose = require('mongoose')
const productSchema = new mongoose.Schema({
name:{
type: String,
trim: true,
required: true
},
price:{
type: Number,
trim: true,
required: true
},
description:{
type: String,
required: true
},
images:{
type: Object,
required: true
},
category:{
type: mongoose.Schema.Types.ObjectId,
ref: "Category",
required: true
},
}, {
timestamps: true
})
module.exports = mongoose.model("Products", productSchema)
And this is my function to create the product:
createProduct: async (req, res) => {
try {
const {
name,
price,
images,
description,
categoryId
} = req.body;
if (!images) return res.status(400).json({
message: "No hay imagen del producto!"
})
if (!(name || price || description))
return res.status(400).json({
message: "Por favor, complete todos los campos"
})
const product = await Products.findOne({
name
})
if (product)
return res.status(400).json({
message: "Este producto ya ha sido creado anteriormente"
})
const newProduct = new Products({
name: name.toLowerCase(),
price,
description,
categoryId,
images
})
await newProduct.save()
res.json({
message: "Producto creado ! "
})
} catch (err) {
return res.status(500).json({
message: err.message
})
}
},
And this is the route for create product :
app.use('/api', require('./routes/productRouter'))
router.post('/products', auth, authAdmin, productCtrl.createProduct)
The point is that, first, I would upload the image, and when creating the product, it would pass through Postman the results of the image upload (public_id and url).
In the review, my teacher told me to put everything together in the same route, that of creating the product, I have been trying all morning and there is no way it will work for me. Can somebody help me ? I don't know exactly what the function should look like with the integrated image.
Kind regards, thank you in advance
// Create Product -- Admin
exports.createProduct = catchAsyncErrors(async (req, res, next) => {
let images = [];
if (typeof req.body.images === "string") {
images.push(req.body.images);
} else {
images = req.body.images;
}
const imagesLinks = [];
for (let i = 0; i < images.length; i++) {
const result = await cloudinary.v2.uploader.upload(images[i], {
folder: "products",
});
imagesLinks.push({
public_id: result.public_id,
url: result.secure_url,
});
}
req.body.images = imagesLinks;
req.body.user = req.user.id;
const product = await Product.create(req.body);
res.status(201).json({
success: true,
product,
});
});
Related
I am developing a MERN app with axios and trying to populate a field(songList) in a model (User) that is referencing the Schema.Types.ObjectId of another schema (Song).
The _id populates when I create a new Song appropriately.
I can see the entire referenced field is populated in the terminal server side with a console.log but the I cannot get the field to populate on the client side.
My Model; I am trying to populate songList.
const { Schema, model } = require('mongoose')
const bcrypt = require('bcrypt');
const userSchema = new Schema({
username: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
songList: [
{
type: Schema.Types.ObjectId,
ref: 'Song'
}
],
});
userSchema.pre('save', async function (next) {
if (this.isNew || this.isModified('password')) {
const saltRounds = 10;
this.password = await bcrypt.hash(this.password, saltRounds);
}
next();
});
userSchema.methods.isCorrectPassword = async function (password) {
return bcrypt.compare(password, this.password);
};
const User = model("User", userSchema);
module.exports = User;
My server side query, console.log(userSongs) and console.log(user.songList) shows the array of songs appropriately in the terminal:
//login
router.post('/login', async (req, res) => {
const username = req.body.username;
const password = req.body.password;
User.findOne({ username: username })
.populate({path: "songList"})
.exec((err, user) => {
if (err) {
res.status(500).send({ message: err });
return;
}
if (!user) {
res.status(404).json({ message: 'User Not Found' });
}
const passwordIsValid = bcrypt.compareSync(
password,
user.password
);
if (!passwordIsValid) {
return res.status(401).send({ message: "Invalid Password" });
}
let userSongs = [];
for (let i = 0; i < user.songList.length; i++) {
userSongs.push(user.songList[i])
}
const accessToken = sign(
{ username: user.username, _id: user._id },
"importantsecret");
// res.json({
// token: accessToken,
// username: username,
// _id: user._id,
// songList: user.songList
// });
res.status(200).send({
token: accessToken,
username: username,
_id: user._id,
userSongs: userSongs
});
console.log(userSongs)
});
});
The client side request for the user information where I am hoping to see a populated songList with console.log(singleUser):
const [singleUser, setSingleUser] = useState({})
const [userSongs, setUserSongs] = useState([])
useEffect(() => {
Axios.get(`http://localhost:3001/api/user/${id}`).then((response) => {
setSingleUser(response.data)
})
}, [authState])
Client side login request.
const login = () => {
const data = { username: username, password: password };
Axios
.post("http://localhost:3001/api/user/login", data)
.then((response) => {
if (response.data.error) {
console.log(response.data.error)
} else {
localStorage.setItem('accessToken', response.data.token)
setAuthState({
username: response.data.username,
_id: response.data._id,
status: true
});
window.location.replace('/')
}
})
}
Here is where I create a new Song and add it to the user that is logged in.
router.post('/insert', (req, res) => {
const id = req.body.id;
const songTitle = req.body.songTitle;
const artist = req.body.artist;
const album = req.body.album;
Song.create({ songTitle: songTitle, artist: artist, album: album })
.then((song) => {
return User.findOneAndUpdate(
{ _id: id },
{ $addToSet: { songList: song._id } },
{ new: true }
)
})
.then((user) =>
!user
? res.status(404).json({
message: 'Song created, but found no user with that ID',
})
: res.json('Created the song')
)
.catch((err) => {
console.log(err);
res.status(500).json(err)
})
});
Any suggests on how I can get songList to populate using the populate() mongoose function is much appreciated.
Thank you,
Brandon
I've read articles on stack overflow, 'Mongoose 'populate' not populating",
"Mongoose .populate() not working correctly". Medium articles, and the mongoose documentation.
I've tried sending the user songs in the response back as res.json() and res.send(). The field shows up but is not populated.
I've tried being more specific with songList.songTitle and {path: "songList")
All of these show the field populated in the terminal but not on the front side.
I have a user schema as follows:
const UserSchema = new mongoose.Schema({
skills: [String]
});
module.exports = mongoose.model("User", UserSchema);
And a Fetch request to delete a skill as follows:
const deleteItem = async (id) => {
try {
await fetch(`http://localhost:5000/api/user/deleteskill`, {
method: "DELETE",
headers: { "Content-Type": "application/JSON", token: accessToken },
body: JSON.stringify({ userid: userid , skill:id}),
})
.then((res) => res.json())
.then((data) => {
console.log("USER SKILLS:", data.userskills);
});
} catch (err) {
console.log(err);
}
};
Server
const deleteSkill = async (req, res) => {
try {
const user = await User.findById(req.body.userid)
//user.skills.pull(req.body.skill);
// removeskill = user.skills.filter(function(item) {
// return item !== req.body.skill
// })
if (user.skills.includes(req.body.skill)) {
res.status(400).json("Item Still Exists");
} else {
res.status(200).json("Item Deleted");
}
} catch (error) {
res.status(500).send({ error: error.message });
}
};
the array is in the following structure
[
'skill1', 'java', 'skill5'
]
I have tried to remove the user skill from the array in several ways but I still get res.status(400).json("Item Still Exists");. What I'm doing wrong?
Use the findOneAndUpdate method to find a document with the user id and update it in one atomic operation:
const deleteSkill = async (req, res) => {
try {
let message = "Item Deleted";
let status = 200;
const user = await User.findOneAndUpdate(
{ _id: req.body.userid },
{ $pull: { skills: req.body.skill } },
{ new: true }
)
if (user && user.skills.includes(req.body.skill)) {
message = "Item Still Exists";
status = 400;
} else if (!user) {
message = "User Not Found";
status = 404;
}
res.status(status).send({ message });
} catch (error) {
res.status(500).send({ error: error.message });
}
};
I believe you want to remove skills from the database then the following function could help you out.
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
MongoClient.connect(url, function(err, db) {
if (err) throw err;
var dbo = db.db("mydb");
var myquery = { userid: userid, skillid: skillid};
dbo.collection("skills").deleteOne(myquery, function(err, obj) {
if (err) throw err;
console.log("1 document deleted");
db.close();
});
});
You have a method of removing elements from arrays, if you want to remove the first one you could use array.shift (more on it here), but if you want to delete it completely from your database you could always, find it and then update it.
User.update({ _id: userid }, { $pull: { "skills": "[skill]" }})
I'm having trouble with my controller Post to create a post.
I think the main problem is that the condition "if (req.file !== null)" is still working same if i'm not uploading a file (The picture field to be accurate because picture is supposed to be a file)
If someone already had this problem I would like to hear it ! Thank you by advance.
I will show you my code here :
post.controller.js (createPost only)
module.exports.createPost = async (req, res) => {
let fileName;
if (req.file !== null) {
try {
if (
req.file.mimetype != "image/jpg" &&
req.file.mimetype != "image/png" &&
req.file.mimetype != "image/jpeg"
)
throw Error("invalid file");
if (req.file.size > 500000) throw Error("max size");
} catch (err) {
const errors = uploadErrors(err);
return res.status(201).json({ errors });
}
fileName = req.body.posterId + Date.now() + ".jpg";
try {
await sharp(req.file.buffer)
.resize({ width: 150, height: 150 })
.toFile(`${__dirname}/../client/public/uploads/posts/${fileName}`
);
} catch (err) {
res.status(400).send(err);
}
}
const newPost = new postModel({
posterId: req.body.posterId,
message: req.body.message,
picture: req.file !== null ? "./uploads/posts/" + fileName : "",
video: req.body.video,
likers: [],
comments: [],
});
try {
const post = await newPost.save();
return res.status(201).json(post);
} catch (err) {
return res.status(400).send(err);
}
};
post.routes.js :
const router = require('express').Router();
const postController = require('../controllers/post.controller');
const multer = require("multer");
const upload = multer();
router.get('/', postController.readPost);
router.post('/', upload.single("file"), postController.createPost);
postModel :
const mongoose = require('mongoose');
const PostSchema = new mongoose.Schema(
{
posterId: {
type: String,
required: true
},
message: {
type: String,
trim: true,
maxlength: 500,
},
picture: {
type: String,
},
video: {
type: String,
},
likers: {
type: [String],
// required: true,
},
comments: {
type: [
{
commenterId:String,
commenterPseudo: String,
text: String,
timestamp: Number,
}
],
// required: true,
},
},
{
timestamps: true,
}
);
module.exports = mongoose.model('post', PostSchema);
It's because when you don't upload image, the req.file will be undefined, and NOT null.
Try to change your code from this:
if (req.file !== null) {
to this:
if (req.file) {
Hello I want to find posts which user has made ..
I do my request with JWT Token:
###
http://localhost:8080/forum/getByOwnerID
Authorization: Bearer {{token}}
This is my create function :
exports.create = async (req, res) => {
const { forumName, forumDescription } = req.body;
const token = req.token;
const forumExist = await Forum.findOne({ forumName: req.body.forumName });
if(forumExist){
res.status(400).send("Forum Exists already.");
}
try{
const owner = await User.findOne({userID:token._id});
if (!forumName || !forumDescription) {
res.status(400);
throw new Error("Please Fill all the feilds");
return;
}
else {
const newForum = new Forum({ forumName, forumDescription,user: owner.userID });
newForum.user = owner;
const createdNote = await newForum.save();
res.status(201).json(createdNote);
}
}catch(err){
res.status(400).send(err);
}
};
This is my function where I want to get the Posts which the user has made :
exports.getByToken = async (req, res, next) => {
const forum = await Forum.findById( {user: req.token._id} );
if (forum) {
res.json(forum);
} else {
res.status(404).json({ message: "Forum not found" });
}
res.json(forum);
}
And this is model which I have for Post:
const forumSchema = ({
forumName: {
type: String,
required: true,
},
forumDescription: {
type: String,
required: true,
},
user: {
type: Schema.Types.ObjectId,
ref: 'user'
},
published_on: {
type: String,
default: moment().format("LLL")
},
});
Everytime I do a request it has this error :
UnhandledPromiseRejectionWarning: CastError: Cast to ObjectId failed for value "{ user: 'admin' }" (type Object) at path "_id" for model "Forum"
my generate Token :
const generateToken = (_id, userID) => {
console.log('Signing token for ID ', _id,userID);
console.log('Secret key is ', process.env.JWT_KEY);
const token = jwt.sign({ _id,userID}, process.env.JWT_KEY, {
expiresIn: "30d",
});
console.log('Signed token: ', token);
return token;
};
As you are using findById, you should only send the id as argument function.
If you want to search with filter query, use find method
I'm trying to update a transaction, but instead its updating that one and at the same time deleting the other ones. My focus is to just update one transaction. Can someone check my backend logic please.
My Schema:
const mongoose = require('mongoose')
mongoose.Schema.Types.String.set('trim', true)
const transactionSchema = mongoose.Schema(
{
note: { type: String, required: true },
amount: { type: Number, default: 0 },
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User',
},
},
{
timestamps: true,
}
)
const WalletSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true,
},
name: {
type: String,
trim: true,
required: [true, 'Please enter a name'],
},
balance: {
type: Number,
default: 0,
},
transactions: [transactionSchema],
createdAt: {
type: Date,
default: Date.now,
},
})
module.exports = mongoose.model('Wallet', WalletSchema)
My backend controller logic:
exports.updateWalletTransactions = asyncHandler(async (req, res, next) => {
const { amount, note } = req.body
const foundWallet = await Wallet.findOne({ user: req.user.id })
foundWallet.transactions = foundWallet.transactions.filter(
(trans) => trans._id.toString() === req.params.id
)
if (foundWallet.transactions) {
foundWallet.transactions[0].amount =
amount || foundWallet.transactions[0].amount
foundWallet.transactions[0].note = note || foundWallet.transactions[0]
const updatedTransaction = await foundWallet.save()
return res.status(200).json(updatedTransaction)
} else {
return next(new ErrorResponse('Transaction not found', 404))
}
})
The problem is that you're using the filter method of Array that will only return the specified transaction.
the better approach is to write your code using the map method like this:
exports.updateWalletTransactions = asyncHandler(async (req, res, next) => {
const { amount, note } = req.body;
const foundWallet = await Wallet.findOne({ user: req.user.id });
if (!foundWallet) return next(new ErrorResponse("no wallet found", 404));
const transIndex = foundWallet.transactions.findIndex(
(trans) => trans._id.toString() === req.params.id
);
if (!transIndex) return next(new ErrorResponse("Transaction not found", 404));
foundWallet.transactions = foundWallet.transactions.map((trans) => {
if (trans._id.toString() === req.params.id) {
trans.amount = amount || trans.amount;
trans.note = note || trans.note;
}
return trans;
});
const updatedWallet = await foundWallet.save();
const updatedTransactions = updatedWallet.transactions;
return res.status(200).json(updatedTransactions);
});
or you can do it like this:
exports.updateWalletTransactions = asyncHandler(async (req, res, next) => {
const { amount, note } = req.body;
const foundWallet = await Wallet.findOne({ user: req.user.id });
if (!foundWallet) return next(new ErrorResponse("no wallet found", 404));
const transIndex = foundWallet.transactions.findIndex(
(trans) => trans._id.toString() === req.params.id
);
const trans = foundWallet.transactions[transIndex];
if (trans) {
trans.amount = amount || trans.amount;
trans.note = note || trans.note;
} else {
return next(new ErrorResponse("Transaction not found", 404));
}
const updatedWallet = await foundWallet.save();
const updatedTransactions = updatedWallet.transactions;
return res.status(200).json(updatedTransactions);
});
You are overwriting the transaction. Better to iterate over each transaction and update on matching transaction id.
exports.updateWalletTransactions = asyncHandler(async(req, res, next) => {
const {
amount,
note
} = req.body;
const foundWallet = await Wallet.findOne({
user: req.user.id
})
let transFound = false;
if (foundWallet) {
foundWallet.transactions.forEach(trans => {
if (trans._id.toString() === req.params.id) {
transFound = true;
trans.amount = amount || trans.amount
trans.note = note || trans.note
}
})
if(transFound){
const updatedTransaction = await foundWallet.save()
return res.status(200).json(updatedTransaction)
} else {
return next(new ErrorResponse('Transaction not found', 404))
}
} else {
return next(new ErrorResponse('User Id not found', 404))
}
})