E-Commerce application in Nodejs Backend Development Project

├── backend/
  │   ├── server.js
  │   ├── routes/
  │   │   ├── products.js
  │   │   ├── auth.js
  │   │   ├── orders.js
  │   │   ├── admin/
  │   │   │   ├── adminDashboard.js
  │   ├── controllers/
  │   │   ├── productsController.js
  │   │   ├── authController.js
  │   │   ├── ordersController.js
  │   │   ├── admin/
  │   │   │   ├── adminDashboardController.js
  ├── db/
  │   ├── db.js
  ├── models/
  │   ├── Product.js
  │   ├── User.js
  │   ├── Order.js
  ├── config/
  │   ├── config.js
  ├── middleware/
  │   ├── auth.js
  ├── public/
  │   ├── ...
  ├── package.json
  ├── .env

Models E-Commerce application in Nodejs Backend Development Project

User.js Models

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const userSchema = new Schema({
  firstName: {
    type: String,
    required: true,
  },
  lastName: {
    type: String,
    required: true,
  },
  email: {
    type: String,
    required: true,
    unique: true,
    lowercase: true,
  },
  password: {
    type: String,
    required: true,
  },
  role: {
    type: String,
    enum: ['user', 'admin'],
    default: 'user',
  },
  orders: [
    {
      type: Schema.Types.ObjectId,
      ref: 'Order',
    },
  ],
  address: {
    street: String,
    city: String,
    state: String,
    postalCode: String,
    country: String,
  },
  createdAt: {
    type: Date,
    default: Date.now,
  },
  updatedAt: {
    type: Date,
    default: Date.now,
  },
});

module.exports = mongoose.model('User', userSchema);
  • firstName and lastName represent the user’s name.
  • email is the user’s unique email address.
  • password stores a hashed and salted password.
  • role indicates whether the user is an ‘admin’ or a regular ‘user’. You can expand the roles as needed.
  • orders is an array of references to the user’s orders, demonstrating a one-to-many relationship between users and orders.
  • address stores the user’s shipping address.
  • createdAt and updatedAt fields help track when the user’s account was created and last updated.

Product.js Models

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const productSchema = new Schema({
  name: {
    type: String,
    required: true,
  },
  description: {
    type: String,
    required: true,
  },
  price: {
    type: Number,
    required: true,
  },
  stock: {
    type: Number,
    required: true,
  },
  category: {
    type: Schema.Types.ObjectId,
    ref: 'Category',
    required: true,
  },
  variations: [
    {
      name: String,
      price: Number,
      stock: Number,
    },
  ],
  reviews: [
    {
      user: {
        type: Schema.Types.ObjectId,
        ref: 'User',
      },
      rating: Number,
      comment: String,
    },
  ],
  createdAt: {
    type: Date,
    default: Date.now,
  },
  updatedAt: {
    type: Date,
    default: Date.now,
  },
});

module.exports = mongoose.model('Product', productSchema);
  • name is the name of the product.
  • description provides a detailed product description.
  • price represents the price of the product.
  • stock denotes the available stock quantity.
  • category is a reference to the product’s category. This demonstrates a one-to-one relationship with a Category model.
  • variations is an array that allows you to manage product variations like size, color, etc., with their respective prices and stock quantities.
  • reviews is an array of product reviews, including the user who left the review, a rating, and a comment.
  • createdAt and updatedAt fields help track when the product was created and last updated.

You should also create a Category model separately and reference it in the category field of the Product model to manage product categorization.

Category.js Models

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const categorySchema = new Schema({
  name: {
    type: String,
    required: true,
  },
  description: String,
  parent: {
    type: Schema.Types.ObjectId,
    ref: 'Category',
  },
  ancestors: [
    {
      type: Schema.Types.ObjectId,
      ref: 'Category',
    },
  ],
  children: [
    {
      type: Schema.Types.ObjectId,
      ref: 'Category',
    },
  ],
  products: [
    {
      type: Schema.Types.ObjectId,
      ref: 'Product',
    },
  ],
  createdAt: {
    type: Date,
    default: Date.now,
  },
  updatedAt: {
    type: Date,
    default: Date.now,
  },
});

categorySchema.index({ parent: 1 });

categorySchema.pre('save', function (next) {
  if (this.isModified('parent')) {
    this.ancestors = [this.parent, ...(this.parent.ancestors || [])];
  }
  next();
});

