Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
5 changes: 5 additions & 0 deletions BE/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
PORT=8000
MONGODB_URI=mongodb://lesavantdon:icecream2@localhost:27017/productsdb?authSource=admin

console.log('MongoDB URI:', process.env.MONGODB_URI); // Debugging line

3 changes: 3 additions & 0 deletions BE/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Node modules
node_modules/
package-lock.json/
11 changes: 11 additions & 0 deletions BE/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
*dependencies required for back end*
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"mongoose": "^8.5.3"

*from terminal CD into BE*

CD BE "npm start" to run back end
make sure data is seeded into the mongo database with
"BE git:(master) ✗ node scripts/seed.js" in shell/terminal
12 changes: 12 additions & 0 deletions BE/api/products/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const express = require('express');
const router = express.Router();
const productController = require('../../controllers/productController');


router.get('/', productController.getAllProducts);
router.get('/:id', productController.getProductById);
router.post('/', productController.createProduct);
router.put('/:id', productController.updateProduct);
router.delete('/:id', productController.deleteProduct);
Comment on lines +6 to +10

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very clear and good organization


module.exports = router;
17 changes: 17 additions & 0 deletions BE/api/reviews/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const express = require('express');
const router = express.Router();
const reviewController = require('../../controllers/reviewController'); // Make sure the path is correct


// In your reviews route file (e.g., api/reviews/index.js)

router.get('/', reviewController.getAllReviews);
router.post('/:productId', reviewController.createReview); // Create a new review
router.get('/:productId', reviewController.getReviewsByProductId);
router.get('/reviews/:productId', reviewController.getPaginatedReviewsByProductId);

router.delete('/:reviewId', reviewController.deleteReview);


module.exports = router;

38 changes: 38 additions & 0 deletions BE/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// app.js
const express = require('express');
const dotenv = require('dotenv');
const cors = require('cors'); // Import cors
const connectDB = require('./config/db');
const productsRoutes = require('./api/products/index.js');
const reviewsRoutes = require('./api/reviews/index.js'); // Import reviews routes
const errorHandler = require('./middleware/errorHandler');

dotenv.config(); // Load environment variables from .env file
const app = express();
app.use(cors());

app.use(express.json());
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});

app.get('/', (req, res) => {
res.send('Welcome to the API');
});

app.use('/api/products', productsRoutes);
app.use('/api/reviews', reviewsRoutes);


app.use((req, res) => {
res.status(404).send('Sorry, that route doesn’t exist.');
});
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});

module.exports = app; // Export the app
18 changes: 18 additions & 0 deletions BE/config/db.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const mongoose = require('mongoose');

const connectDB = async () => {
try {
const conn = await mongoose.connect(process.env.MONGODB_URI, {
// The following options are deprecated and can be removed:
// useNewUrlParser: true,
// useUnifiedTopology: true,
});

console.log(`MongoDB Connected: ${conn.connection.host}`);
} catch (error) {
console.error(`Error: ${error.message}`);
process.exit(1); // Exit process with failure
}
};

module.exports = connectDB;
132 changes: 132 additions & 0 deletions BE/controllers/productController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
const Product = require('../models/product');

// Get all products with pagination, filtering, and sorting
const getAllProducts = async (req, res) => {
const { page = 1, limit = 9, category = 'all', sort = 'asc' } = req.query;

const query = {};
// Filter by category if not 'all'
if (category && category !== 'all') {
query.category = category;
}
Comment on lines +9 to +11

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (category && category !== 'all') {
query.category = category;
}
query.category = category !== 'all' ? category : null;


try {
// Get total count of products for pagination
const totalProducts = await Product.countDocuments(query);

// Calculate the number of products to skip
const skip = (page - 1) * limit;

// Fetch the products from the database
const products = await Product.find(query)
.sort({ price: sort === 'asc' ? 1 : -1 }) // Sort by price
.skip(skip) // Skip the appropriate number of products
.limit(parseInt(limit)); // Limit the number of results

// Calculate total pages
const totalPages = Math.ceil(totalProducts / limit);

// Send the response
res.json({
products,
totalPages
});
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Error fetching products.' });
}
};


// Get a single product by ID
const getProductById = async (req, res) => {
const { id } = req.params;

try {
const product = await Product.findById(id);

if (!product) {
return res.status(404).json({ message: 'Product not found.' });
}

res.json(product);
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Error fetching product.' });
}
};

// Create a new product
const createProduct = async (req, res) => {
const { userName, name, description, price, category, image } = req.body;

if (!userName || !name || !description || !price || !category || !image) {
return res.status(400).json({ message: 'All fields are required.' });
}

try {
const newProduct = new Product({
userName,
name,
description,
price,
category,
image
});

const savedProduct = await newProduct.save();
res.status(201).json(savedProduct);
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Error creating product.' });
}
};

