| const User = require("../models/userModel.js"); |
| const asyncHandler = require("express-async-handler"); |
| const generateToken = require("../utils/generateToken.js"); |
| const jwt = require("jsonwebtoken"); |
|
|
| const createUser = async (req, res, next) => { |
| const { name, email, password, dateOfBirth, gender, country } = req.body; |
| console.log('User registration attempt:', { name, email, dateOfBirth, gender, country }); |
|
|
| if (!name || !email || !password) { |
| res.status(400); |
| const err = new Error("Please provide name, email and password"); |
| return next(err); |
| } |
|
|
| if (password.length < 6) { |
| res.status(400); |
| const err = new Error("Password must be atleast 6 characters"); |
| return next(err); |
| } |
|
|
| const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; |
| if (!emailRegex.test(email)) { |
| res.status(400); |
| const err = new Error("Invalid email address"); |
| return next(err); |
| } |
|
|
| try { |
| |
| const userExists = await User.findOne({ email }); |
| if (userExists) { |
| res.status(400); |
| const err = new Error( |
| "Email is already registered. Please use a different email address" |
| ); |
| return next(err); |
| } |
|
|
| const user = await User.create({ |
| name, |
| email, |
| password, |
| dateOfBirth: dateOfBirth || '', |
| gender: gender || '', |
| country: country || '', |
| createdAt: new Date(), |
| }); |
|
|
| if (user) { |
| |
| const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET_KEY, { |
| expiresIn: "10d", |
| }); |
|
|
| generateToken(res, user._id); |
|
|
| res.status(201).json({ |
| _id: user._id, |
| name: user.name, |
| email: user.email, |
| dateOfBirth: user.dateOfBirth, |
| gender: user.gender, |
| country: user.country, |
| token: token, |
| }); |
| } |
| } catch (error) { |
| console.log(error); |
| if (error.code === 11000) { |
| res.status(400); |
| const err = new Error( |
| "Email is already registered. Please use a different email address..." |
| ); |
| return next(err); |
| } |
| res.status(500).json({ error: error.message } || "Internal server error"); |
| } |
| }; |
|
|
| const login = asyncHandler(async (req, res) => { |
| const { email, password } = req.body; |
| console.log('Login attempt for:', email); |
|
|
| const user = await User.findOne({ email }); |
|
|
| if (user && (await user.checkPassword(password))) { |
| console.log('Password check passed for user:', user._id); |
|
|
| try { |
| await User.findByIdAndUpdate(user._id, { |
| $set: { isActive: true, lastLogin: new Date() } |
| }); |
| } catch (e) { |
| console.error('Failed to update user active state/lastLogin:', e); |
| } |
|
|
| try { |
| const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET_KEY, { |
| expiresIn: "10d", |
| }); |
| console.log('Token generated successfully:', token ? 'YES' : 'NO'); |
| console.log('JWT_SECRET_KEY exists:', !!process.env.JWT_SECRET_KEY); |
|
|
| generateToken(res, user._id); |
|
|
| const responseData = { |
| _id: user._id, |
| name: user.name, |
| email: user.email, |
| dateOfBirth: user.dateOfBirth, |
| gender: user.gender, |
| country: user.country, |
| token: token, |
| }; |
| |
| console.log('Sending response with token:', !!responseData.token); |
| res.status(200).json(responseData); |
| } catch (tokenError) { |
| console.error('Error generating token:', tokenError); |
| res.status(500).json({ error: 'Failed to generate authentication token' }); |
| } |
| } else { |
| console.log('Login failed for:', email); |
| res.status(400); |
| throw new Error("Invalid email or password"); |
| } |
| }); |
|
|
| const logout = asyncHandler(async (req, res) => { |
| const isProduction = process.env.NODE_ENV === 'production'; |
| |
| try { |
| if (req.user?._id) { |
| await User.findByIdAndUpdate(req.user._id, { $set: { isActive: false } }); |
| } |
| } catch (e) { |
| console.error('Failed to mark user inactive on logout:', e); |
| } |
| |
| const cookieOptions = { |
| httpOnly: true, |
| expires: new Date(0), |
| path: '/', |
| sameSite: isProduction ? 'none' : 'lax', |
| secure: isProduction, |
| ...(isProduction && process.env.COOKIE_DOMAIN && { |
| domain: process.env.COOKIE_DOMAIN |
| }) |
| }; |
| |
| res.cookie("token", "", cookieOptions); |
| res.status(200).json({ message: "Logged out.." }); |
| }); |
|
|
| const getProfile = asyncHandler(async (req, res) => { |
| const user = { |
| _id: req.user._id, |
| name: req.user.name, |
| email: req.user.email, |
| dateOfBirth: req.user.dateOfBirth, |
| gender: req.user.gender, |
| country: req.user.country, |
| }; |
| res.status(200).json(user); |
| }); |
|
|
| const updateProfile = asyncHandler(async (req, res) => { |
| const { password } = req.body; |
|
|
| if (password && password.length < 6) { |
| res.status(400); |
| throw new Error("Password must be at least 6 characters"); |
| } |
|
|
| const user = await User.findById(req.user._id); |
| if (user) { |
| user.name = req.body.name || user.name; |
| if (password) { |
| user.password = password; |
| } |
| const updatedUser = await user.save(); |
| res.status(200).json({ |
| _id: updatedUser._id, |
| name: updatedUser.name, |
| email: updatedUser.email, |
| }); |
| } else { |
| res.status(404); |
| throw new Error("User not found."); |
| } |
| }); |
|
|
| const getWishlist = asyncHandler(async (req, res) => { |
| if (!req.user || !req.user._id) { |
| return res.status(401).json({ message: "Not authorized, user not found" }); |
| } |
| const user = await User.findById(req.user._id).populate("wishlist"); |
| res.json({ wishlist: user.wishlist }); |
| }); |
|
|
| const createWishlistItem = asyncHandler(async (req, res) => { |
| if (!req.user || !req.user._id) { |
| return res.status(401).json({ message: "Not authorized, user not found" }); |
| } |
| const { productId } = req.body; |
| if (!productId) { |
| res.status(400); |
| throw new Error("Missing productId in request body"); |
| } |
| const user = await User.findById(req.user._id); |
| |
| if (!user.wishlist.some(item => item.productId && item.productId.toString() === productId.toString())) { |
| |
| const Product = require('../models/productModel'); |
| const product = await Product.findById(productId); |
| if (!product) { |
| res.status(404); |
| throw new Error("Product not found"); |
| } |
| user.wishlist.push({ |
| productId: product._id, |
| name: product.name, |
| price: Number(product.price), |
| category: product.category, |
| seller: product.seller, |
| stock: product.stock, |
| image: product.images && product.images.length > 0 ? product.images[0].image : '', |
| ratings: Number(product.ratings), |
| createdAt: new Date() |
| }); |
| await user.save(); |
| } |
| res.json({ wishlist: user.wishlist }); |
| }); |
|
|
| const deleteWishlistItem = asyncHandler(async (req, res) => { |
| if (!req.user || !req.user._id) { |
| return res.status(401).json({ message: "Not authorized, user not found" }); |
| } |
| const { productId } = req.body; |
| const user = await User.findById(req.user._id); |
| user.wishlist = user.wishlist.filter( |
| (item) => item.productId && item.productId.toString() !== productId.toString() |
| ); |
| await user.save(); |
| res.json({ wishlist: user.wishlist }); |
| }); |
|
|
| const updateWishlist = asyncHandler(async (req, res) => { |
| if (!req.user || !req.user._id) { |
| return res.status(401).json({ message: "Not authorized, user not found" }); |
| } |
| const { wishlistIndex, update } = req.body; |
| const user = await User.findById(req.user._id); |
| if (user.wishlist[wishlistIndex]) { |
| Object.assign(user.wishlist[wishlistIndex], update); |
| await user.save(); |
| res.json({ success: true, wishlist: user.wishlist[wishlistIndex] }); |
| } else { |
| res.status(404); |
| throw new Error("Wishlist item not found"); |
| } |
| }); |
|
|
| const getWishlists = asyncHandler(async (req, res) => { |
| if (!req.user || !req.user._id) { |
| return res.status(401).json({ message: "Not authorized, user not found" }); |
| } |
| const user = await User.findById(req.user._id).populate("wishlist"); |
| res.json({ wishlist: user.wishlist }); |
| }); |
|
|
| const createWishlistItems = asyncHandler(async (req, res) => { |
| if (!req.user || !req.user._id) { |
| return res.status(401).json({ message: "Not authorized, user not found" }); |
| } |
| const { productIds } = req.body; |
| if (!Array.isArray(productIds) || productIds.length === 0) { |
| res.status(400); |
| throw new Error("Missing productIds array in request body"); |
| } |
| const user = await User.findById(req.user._id); |
| const Product = require('../models/productModel'); |
| for (const productId of productIds) { |
| if (!user.wishlist.some(item => item.productId && item.productId.toString() === productId.toString())) { |
| const product = await Product.findById(productId); |
| if (product) { |
| user.wishlist.push({ |
| productId: product._id, |
| name: product.name, |
| price: Number(product.price), |
| category: product.category, |
| seller: product.seller, |
| stock: product.stock, |
| image: product.images && product.images.length > 0 ? product.images[0].image : '', |
| ratings: Number(product.ratings), |
| createdAt: new Date() |
| }); |
| } |
| } |
| } |
| await user.save(); |
| res.json({ wishlist: user.wishlist }); |
| }); |
|
|
| const deleteWishlistItems = asyncHandler(async (req, res) => { |
| if (!req.user || !req.user._id) { |
| return res.status(401).json({ message: "Not authorized, user not found" }); |
| } |
| const { productIds } = req.body; |
| if (!Array.isArray(productIds) || productIds.length === 0) { |
| res.status(400); |
| throw new Error("Missing productIds array in request body"); |
| } |
| const user = await User.findById(req.user._id); |
| user.wishlist = user.wishlist.filter( |
| (item) => !productIds.includes(item.productId?.toString()) |
| ); |
| await user.save(); |
| res.json({ wishlist: user.wishlist }); |
| }); |
|
|
| const updateWishlists = asyncHandler(async (req, res) => { |
| if (!req.user || !req.user._id) { |
| return res.status(401).json({ message: "Not authorized, user not found" }); |
| } |
| const { updates } = req.body; |
| if (!Array.isArray(updates) || updates.length === 0) { |
| res.status(400); |
| throw new Error("Missing updates array in request body"); |
| } |
| const user = await User.findById(req.user._id); |
| for (const { productId, update } of updates) { |
| const item = user.wishlist.find(item => item.productId && item.productId.toString() === productId.toString()); |
| if (item) { |
| Object.assign(item, update); |
| } |
| } |
| await user.save(); |
| res.json({ wishlist: user.wishlist }); |
| }); |
|
|
| const getReviews = asyncHandler(async (req, res) => { |
| if (!req.user || !req.user._id) { |
| return res.status(401).json({ message: "Not authorized, user not found" }); |
| } |
| const user = await User.findById(req.user._id).populate("reviews.product"); |
| res.json({ reviews: user.reviews }); |
| }); |
|
|
| const addReview = asyncHandler(async (req, res) => { |
| if (!req.user || !req.user._id) { |
| return res.status(401).json({ message: "Not authorized, user not found" }); |
| } |
| const { productId, rating, comment } = req.body; |
| const user = await User.findById(req.user._id); |
| user.reviews.push({ product: productId, rating, comment }); |
| await user.save(); |
| res.json({ reviews: user.reviews }); |
| }); |
|
|
| const deleteReview = asyncHandler(async (req, res) => { |
| if (!req.user || !req.user._id) { |
| return res.status(401).json({ message: "Not authorized, user not found" }); |
| } |
| const { reviewId } = req.body; |
| const user = await User.findById(req.user._id); |
| user.reviews = user.reviews.filter( |
| (r) => r._id.toString() !== reviewId.toString() |
| ); |
| await user.save(); |
| res.json({ reviews: user.reviews }); |
| }); |
|
|
| const getOrders = asyncHandler(async (req, res) => { |
| if (!req.user || !req.user._id) { |
| return res.status(401).json({ message: "Not authorized, user not found" }); |
| } |
| const user = await User.findById(req.user._id); |
| res.json({ orders: user.orders }); |
| }); |
|
|
| const getOrderById = asyncHandler(async (req, res) => { |
| if (!req.user || !req.user._id) { |
| return res.status(401).json({ message: "Not authorized, user not found" }); |
| } |
| const { orderId } = req.params; |
| const user = await User.findById(req.user._id); |
| |
| const order = user.orders.find(order => |
| order._id.toString() === orderId || |
| (order.createdAt && order.createdAt.toISOString() === orderId) |
| ); |
| |
| if (order) { |
| res.json({ success: true, order }); |
| } else { |
| res.status(404).json({ success: false, message: "Order not found" }); |
| } |
| }); |
|
|
| const createOrderSnapshot = asyncHandler(async (req, res) => { |
| if (!req.user || !req.user._id) { |
| return res.status(401).json({ message: "Not authorized, user not found" }); |
| } |
| const { order } = req.body; |
| |
| const orderToSave = { |
| ...order, |
| shippingAddress: order.shippingAddress || null, |
| paymentMethod: order.paymentMethod || null, |
| }; |
| const user = await User.findById(req.user._id); |
| user.orders.push(orderToSave); |
| await user.save(); |
| const newOrder = user.orders[user.orders.length - 1]; |
| res.json({ success: true, order: newOrder, orders: user.orders }); |
| }); |
|
|
| const deleteOrderSnapshot = asyncHandler(async (req, res) => { |
| if (!req.user || !req.user._id) { |
| return res.status(401).json({ message: "Not authorized, user not found" }); |
| } |
| const { createdAt } = req.body; |
| const user = await User.findById(req.user._id); |
| const initialLength = user.orders.length; |
| user.orders = user.orders.filter(order => order.createdAt && order.createdAt.toISOString() !== createdAt); |
| if (user.orders.length === initialLength) { |
| return res.status(404).json({ message: "Order not found" }); |
| } |
| await user.save(); |
| res.json({ orders: user.orders }); |
| }); |
|
|
| const updateOrderSnapshot = asyncHandler(async (req, res) => { |
| if (!req.user || !req.user._id) { |
| return res.status(401).json({ message: "Not authorized, user not found" }); |
| } |
| const { createdAt, update } = req.body; |
| const user = await User.findById(req.user._id); |
| const order = user.orders.find(order => order.createdAt && order.createdAt.toISOString() === createdAt); |
| if (order) { |
| Object.assign(order, update); |
| await user.save(); |
| res.json({ order, orders: user.orders }); |
| } else { |
| res.status(404); |
| throw new Error("Order not found in user history"); |
| } |
| }); |
|
|
| const getCarts = asyncHandler(async (req, res) => { |
| if (!req.user || !req.user._id) { |
| return res.status(401).json({ message: "Not authorized, user not found" }); |
| } |
| const user = await User.findById(req.user._id); |
| res.json({ carts: user.carts }); |
| }); |
|
|
| const createCartSnapshot = asyncHandler(async (req, res) => { |
| if (!req.user || !req.user._id) { |
| return res.status(401).json({ message: "Not authorized, user not found" }); |
| } |
| const { cart } = req.body; |
| if (!cart || !Array.isArray(cart.cartItems) || cart.cartItems.length === 0) { |
| res.status(400); |
| throw new Error("Missing or invalid cart in request body"); |
| } |
| |
| for (const item of cart.cartItems) { |
| if (!item.productId || typeof item.qty !== 'number') { |
| res.status(400); |
| throw new Error("Each cart item must have a productId and qty"); |
| } |
| } |
| const user = await User.findById(req.user._id); |
| user.carts.push(cart); |
| await user.save(); |
| res.json({ success: true, carts: user.carts }); |
| }); |
|
|
| const deleteCartSnapshot = asyncHandler(async (req, res) => { |
| if (!req.user || !req.user._id) { |
| return res.status(401).json({ message: "Not authorized, user not found" }); |
| } |
| const { createdAt } = req.body; |
| const user = await User.findById(req.user._id); |
| const initialLength = user.carts.length; |
| user.carts = user.carts.filter(cart => cart.createdAt && cart.createdAt.toISOString() !== createdAt); |
| if (user.carts.length === initialLength) { |
| return res.status(404).json({ message: "Cart not found" }); |
| } |
| await user.save(); |
| res.json({ carts: user.carts }); |
| }); |
|
|
| const updateCartSnapshot = asyncHandler(async (req, res) => { |
| if (!req.user || !req.user._id) { |
| return res.status(401).json({ message: "Not authorized, user not found" }); |
| } |
| const { createdAt, update } = req.body; |
| const user = await User.findById(req.user._id); |
| const cart = user.carts.find(cart => cart.createdAt && cart.createdAt.toISOString() === createdAt); |
| if (cart) { |
| Object.assign(cart, update); |
| await user.save(); |
| res.json({ cart, carts: user.carts }); |
| } else { |
| res.status(404); |
| throw new Error("Cart not found in user history"); |
| } |
| }); |
|
|
| module.exports = { createUser, login, logout, getProfile, updateProfile, getWishlist, createWishlistItem, deleteWishlistItem, updateWishlist, getWishlists, createWishlistItems, deleteWishlistItems, updateWishlists, getOrders, getOrderById, createOrderSnapshot, deleteOrderSnapshot, updateOrderSnapshot, getCarts, createCartSnapshot, deleteCartSnapshot, updateCartSnapshot, getReviews, addReview, deleteReview }; |