module.exports = mongoose.model('Category', categorySchema);
  • name represents the name of the category.
  • description provides additional details about the category.
  • parent is a reference to the parent category, enabling the creation of a hierarchical category structure.
  • ancestors is an array of references to all parent categories, forming a chain from the current category to the root category. This is useful for navigating the category tree and querying related products.
  • children is an array of references to subcategories, allowing you to create nested category structures.
  • products is an array of references to products that belong to this category.
  • createdAt and updatedAt fields record when the category was created and last updated.

The use of hierarchical categories and nested subcategories is a powerful way to organize products. The ancestors and children fields enable the creation of a category tree, making it easier to navigate and filter products.

Additionally, a custom pre-save middleware is used to automatically update the ancestors array when the parent field is modified. This ensures that the hierarchy is maintained correctly.

Order.js Models

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const orderSchema = new Schema({
  user: {
    type: Schema.Types.ObjectId,
    ref: 'User',
    required: true,
  },
  items: [
    {
      product: {
        type: Schema.Types.ObjectId,
        ref: 'Product',
        required: true,
      },
      quantity: {
        type: Number,
        required: true,
      },
      price: {
        type: Number,
        required: true,
      },
    },
  ],
  status: {
    type: String,
    enum: ['pending', 'processing', 'shipped', 'delivered'],
    default: 'pending',
  },
  shippingAddress: {
    street: String,
    city: String,
    state: String,
    postalCode: String,
    country: String,
  },
  paymentMethod: String,
  paymentStatus: {
    type: String,
    enum: ['unpaid', 'paid', 'refunded'],
    default: 'unpaid',
  },
  orderDate: {
    type: Date,
    default: Date.now,
  },
  totalAmount: {
    type: Number,
    required: true,
  },
});

module.exports = mongoose.model('Order', orderSchema);
  • user is a reference to the user who placed the order.
  • items is an array that contains order items, where each item includes a reference to the product, quantity, and price.
  • status represents the order status, with predefined values like ‘pending,’ ‘processing,’ ‘shipped,’ and ‘delivered.’
  • shippingAddress stores the shipping address details.
  • paymentMethod specifies the payment method used for the order.
  • paymentStatus indicates the payment status, with options like ‘unpaid,’ ‘paid,’ and ‘refunded.’
  • orderDate records the date and time when the order was placed.
  • totalAmount is the total order amount.

Transaction models

const mongoose = require('mongoose');

const transactionSchema = new mongoose.Schema({
  user: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User',
    required: true,
  },
  order: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Order',
    required: true,
  },
  amount: {
    type: Number,
    required: true,
  },
  status: {
    type: String,
    enum: ['pending', 'completed', 'failed'],
    default: 'pending',
    required: true,
  },
  paymentMethod: {
    type: String,
    required: true,
  },
  transactionDate: {
    type: Date,
    default: Date.now,
  },
});

const Transaction = mongoose.model('Transaction', transactionSchema);

module.exports = Transaction;

package.json

{
  "name": "your-ecommerce-backend",
  "version": "1.0.0",
  "description": "Your e-commerce application backend",
  "dependencies": {
    "express": "latest",
    "mongoose": "latest",
    "bcrypt": "latest",
    "jsonwebtoken": "latest",
    "dotenv": "latest",
    "body-parser": "latest",
    "cors": "latest",
    "helmet": "latest",
    "morgan": "latest"
  }
}

Auth.js Middleware

const jwt = require('jsonwebtoken');
const config = require('../config/config'); // Load your JWT secret and other configuration

function authenticateUser(roles = []) {
  return (req, res, next) => {
    const token = req.header('x-auth-token');

    // Check if a token is provided in the request headers
    if (!token) {
      return res.status(401).json({ message: 'Access denied. No token provided.' });
    }

    try {
      const decoded = jwt.verify(token, config.jwtSecret);

      // Check if the user has the required role
      if (roles.length > 0 && !roles.includes(decoded.role)) {
        return res.status(403).json({ message: 'Access denied. Insufficient permissions.' });
      }

      // Attach the user information to the request for further processing
      req.user = decoded;
      next();
    } catch (ex) {
      return res.status(401).json({ message: 'Invalid token.' });
    }
  };
}