// Update a product by ID
const updateProduct = async (req, res) => {
const { id } = req.params;
const { userName, name, description, price, category, image } = req.body;

try {
const updatedProduct = await Product.findByIdAndUpdate(
id,
{ userName, name, description, price, category, image },
{ new: true, runValidators: true }
);

if (!updatedProduct) {
return res.status(404).json({ message: 'Product not found.' });
}

res.json(updatedProduct);
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Error updating product.' });
}
};

// Delete a product by ID
const deleteProduct = async (req, res) => {
const { id } = req.params;

try {
const deletedProduct = await Product.findByIdAndDelete(id);

if (!deletedProduct) {
return res.status(404).json({ message: 'Product not found.' });
}

res.status(200).json({ message: 'Product deleted successfully.' });
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Error deleting product.' });
}
};

module.exports = {
getAllProducts,
getProductById,
createProduct,
updateProduct,
deleteProduct
};
115 changes: 115 additions & 0 deletions BE/controllers/reviewController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
const Review = require('../models/review');
const Product = require('../models/product');
const mongoose = require('mongoose');

const getAllReviews = async (req, res) => {
try {
const reviews = await Review.find(); // Fetch all reviews
res.json(reviews); // Return the reviews
} catch (error) {
console.error('Error fetching reviews:', error);
res.status(500).json({ message: 'Server error' });
}
};

const createReview = async (req, res) => {
try {
// Extract data from request body
const { user, rating, review, productId } = req.body;

// Create a new review instance
const newReview = new Review({
user,
rating,
review,
productId
});

// Save the review to the database
const savedReview = await newReview.save();

// Send the saved review as a response
return res.status(201).json(savedReview);
} catch (error) {
console.error('Error creating review:', error);
return res.status(500).json({ message: 'Failed to create review.' });
}
};

// Corrected the function declaration
const getReviewsByProductId = async (req, res) => {
try {
const { productId } = req.params; // Get the product ID from the route
const reviews = await Review.find({ productId }); // Find reviews associated with the product ID

if (!reviews || reviews.length === 0) {
return res.status(404).json({ message: 'No reviews found for this product.' });
}

res.json(reviews); // Return the found reviews
} catch (error) {
console.error('Error fetching reviews:', error);
res.status(500).json({ message: 'Server error' });
}
};



// Delete a review by productId and reviewId
const deleteReview = async (req, res) => {
try {
const { reviewId } = req.params; // Get reviewId from request parameters
console.log('Attempting to delete review with ID:', reviewId);

const review = await Review.findById(reviewId); // Check if review exists
if (!review) {
console.error('Review not found:', reviewId);
return res.status(404).json({ message: 'Review not found' });
}

await Review.findByIdAndDelete(reviewId); // Delete the review
console.log('Review deleted successfully:', reviewId);

return res.status(200).json({ message: 'Review deleted successfully' });
} catch (error) {
console.error('Error deleting review:', error);
return res.status(500).json({ message: 'Server error', error });
}
};

// Fetch reviews for a product with pagination
const getPaginatedReviewsByProductId = async (req, res) => {
const { productId } = req.params;
const { page = 1, limit = 4 } = req.query; // Default to page 1 and limit 4 reviews

try {
// Fetch reviews using pagination
const reviews = await Review.find({ productId })
.limit(limit * 1) // Limit to the specified number of reviews
.skip((page - 1) * limit) // Skip to the relevant page
.exec();

// Get the total number of reviews
const totalReviews = await Review.countDocuments({ productId });

res.status(200).json({
reviews,
totalPages: Math.ceil(totalReviews / limit),
currentPage: page,
});
} catch (error) {
res.status(500).json({ message: 'Error fetching paginated reviews', error });
}
};




module.exports = {
getAllReviews,
createReview,
getReviewsByProductId,
deleteReview,
getPaginatedReviewsByProductId,

};
10 changes: 10 additions & 0 deletions BE/middleware/errorHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const errorHandler = (err, req, res, next) => {
const statusCode = res.statusCode === 200 ? 500 : res.statusCode;
res.status(statusCode);
res.json({
message: err.message,
stack: process.env.NODE_ENV === 'production' ? null : err.stack,
});
};

module.exports = errorHandler;
15 changes: 15 additions & 0 deletions BE/models/product.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const mongoose = require('mongoose');

// Product Schema
const productSchema = new mongoose.Schema({
userName: { type: String, required: true },
name: { type: String, required: true },
description: { type: String, required: true },
price: { type: Number, required: true },
category: { type: String, required: true },

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ideally we would want to limit categories and use an enum or some specific ones

image: { type: String, required: true },
}, { timestamps: true });

const Product = mongoose.model('Product', productSchema);

module.exports = Product;
Loading