API

ForgotPassword API

This code is an implementation of a password reset functionality in a web application. Here’s a detailed explanation of each part of the code:

./pages/api/auth/forgot-password/index.ts


import db from "@/utils/db";
import User from "@/models/User";
import Env from "@/config/env";
import Cryptr from "cryptr";
import cryptoRandomString from "crypto-random-string";
import { sendEmail } from "@/config/mail";
import { render } from "@react-email/render";
import ForgotPasswordEmail from "@/emails/ForgotPasswordEmail";

export default async function POST(req:any, res:any) {
  const payload:ForgotPasswordPayload = await req.body;
  if(req.method !== 'POST') {
    return;
 }
  await db.connect();
  const user = await User.findOne({ email: payload.email });
  if (user == null) {
    res.status(422).json({message: 'Nonexistent user!'},console.log('out'));
    await db.disconnect();
    return;
  }

  const randomStr = cryptoRandomString({
    length: 64,
    type: "alphanumeric",
  });

  user.password_reset_token = randomStr;
  await user.save();

  const crypt = new Cryptr(Env.SECRET_KEY);
  const encryptedEmail = crypt.encrypt(user.email);
  const url = Env.APP_URL + "/reset-password/" + encryptedEmail + "?signature=" + randomStr + "&mail=" + encryptedEmail;

  console.log(url)

  try {
    const html =  render(
      ForgotPasswordEmail({
        params: {
          name: user.name,
          email: user.email,
          url: url,
        },
      })
    );
    await sendEmail(payload.email, "Reset Password", html);
    return res.json({
      status: 200,
      message: "Email successfully sent. Please check your email.",
    });
  } catch (error) {
    console.log("the error is", error);
    return res.json({
      status: 500,
      message: "Something went wrong, please try again!",
    });
  }
}
    

Imports

  1. - db:Utility to handle database connections.
  2. - User: The user model used for database operations.
  3. - Env: Configuration settings, likely including environment variables.
  4. - Cryptr: A library for encryption and decryption.
  5. - cryptoRandomString: Generates random strings for secure tokens.
  6. - sendEmail: : Function to send emails.
  7. - render: Used to render the email template.
  8. - ForgotPasswordEmail: The email template for the password reset emai

Function Definition:

This is an asynchronous function that handles POST requests for password reset.

Payload and Method Check:

  1. - payload: The data sent in the request body, expected to follow the ForgotPasswordPayload structure.
  2. - Checks if the HTTP request method is POST. If not, it returns early without processing further.

Database connection:

Connects to the database.

User Lookup:

  1. - Looks for a user in the database by email.
  2. - If no user is found, it sends a 422 status response with an error message and disconnects from the database.

Token Generation and Assignment:

  1. - Generates a random alphanumeric string of length 64.
  2. - Assigns this string as the user's password reset token.
  3. - Saves the updated user document to the database.

Email Encryption and URL Creation:

  1. - Initializes an encryption object with a secret key from the environment variables.
  2. - Encrypts the user's email.
  3. - Constructs a password reset URL containing the encrypted email and the token as query parameters.
  4. - Logs the URL to the console.

Email Rendering and Sending:

  1. - Tries to render the password reset email using the ForgotPasswordEmail template, including the user’s name, email, and reset URL.
  2. - Sends the rendered email to the user.
  3. - If successful, sends a JSON response indicating the email was sent.
  4. - If an error occurs, logs the error and sends a JSON response indicating a failure.

ResetPassword API

This code is an implementation of a password reset handler that updates the user's password in a web application. Here’s a detailed explanation of each part of the code:

./pages/api/auth/reset-password/index.ts


import  User  from "@/models/User";
import Cryptr from "cryptr";
import Env from "@/config/env";
import db from "@/utils/db";
import bcrypt from "bcryptjs";


type ResetPasswordPayload = {
  email: string;
  signature: string;
  password: string;
  password_confirmation: string;
};

export default async function POST(req: any, res:any) {
const payload: ResetPasswordPayload = await req.body;
 
  console.log('payload: ',payload)
  // * Decrypt string
  const crypter = new Cryptr(Env.SECRET_KEY);
  const email = crypter.decrypt(payload.email);
  
  
  await db.connect();
  const user = await User.findOne({
    email: email,
    password_reset_token: payload.signature,
  });
  
  if (user == null || user == undefined) {
    return res.json({
      status: 400,
      message: "Something went wrong.Please try again .",
    });
    
  }

  const salt = bcrypt.genSaltSync(10);
  user.password = bcrypt.hashSync(payload.password, salt);
  user.password_reset_token = '';
  await user.save();

  return res.json({
    status: 200,
    message: "Password updated successfully. please log in with new password",
  });
}
    

Imports

  1. - db:Utility to handle database connections.
  2. - User: The user model used for database operations.
  3. - Env: Configuration settings, likely including environment variables.
  4. - Cryptr: A library for encryption and decryption.
  5. - bcrypt: A library to hash passwords.

Function Definition:

This is an asynchronous function that handles POST requests for resetting the password.

Extract Payload:

Extracts the data sent in the request body and logs it.

Decrypt Email:

  1. - Initializes an encryption object with a secret key from the environment variables.
  2. - Decrypts the email from the payload.

Password Hashing and Update:

  1. - Generates a salt for hashing the new password.
  2. - Hashes the new password and assigns it to the user.
  3. - Clears the password reset token.
  4. - Saves the updated user document to the database.

Summary