module.exports = authenticateUser;
  • We use JSON Web Tokens (JWT) for user authentication. You should have a configuration file (config/config.js) where you store the JWT secret key and other settings.
  • The authenticateUser middleware accepts an array of roles as an argument. This allows you to protect routes based on specific roles (e.g., ‘user’, ‘admin’).
  • It checks for the presence of a JWT token in the request headers and verifies its authenticity using the JWT secret.
  • If a token is valid, it further checks if the user’s role is allowed to access the protected route. If roles are specified and the user’s role isn’t among the allowed roles, it returns a 403 Forbidden response.

config.js Environment variable

module.exports = {
  // MongoDB connection URI
  mongoURI: 'mongodb://localhost/your-ecommerce-db',

  // JWT secret key (used for token generation and validation)
  jwtSecret: 'your-secret-key',

  // Port for the Express server
  serverPort: process.env.PORT || 5000,

  // API Key for external services (e.g., payment gateway)
  apiKey: 'your-api-key',

  // Other configuration settings
};
  • mongoURI: This is the MongoDB connection URI, which specifies the database location and credentials.
  • jwtSecret: A secret key used for generating and verifying JSON Web Tokens (JWTs) for authentication.
  • serverPort: The port on which your Express server will run. You can set it to a default value (e.g., 5000) and optionally use the process.env.PORT variable to allow configuration through environment variables.
  • apiKey: If your application relies on external services (e.g., a payment gateway), you can store API keys or credentials here.

.env file

# Database configuration
DB_HOST=localhost
DB_PORT=27017
DB_NAME=your-ecommerce-db
DB_USER=your-db-user
DB_PASSWORD=your-db-password

# JWT secret key
JWT_SECRET=your-secret-key

# API keys
API_KEY=your-api-key

# Port for the Express server
PORT=5000

Utils Error Handler

// utils/errorHandler.js

// Custom error class for API errors
class ApiError extends Error {
  constructor(statusCode, message) {
    super();
    this.statusCode = statusCode;
    this.message = message;
  }
}

// Error handler middleware
function errorHandler(err, req, res, next) {
  if (res.headersSent) {
    return next(err);
  }

  if (err instanceof ApiError) {
    res.status(err.statusCode).json({ error: err.message });
  } else {
    res.status(500).json({ error: 'Internal Server Error' });
  }
}

module.exports = {
  ApiError,
  errorHandler,
};
  1. We define a custom error class, ApiError, that takes a statusCode and an error message as constructor arguments. This class will be used to create specific errors in your application.
  2. The errorHandler middleware function is responsible for catching errors and sending appropriate responses based on the error type.
  3. If the error is an instance of ApiError, we send a JSON response with the provided status code and error message. This allows you to throw specific errors in your application, such as validation errors, and handle them uniformly.
  4. If the error is not an instance of ApiError, we send a generic “Internal Server Error” response with a status code of 500.

You can use this error handler in your routes or controllers like this:

const { ApiError } = require('./utils/errorHandler');

// Example of throwing an error in a route handler
app.get('/some/route', (req, res, next) => {
  try {
    // Some operation that may throw an error
    if (/* condition */) {
      throw new ApiError(400, 'Bad Request');
    }
    // Rest of your code
  } catch (error) {
    next(error); // Pass the error to the error handler
  }
});

// Example of using the error handler middleware
app.use((err, req, res, next) => {
  errorHandler(err, req, res, next);
});

Token.js

const jwt = require('jsonwebtoken');
const config = require('../config/config'); // Load your JWT secret and other configuration

const generateToken = (payload, expiresIn) => {
  return jwt.sign(payload, config.jwtSecret, { expiresIn });
};

const verifyToken = (token) => {
  try {
    const decoded = jwt.verify(token, config.jwtSecret);
    return decoded;
  } catch (error) {
    return null;
  }
};

module.exports = {
  generateToken,
  verifyToken,
};
  • We import the jsonwebtoken library to work with JWTs and load your JWT secret and other configuration settings from your config.js (as shown in previous responses).
  • generateToken is a function that takes a payload (usually user data) and an expiration time (e.g., ‘1h’ for one hour) and returns a JWT token.
  • verifyToken is a function that takes a token as an argument, verifies its authenticity using the JWT secret, and returns the decoded payload. If the token is invalid or has expired, it returns null.

You can use this utility to generate and verify tokens in your authentication and authorization processes. Here’s an example of how you might use it:

const Token = require('./utils/Token');

// Generate a token when a user logs in
const user = { id: 'user_id', email: 'user@example.com' };
const token = Token.generateToken(user, '1h');

// Verify the token when a protected route is accessed
const tokenFromHeader = req.header('x-auth-token');
const decodedUser = Token.verifyToken(tokenFromHeader);

if (decodedUser) {
  // Token is valid, access granted
  // You can use decodedUser to get user information
} else {
  // Token is invalid, access denied
}

auth.js routes

const express = require('express');
const router = express.Router();
const bcrypt = require('bcrypt');
const { check, validationResult } = require('express-validator');
const { generateToken } = require('../utils/Token');
const { authenticateUser } = require('../utils/auth');
const User = require('../models/User');

// User registration route
router.post(
  '/register',
  [
    check('firstName', 'First name is required').not().isEmpty(),
    check('lastName', 'Last name is required').not().isEmpty(),
    check('email', 'Please include a valid email').isEmail(),
    check('password', 'Please enter a password with 6 or more characters').isLength({ min: 6 }),
  ],
  async (req, res) => {
    const errors = validationResult(req);

    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }

    const { firstName, lastName, email, password } = req.body;

    try {
      let user = await User.findOne({ email });

      if (user) {
        return res.status(400).json({ message: 'User already exists' });
      }

      user = new User({ firstName, lastName, email, password });

      const salt = await bcrypt.genSalt(10);
      user.password = await bcrypt.hash(password, salt);

      await user.save();

      const payload = {
        user: {
          id: user.id,
          role: user.role, // You can add user roles as needed
        },
      };

      const token = generateToken(payload, '1h');

      return res.json({ token });
    } catch (err) {
      console.error(err.message);
      return res.status(500).json({ message: 'Server error' });
    }
  }
);

// User login route
router.post(
  '/login',
  [
    check('email', 'Please include a valid email').isEmail(),
    check('password', 'Password is required').exists(),
  ],
  async (req, res) => {
    const errors = validationResult(req);

    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }

    const { email, password } = req.body;

    try {
      let user = await User.findOne({ email });

      if (!user) {
        return res.status(400).json({ message: 'Invalid credentials' });
      }

      const isMatch = await bcrypt.compare(password, user.password);

      if (!isMatch) {
        return res.status(400).json({ message: 'Invalid credentials' });
      }

      const payload = {
        user: {
          id: user.id,
          role: user.role, // You can add user roles as needed
        },
      };

      const token = generateToken(payload, '1h');

      return res.json({ token });
    } catch (err) {
      console.error(err.message);
      return res.status(500).json({ message: 'Server error' });
    }
  }
);

// Protected route for user profile
router.get('/profile', authenticateUser(), async (req, res) => {
  try {
    // Access user information using req.user
    const user = await User.findById(req.user.id).select('-password');

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

    return res.json(user);
  } catch (err) {
    console.error(err.message);
    return res.status(500).json({ message: 'Server error' });
  }
});

module.exports
  • The /register route allows users to create new accounts. It validates the input data, checks if the user already exists, and securely hashes the password before saving it to the database.
  • The /login route handles user authentication by validating the email and password, checking for user existence, and verifying the password using bcrypt.
  • The /profile route is a protected route that requires user authentication. It returns the user’s profile information when a valid token is provided.

products.js routes and controllers

const express = require('express');
const router = express.Router();
const { check, validationResult } = require('express-validator');
const Product = require('../models/Product');
const { authenticateUser, authorizeUser } = require('../middleware/auth');

// Get a list of products
router.get('/', async (req, res) => {
  try {
    const products = await Product.find();
    return res.json(products);
  } catch (err) {
    console.error(err.message);
    return res.status(500).json({ message: 'Server error' });
  }
});

// Get product details by ID
router.get('/:id', async (req, res) => {
  try {
    const product = await Product.findById(req.params.id);
    if (!product) {
      return res.status(404).json({ message: 'Product not found' });
    }
    return res.json(product);
  } catch (err) {
    console.error(err.message);
    return res.status(500).json({ message: 'Server error' });
  }
});