The code effectively handles the process of resetting a user's password. It includes the following steps:

  1. 1- Extracts and logs the payload from the request body.
  2. 2- Decrypts the email from the payload.
  3. 3- Connects to the database.
  4. 4- Verifies the user exists and matches the reset token.
  5. 5- Hashes the new password.
  6. 6- Updates the user's password and clears the reset token.
  7. 7- Sends a success response

Server

This code provides a utility for managing connections to a MongoDB database using Mongoose, along with a function to convert Mongoose documents to plain JavaScript objects

./utils/db.js


import mongoose from "mongoose";


const connection = {}

async function connect() {
    if(connection.isConnected) {
        console.log('already connected!')
        return;
    }
    if (mongoose.connections.length > 0) {
        connection.isConnected = mongoose.connections[0].readyState;
        if(connection.isConnected === 1){
            console.log('use previous connection')
            return;
        }
        await mongoose.disconnect();
    }
   const db = await mongoose.connect(process.env.MONGODB_URI)
   console.log('new connection')
   connection.isConnected = db.connections[0].readyState;

}
async function disconnect() {
    if (connection.isConnected) {
        if(process.env.NODE_ENV === 'production') {
            await mongoose.disconnect();
            connection.isConnected = false;
    }else{
        console.log('not disconnected')
    }
  }
}
//convert object to JSON function
function convertDocToObj(doc) {
    doc._id = doc._id.toString();
    doc.createdAt = doc.createdAt.toString();
    doc.updatedAt = doc.updatedAt.toString();
    return doc;
}

const db =  { connect,  disconnect, convertDocToObj };
export default db;

    

Connect Functions

  1. - Checks if already connected (connection.isConnected). If so, logs a message and returns.
  2. - If there are existing Mongoose connections (mongoose.connections.length > 0) , it updates connection.isConnected to the state of the first connection (mongoose.connections[0].readyState) .
  3. - If the existing connection is ready (readyState === 1), it logs a message and returns.
  4. - f not, it disconnects from the current connection.
  5. - Establishes a new connection to the MongoDB URI specified in the environment variables (process.env.MONGODB_URI) .
  6. - Logs a message indicating a new connection and updates the connection state.
  7. - Checks if currently connected (connection.isConnected).
  8. - If in a production environment (process.env.NODE_ENV === 'production'), disconnects from the database and updates the connection state.
  9. - Otherwise, logs a message indicating that the connection is not disconnected.

Convert Document to Object Function:

  1. - Converts a Mongoose document to a plain JavaScript object.
  2. - Converts the _id, createdAt, and updatedAt fields to strings to ensure they can be easily serialized or manipulated.

Summary

This code provides a well-structured utility for managing MongoDB connections using Mongoose. It includes functions for connecting to and disconnecting from the database, along with a helper function to convert Mongoose documents to plain JavaScript objects. This utility ensures efficient database connection management and simplifies the handling of database documents in the application.

Transporter Configuration

./config/mail.ts


import nodemailer from "nodemailer";
import Env from '@/config/env'

export const transporter = nodemailer.createTransport({
  host: Env.SMTP_HOST,
  port: Number(Env.SMTP_PORT),
  secure: false,
  auth: {
    user: Env.SMTP_USER,
    pass: Env.SMTP_PASSWORD,
  },
});

  //TO send the email

 export const sendEmail = async (
    to: string,
    subject: string,
    html: string
  ): Promise<string | null> => {
    const info = await transporter.sendMail({
      from: Env.EMAIL_FROM,
      to: to,
      subject: subject,
      html: html,
    });
  
    return info ?.messageId;
  };

    
  1. - Creates a transporter object using nodemailer.createTransport.
  2. - Configures the SMTP server details:
  3. - host: The SMTP host address from the environment variable.
  4. - port: The SMTP port number, converted to a number using Number().
  5. - secure: Set to false, meaning the connection will not use SSL/TLS. This can be changed to true if SSL/TLS is required.
  6. - auth: Authentication details including the username and password from the environment variables.

sendEmail Function

  1. - sendEmail: An asynchronous function that sends an email.
  2. - Parameters:
  3. - to: The recipient's email address.
  4. - subject: The subject of the email.
  5. - html: The HTML content of the email.
  6. - transporter.sendMail: Uses the configured transporter to send the email with the specified from, to, subject, and html content.
  7. - The function returns the messageId of the sent email, which is useful for tracking or logging purposes. If messageId is not available, it returns null..

Summary

The code sets up an email sending utility using nodemailer. The transporter is configured using SMTP details from environment variables, ensuring sensitive information like SMTP credentials are not hardcoded in the codebase. The sendEmail function provides a convenient way to send emails with specified recipients, subjects, and HTML content, making it reusable throughout the application. The use of async/await ensures that the function handles asynchronous operations correctly.

./config/env.ts


    class Env {
      static SMTP_HOST: string = process.env.SMTP_HOST!;
      static SMTP_PORT: string = process.env.SMTP_PORT!;
      static SMTP_USER: string = process.env.SMTP_USER!;
      static SMTP_PASSWORD: string = process.env.SMTP_PASSWORD!;
      static SMTP_SECURE: string = process.env.SMTP_SECURE!;
      static EMAIL_FROM: string = process.env.EMAIL_FROM!;
      static SECRET_KEY: string = process.env.NEXTAUTH_SECRET!;
      static APP_URL: string = process.env.APP_URL!;
  }
  
  export default Env