// Create a new product
router.post(
  '/',
  authenticateUser(),
  authorizeUser('admin'), // Requires admin role
  [
    check('name', 'Name is required').not().isEmpty(),
    check('description', 'Description is required').not().isEmpty(),
    check('price', 'Price is required').not().isEmpty(),
  ],
  async (req, res) => {
    const errors = validationResult(req);

    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }

    const { name, description, price } = req.body;

    try {
      const product = new Product({ name, description, price });
      await product.save();

      return res.json(product);
    } catch (err) {
      console.error(err.message);
      return res.status(500).json({ message: 'Server error' });
    }
  }
);

// Update product details by ID
router.put(
  '/:id',
  authenticateUser(),
  authorizeUser('admin'), // Requires admin role
  async (req, res) => {
    try {
      const product = await Product.findById(req.params.id);

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

      // Update product details here based on request body

      await product.save();

      return res.json(product);
    } catch (err) {
      console.error(err.message);
      return res.status(500).json({ message: 'Server error' });
    }
  }
);

// Delete product by ID
router.delete(
  '/:id',
  authenticateUser(),
  authorizeUser('admin'), // Requires admin role
  async (req, res) => {
    try {
      const product = await Product.findById(req.params.id);

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

      await product.remove();

      return res.json({ message: 'Product deleted' });
    } catch (err) {
      console.error(err.message);
      return res.status(500).json({ message: 'Server error' });
    }
  }
);

module.exports = router;

orders.js routes

const express = require('express');
const router = express.Router();
const { check, validationResult } = require('express-validator');
const Order = require('../models/Order');
const Product = require('../models/Product');
const { authenticateUser } = require('../middleware/auth');

// Place a new order
router.post(
  '/',
  authenticateUser(),
  [
    check('products', 'Products are required').isArray().notEmpty(),
    check('totalAmount', 'Total amount is required').isFloat(),
    check('shippingAddress', 'Shipping address is required').notEmpty(),
  ],
  async (req, res) => {
    const errors = validationResult(req);

    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }

    const { products, totalAmount, shippingAddress } = req.body;

    try {
      // Validate product availability and calculate the total amount
      for (const item of products) {
        const product = await Product.findById(item.productId);

        if (!product) {
          return res.status(400).json({ message: 'Invalid product ID' });
        }

        if (product.stock < item.quantity) {
          return res.status(400).json({ message: 'Insufficient product stock' });
        }
      }

      // Create a new order
      const order = new Order({
        user: req.user.id,
        products,
        totalAmount,
        shippingAddress,
      });

      // Decrease product stock after the order is placed
      for (const item of products) {
        const product = await Product.findById(item.productId);
        product.stock -= item.quantity;
        await product.save();
      }

      await order.save();

      return res.json(order);
    } catch (err) {
      console.error(err.message);
      return res.status(500).json({ message: 'Server error' });
    }
  }
);

// Get order details by ID
router.get('/:id', authenticateUser(), async (req, res) => {
  try {
    const order = await Order.findById(req.params.id).populate('user', 'name email');
    if (!order) {
      return res.status(404).json({ message: 'Order not found' });
    }

    if (order.user.id !== req.user.id) {
      return res.status(403).json({ message: 'Unauthorized access to this order' });
    }

    return res.json(order);
  } catch (err) {
    console.error(err.message);
    return res.status(500).json({ message: 'Server error' });
  }
});

// Get a list of orders for the authenticated user
router.get('/user', authenticateUser(), async (req, res) => {
  try {
    const orders = await Order.find({ user: req.user.id });
    return res.json(orders);
  } catch (err) {
    console.error(err.message);
    return res.status(500).json({ message: 'Server error' });
  }
});

module.exports = router;

adminDashboard.js routes

const express = require('express');
const router = express.Router();
const { authenticateUser, authorizeUser } = require('../../middleware/auth');

// Admin dashboard route to list and manage users
router.get(
  '/users',
  authenticateUser(),
  authorizeUser('admin'), // Requires admin role
  async (req, res) => {
    try {
      // Fetch a list of users from your database
      // Implement logic to retrieve user details
      const users = await User.find();

      // You can render an admin dashboard view or return JSON data as needed
      return res.json(users);
    } catch (err) {
      console.error(err.message);
      return res.status(500).json({ message: 'Server error' });
    }
  }
);

// Admin dashboard route to manage products
router.get(
  '/products',
  authenticateUser(),
  authorizeUser('admin'), // Requires admin role
  async (req, res) => {
    try {
      // Fetch a list of products from your database
      // Implement logic to retrieve product details
      const products = await Product.find();

      // You can render an admin dashboard view or return JSON data as needed
      return res.json(products);
    } catch (err) {
      console.error(err.message);
      return res.status(500).json({ message: 'Server error' });
    }
  }
);

// Admin dashboard route to manage orders
router.get(
  '/orders',
  authenticateUser(),
  authorizeUser('admin'), // Requires admin role
  async (req, res) => {
    try {
      // Fetch a list of orders from your database
      // Implement logic to retrieve order details
      const orders = await Order.find();

      // You can render an admin dashboard view or return JSON data as needed
      return res.json(orders);
    } catch (err) {
      console.error(err.message);
      return res.status(500).json({ message: 'Server error' });
    }
  }
);

// Other admin dashboard routes can be added for additional features

module.exports = router;

server.js file

const express = require('express');
const morgan = require('morgan');
const helmet = require('helmet');
const cors = require('cors');
const mongoose = require('mongoose');
const path = require('path');
const config = require('./config/config');
const errorHandler = require('./utils/errorHandler');
const { authenticateUser } = require('./middleware/auth');

const app = express();

// Connect to the MongoDB database
mongoose
  .connect(config.mongoURI, {
    useNewUrlParser: true,
    useUnifiedTopology: true,
  })
  .then(() => {
    console.log('Connected to MongoDB');
  })
  .catch((err) => {
    console.error('MongoDB connection error:', err);
  });

// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cors());
app.use(helmet());

// Logging with morgan
if (process.env.NODE_ENV === 'development') {
  app.use(morgan('dev'));
}

// Serve static files
app.use(express.static(path.join(__dirname, 'public')));

// Routes
app.use('/api/auth', require('./routes/auth'));
app.use('/api/products', require('./routes/products'));
app.use('/api/orders', require('./routes/orders'));
app.use('/api/admin', authenticateUser(), require('./admin/adminDashboard'));

// Error handling
app.use(errorHandler);

const PORT = process.env.PORT || 5000;

app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});

category routes

const express = require('express');
const router = express.Router();
const { check, validationResult } = require('express-validator');
const Category = require('../models/Category');
const { authenticateUser } = require('../middleware/auth');

// List all categories
router.get('/', async (req, res) => {
  try {
    const categories = await Category.find();
    return res.json(categories);
  } catch (err) {
    console.error(err.message);
    return res.status(500).json({ message: 'Server error' });
  }
});

// Get category details by ID
router.get('/:id', async (req, res) => {
  try {
    const category = await Category.findById(req.params.id);
    if (!category) {
      return res.status(404).json({ message: 'Category not found' });
    }
    return res.json(category);
  } catch (err) {
    console.error(err.message);
    return res.status(500).json({ message: 'Server error' });
  }
});

// Create a new category
router.post(
  '/',
  authenticateUser(),
  [
    check('name', 'Name is required').not().isEmpty(),
    check('description', 'Description is required').not().isEmpty(),
  ],
  async (req, res) => {
    const errors = validationResult(req);

    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }

    const { name, description } = req.body;

    try {
      const category = new Category({ name, description });
      await category.save();

      return res.json(category);
    } catch (err) {
      console.error(err.message);
      return res.status(500).json({ message: 'Server error' });
    }
  }
);

// Update category details by ID
router.put(
  '/:id',
  authenticateUser(),
  async (req, res) => {
    try {
      const category = await Category.findById(req.params.id);

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

      // Update category details here based on request body

      await category.save();

      return res.json(category);
    } catch (err) {
      console.error(err.message);
      return res.status(500).json({ message: 'Server error' });
    }
  }
);

// Delete category by ID
router.delete(
  '/:id',
  authenticateUser(),
  async (req, res) => {
    try {
      const category = await Category.findById(req.params.id);

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

      await category.remove();

      return res.json({ message: 'Category deleted' });
    } catch (err) {
      console.error(err.message);
      return res.status(500).json({ message: 'Server error' });
    }
  }
);

module.exports = router;
  • routes/categories.js defines routes for listing categories, getting category details by ID, creating a new category, updating category details, and deleting a category.
  • The authenticateUser middleware ensures that only authenticated users can access these routes.
  • You should implement the necessary database queries and logic to interact with your Category model for creating, updating, and deleting categories. Similarly, you can extend these routes to include more advanced category-related features as per your application’s requirements.

online transaction routes

const express = require('express');
const router = express.Router();
const { check, validationResult } = require('express-validator');
const Transaction = require('../models/Transaction');
const Order = require('../models/Order');
const { authenticateUser } = require('../middleware/auth');
const PaymentService = require('../services/PaymentService');

// Process an online payment
router.post(
  '/',
  authenticateUser(),
  [
    check('orderId', 'Order ID is required').notEmpty(),
    check('paymentMethod', 'Payment method is required').notEmpty(),
  ],
  async (req, res) => {
    const errors = validationResult(req);

    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }

    const { orderId, paymentMethod } = req.body;

    try {
      // Verify that the order belongs to the authenticated user
      const order = await Order.findOne({ _id: orderId, user: req.user.id });

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

      // Calculate the payment amount based on the order details
      const paymentAmount = calculatePaymentAmount(order);

      // Initiate the payment process with the selected payment method (e.g., PayPal, Stripe)
      const paymentService = new PaymentService(paymentMethod);
      const paymentResult = await paymentService.processPayment(order, paymentAmount);

      if (paymentResult.success) {
        // Create a transaction record
        const transaction = new Transaction({
          user: req.user.id,
          order: orderId,
          amount: paymentAmount,
          status: 'completed',
          paymentMethod: paymentMethod,
        });

        await transaction.save();

        // Update the order status to 'paid'
        order.status = 'paid';
        await order.save();

        return res.json({ message: 'Payment successful', transaction: transaction });
      } else {
        return res.status(400).json({ message: 'Payment failed', error: paymentResult.error });
      }
    } catch (err) {
      console.error(err.message);
      return res.status(500).json({ message: 'Server error' });
    }
  }
);

// Calculate the payment amount based on order details (replace with your logic)
function calculatePaymentAmount(order) {
  // Implement your payment calculation logic here
  // For example, sum up the prices of order items
  let totalAmount = 0;
  order.items.forEach((item) => {
    totalAmount += item.price * item.quantity;
  }
  return totalAmount;
}

module.exports = router;
  • routes/transactions.js defines a route for processing online payments. It requires authentication, and the user must provide the order ID and payment method.
  • The calculatePaymentAmount function calculates the payment amount based on the order’s items and prices. You should customize this function according to your application’s pricing logic.
  • The route initiates the payment process using a PaymentService (replace with your payment gateway service) and updates the order’s status and creates a transaction record accordingly.

contact Message model

const mongoose = require('mongoose');
const nodemailer = require('nodemailer');

const contactSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true,
  },
  email: {
    type: String,
    required: true,
  },
  message: {
    type: String,
    required: true,
  },
  createdAt: {
    type: Date,
    default: Date.now,
  },
});

const Contact = mongoose.model('Contact', contactSchema);

// Send an email when a new contact message is created
Contact.post('save', async function () {
  const transporter = nodemailer.createTransport({
    service: 'your-email-service-provider',
    auth: {
      user: 'your-email@example.com',
      pass: 'your-email-password',
    },
  });

  const mailOptions = {
    from: 'your-email@example.com',
    to: 'recipient@example.com',
    subject: 'New Contact Message',
    text: `You have received a new contact message from ${this.name} (${this.email}): \n\n${this.message}`,
  };

  try {
    const info = await transporter.sendMail(mailOptions);
    console.log(`Email sent: ${info.response}`);
  } catch (error) {
    console.error('Error sending email:', error);
  }
});

module.exports = Contact;

contact routes

const express = require('express');
const router = express.Router();
const { check, validationResult } = require('express-validator');
const Contact = require('../models/Contact');
const nodemailer = require('nodemailer');

// Create a new contact message
router.post(
  '/',
  [
    check('name', 'Name is required').not().isEmpty(),
    check('email', 'Please include a valid email').isEmail(),
    check('message', 'Message is required').not().isEmpty(),
  ],
  async (req, res) => {
    const errors = validationResult(req);

    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }

    const { name, email, message } = req.body;

    try {
      // Create a new contact message record
      const contact = new Contact({
        name,
        email,
        message,
      });

      await contact.save();

      // Send an email notification
      const transporter = nodemailer.createTransport({
        service: 'your-email-service-provider',
        auth: {
          user: 'your-email@example.com',
          pass: 'your-email-password',
        },
      });

      const mailOptions = {
        from: 'your-email@example.com',
        to: 'recipient@example.com',
        subject: 'New Contact Message',
        text: `You have received a new contact message from ${name} (${email}):\n\n${message}`,
      };

      const info = await transporter.sendMail(mailOptions);

      console.log(`Email sent: ${info.response}`);

      return res.json({ message: 'Contact message sent successfully' });
    } catch (error) {
      console.error('Error sending email:', error);
      return res.status(500).json({ message: 'Server error' });
    }
  }
);

module.exports = router;
  • routes/contact.js defines a route for creating a new contact message.
  • We use the express-validator library to validate the name, email, and message fields. If validation fails, a 400 Bad Request response with validation errors is returned.
  • The route saves the contact message to the database using the Contact model.
  • It also sends an email notification to a specified recipient using Nodemailer. Replace 'your-email-service-provider', 'your-email@example.com', and 'recipient@example.com' with your actual email service provider and email addresses.
  • The email’s subject and content are customizable based on your requirements.
  • The route returns a success message when the contact message is successfully created and the email is sent.

forgot password and reset password in User Routes and model

models/User.js:

const mongoose = require('mongoose');
const crypto = require('crypto');
const { Schema } = mongoose;

const userSchema = new Schema({
  // ... other user fields ...

  resetPasswordToken: String,
  resetPasswordExpires: Date,
});

// Generate a reset password token and expiration date
userSchema.methods.generatePasswordReset = function () {
  this.resetPasswordToken = crypto.randomBytes(20).toString('hex');
  this.resetPasswordExpires = Date.now() + 3600000; // Token expires in 1 hour
};

const User = mongoose.model('User', userSchema);

module.exports = User;

routes/auth.js:

const express = require('express');
const router = express.Router();
const { check, validationResult } = require('express-validator');
const User = require('../models/User');
const nodemailer = require('nodemailer');

// Forgot Password: Generate and send a reset password link via email
router.post('/forgot-password', [
  check('email', 'Please include a valid email').isEmail(),
], async (req, res) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(400).json({ errors: errors.array() });
  }

  const { email } = req.body;

  try {
    const user = await User.findOne({ email });

    if (!user) {
      return res.status(400).json({ message: 'User not found' });
    }

    // Generate a reset token and expiration date
    user.generatePasswordReset();
    await user.save();

    // Send a reset link via email
    const transporter = nodemailer.createTransport({
      service: 'your-email-service-provider',
      auth: {
        user: 'your-email@example.com',
        pass: 'your-email-password',
      },
    });

    const resetLink = `${req.headers.origin}/reset-password/${user.resetPasswordToken}`;

    const mailOptions = {
      from: 'your-email@example.com',
      to: user.email,
      subject: 'Password Reset',
      text: `You are receiving this because you (or someone else) have requested to reset your password.
      Please click on the following link to reset your password: ${resetLink}`,
    };

    const info = await transporter.sendMail(mailOptions);

    console.log(`Email sent: ${info.response}`);

    return res.json({ message: 'Password reset link sent to your email' });
  } catch (error) {
    console.error('Error sending email:', error);
    return res.status(500).json({ message: 'Server error' });
  }
});

// Reset Password: Validate reset token and update the user's password
router.post('/reset-password/:token', [
  check('password', 'Password is required').not().isEmpty(),
], async (req, res) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(400).json({ errors: errors.array() });
  }

  const { token } = req.params;
  const { password } = req.body;

  try {
    const user = await User.findOne({
      resetPasswordToken: token,
      resetPasswordExpires: { $gt: Date.now() },
    });

    if (!user) {
      return res.status(400).json({ message: 'Invalid or expired token' });
    }

    // Update the user's password
    user.password = password;
    user.resetPasswordToken = undefined;
    user.resetPasswordExpires = undefined;

    await user.save();

    return res.json({ message: 'Password reset successfully' });
  } catch (error) {
    console.error(error.message);
    return res.status(500).json({ message: 'Server error' });
  }
});

module.exports = router;
  • POST /forgot-password allows a user to request a password reset. It generates a reset token and sends it via email. Replace 'your-email-service-provider', 'your-email@example.com', and 'your-email-password' with your actual email service provider and email addresses.
  • POST /reset-password/:token validates the reset token and updates the user’s password. It checks if the token is valid and not expired, then updates the password and clears the token and expiration date.

JavaScript

Graph algorithms in JavaScript

Sorting algorithms in JavaScript

stock market analysis tools free

Leave a Comment

Skip to content