ExpressJs

⌘K
  1. Home
  2. Docs
  3. ExpressJs
  4. Role And Permission
  5. Project Overview

Project Overview

আমাদের প্রজেক্ট টি একটি typeorm এবং এক্সপ্রেস বেসড প্রজেক্ট যার একটি User নামের entity রয়েছে এবং একটি database কানেকশন এর কোড রয়েছে।

Database Connection

// Import necessary packages
require('reflect-metadata');
require('dotenv').config(); // Load environment variables

const { DataSource } = require('typeorm');
const path = require('path');

// Import your entities here
const User = require('../entities/User'); 
// Common configuration options
const commonConfig = {
    synchronize: process.env.DB_SYNCHRONIZE === 'true',
    logging: process.env.DB_LOGGING === 'true',
    entities: [User], // Add your entities here
    migrations: [
        path.join(__dirname, 'migrations/*.js'), // Path to your migration files
    ],
    subscribers: [], // Add your subscribers here or load from a specific file
};

// Method to configure MySQL or other SQL-based databases
const getMySqlConfig = () => ({
    type: 'mysql',
    host: process.env.DB_HOST || 'localhost',
    port: parseInt(process.env.DB_PORT, 10) || 3306,
    username: process.env.DB_USERNAME || 'root',
    password: process.env.DB_PASSWORD || '',
    database: process.env.DB_DATABASE || 'passportjs_example',
});

// Method to configure SQLite
const getSqliteConfig = () => ({
    type: 'sqlite',
    database: process.env.DB_DATABASE || './database.sqlite', // SQLite database file path
});

// Method to configure MongoDB
const getMongoDbConfig = () => ({
    type: 'mongodb',
    url: `mongodb://${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_DATABASE}`, // MongoDB connection string
    useNewUrlParser: true, // MongoDB-specific options
    useUnifiedTopology: true, // MongoDB-specific options
});

// Method to select the appropriate database configuration based on DB_TYPE
const getDatabaseConfig = (dbType) => {
    switch (dbType) {
        case 'sqlite':
            return getSqliteConfig();
        case 'mongodb':
            return getMongoDbConfig();
        case 'mysql':
        default:
            return getMySqlConfig();
    }
};

// Determine the database type from environment variables
const dbType = process.env.DB_TYPE || 'mysql';

// Initialize Data Source with merged configurations
const AppDataSource = new DataSource({
    ...commonConfig,
    ...getDatabaseConfig(dbType),
});

AppDataSource.initialize()
    .then(() => {
        console.log('Data Source has been initialized!');
    })
    .catch((err) => {
        console.error('Error during Data Source initialization:', err);
    });

module.exports = AppDataSource;

Entity

const { EntitySchema } = require('typeorm');

module.exports = new EntitySchema({
    name: 'User',
    tableName: 'users',
    columns: {
        id: {
            type: 'int',
            primary: true,
            generated: true,
        },
        username: {
            type: 'varchar',
            unique: true,
            nullable: true,
        },
        email: {
            type: 'varchar',
            unique: true,
            nullable: true,
        },
        password: {
            type: 'varchar',
            nullable: true,
        },
        phone: {
            type: 'varchar',
            unique: true,
            nullable: true,
        },
        firstName: {
            type: 'varchar',
            nullable: true,
        },
        lastName: {
            type: 'varchar',
            nullable: true,
        },
        role: {
            type: 'varchar',
            default: 'subscriber',  // Default value for the role column
        },
        isActivated: {
            type: 'boolean',
            default: false,
        },
        googleId: {
            type: 'varchar',
            unique: true,
            nullable: true,
        },
        facebookId: {
            type: 'varchar',
            unique: true,
            nullable: true,
        },
        otp: {
            type: 'varchar',
            nullable: true,
        },
        otpExpires: {
            type: 'timestamp',
            nullable: true,
        },
        activationToken: {
            type: 'varchar',
            nullable: true,
        },
        resetToken: {
            type: 'varchar',
            nullable: true,
        },
        createdAt: {
            type: 'timestamp',
            createDate: true,
        },
        updatedAt: {
            type: 'timestamp',
            updateDate: true,
        },
    },
});

Helper method

আমি এখানে কিছু হেল্পার মেথড ব্যবহার করেছি যাতে register মেথডটি অনেক গুলো কোডে হিজিবিজি না হয়ে যায়

const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const User = require('../entities/User');
// Check if User Exists
const checkIfUserExists = async (userRepository, username, email, phone) => {
    return await userRepository.findOne({ where: [{ username }, { email }, { phone }] });
};

// Hash Password
const hashPassword = async (password) => {
    return await bcrypt.hash(password, 10);
};

// Create New User
const createNewUser = (userRepository, username, email, hashedPassword, phone) => {
    return userRepository.create({
        username,
        email,
        password: hashedPassword,
        phone,
        isActivated: false,
    });
};

// Save New User
const saveUser = async (userRepository, newUser) => {
    return await userRepository.save(newUser);
};

User রেজিস্ট্রেশন এর জন্য আমার একটি কন্ট্রোলার আছে

const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const AppDataSource = require('../config/database');
const User = require('../entities/User');


/**
 * নতুন ব্যবহারকারী নিবন্ধন করার জন্য এই ফাংশনটি ব্যবহৃত হয়।
 * @function register
 * @description ইউজার ডিভাইস থেকে প্রাপ্ত প্যারামিটারগুলো (যেমন ইউজারনেম, ইমেইল, পাসওয়ার্ড, ফোন নম্বর) প্রক্রিয়া করে এবং নতুন ব্যবহারকারী তৈরি করে।
 * @param {object} req - রিকোয়েস্ট অবজেক্ট যা ব্যবহারকারীর ইনপুট ডেটা ধারণ করে।
 * @param {object} res - রেসপন্স অবজেক্ট যা ক্লায়েন্টকে রেসপন্স পাঠাতে ব্যবহৃত হয়।
 * @returns {Promise<void>} - ফাংশনটি একটি প্রমিস রিটার্ন করে যা রিকোয়েস্ট সম্পন্ন হওয়ার পর রেসপন্স প্রদান করে।
 */
const register = async (req, res) => {
    // রিকোয়েস্ট বডি থেকে প্যারামিটারগুলো গ্রহণ করা হচ্ছে
    const { username, email, password, phone } = req.body;

    // ইউজার রেপোজিটরি তৈরি করা হচ্ছে যা ডাটাবেজে ব্যবহারকারীর তথ্য পরিচালনা করবে
    const userRepository = AppDataSource.getRepository(User);

    try {
        // চেক করা হচ্ছে ব্যবহারকারী ইতোমধ্যে বিদ্যমান কিনা
        const existingUser = await checkIfUserExists(userRepository, username, email, phone);
        if (existingUser) {
            // যদি ব্যবহারকারী ইতোমধ্যে বিদ্যমান থাকে, তাহলে 400 স্টেটাস কোড এবং একটি মেসেজ পাঠানো হচ্ছে
            return res.status(400).json({ message: 'ব্যবহারকারী ইতোমধ্যে নিবন্ধিত' });
        }

        // পাসওয়ার্ড হ্যাশ করা হচ্ছে যাতে নিরাপত্তা নিশ্চিত করা যায়
        const hashedPassword = await hashPassword(password);

        // নতুন ব্যবহারকারী তৈরি করা হচ্ছে দেওয়া ইনপুট তথ্য ব্যবহার করে
        const newUser = createNewUser(userRepository, username, email, hashedPassword, phone);

        // নতুন ব্যবহারকারী ডাটাবেজে সংরক্ষণ করা হচ্ছে
        await saveUser(userRepository, newUser);
                
    } catch (err) {
        // যদি কোন অপ্রত্যাশিত ত্রুটি ঘটে, তাহলে 500 স্টেটাস কোড এবং ত্রুটির মেসেজ পাঠানো হচ্ছে
        return res.status(500).json({ message: 'ব্যবহারকারী নিবন্ধন করতে ত্রুটি', error: err.message });
    }
};

ইমেইল দিয়ে রেজিস্ট্রেশন

আমরা যদি ইমেইল দিয়ে রেজিস্ট্রেশন করতে চাই তাহলে আমাদের কয়েকটি জিনিস খেয়াল রাখতে হবে :

  • যদি ইমেইল ভেরিফিকেশন চালু রাখি তাহলে ইউজার কে ইমেইল সেন্ড করতে হবে
  • ইউজারকে OTP ভেরিফিকেশন অথবা টোকেন জেনারেট করে পাঠাতে হবে

এজন্য আমরা নিচের হেলপার মেথড ব্যবহার করবো

sendEmail.js

// src/utils/sendEmail.js
const nodemailer = require('nodemailer');
require('dotenv').config(); // Ensure environment variables are loaded

/**
 * Sends an email using Nodemailer with the provided options.
 * @param {Object} options - The email options.
 * @param {string} options.to - The recipient's email address.
 * @param {string} options.subject - The subject of the email.
 * @param {string} options.text - The body text of the email.
 */
const sendEmail = async (options) => {
    // Create a Nodemailer transporter using environment variables
    const transporter = nodemailer.createTransport({
        service: process.env.EMAIL_SERVICE, // Email service from environment variables
        auth: {
            user: process.env.EMAIL_USER, // Your email address from environment variables
            pass: process.env.EMAIL_PASS, // Your email password (or app password) from environment variables
        },
        port: parseInt(process.env.EMAIL_PORT, 10) || 465, // Port from environment variables, default to 465
        secure: process.env.EMAIL_SECURE === 'true', // Secure option (true for port 465, false for port 587)
        // Optional settings
        connectionTimeout: parseInt(process.env.EMAIL_CONNECTION_TIMEOUT, 10) || 5000, // Connection timeout
        maxConnections: parseInt(process.env.EMAIL_MAX_CONNECTIONS, 10) || 5, // Max simultaneous connections
        // host: process.env.EMAIL_HOST, // Optionally set host if not using 'service'
    });

    // Email options
    const mailOptions = {
        from: process.env.EMAIL_USER, // Sender's email address
        to: options.to,             // Recipient's email address
        subject: options.subject,   // Email subject
        text: options.text,         // Email body text
    };

    try {
        // Send the email
        await transporter.sendMail(mailOptions);
        console.log('Email sent successfully!');
    } catch (error) {
        console.error('Error sending email:', error);
    }
};

module.exports = sendEmail;

generateToken

// Generate JWT Token
const generateToken = (user) => {
    return jwt.sign(
        { id: user.id, email: user.email,role:user.role}, // Payload
        process.env.JWT_SECRET, // Secret key
        { expiresIn: '1h' } // Options
    );
};

ইমেইল রেজিস্ট্রেশন হ্যান্ডলার মেথড

আমরা দুইটি ভাবে ইমেইল দিয়ে রেজিস্ট্রেশন করবো ইমেইল ভেরিফিকেশন সহ এবং ইমেইল ভেরিফিকেশন ছাড়া।

ইমেইল ভেরিফিকেশন সহ :

এখানে বলা হয়েছে যদি ENV ফাইলে বলা হয় EMAIL_VERIFICATION_ENABLED টি চালু আছে তাহলে এটি আরেকটি জিনিস চেক করবে : OTP_VERIFICATION_ENABLED যদি অন থাকে তাহলে USER কে OTP পাঠাবে SENDEMAIL মেথড দিয়ে যা আমরা আগে বানিয়ে রেখেছি আর যদি অফ থাকে তাহলে টোকেন পাঠাবে ইমেইল এ এবং ইউজার একটিভ হবে না হিসাবে সেভ হবে ডেটাবেজে এরপর টোকেন এবং OTP ভেরিফাই হওয়ার পর ইউজার এক্টিভেট হবে।

ইমেইল ভেরিফিকেশন ছাড়া

যদি ইমেইল ভেরিফিকেশন EMAIL_VERIFICATION_ENABLED অফ থাকে তাহলে আর কোনো ইমেইল পাঠনো লাগবে না ইউজার টি সেভ করবো ডাটাবেজে এবং ইউজার একটিভ হবে

// Email Registration Handler
const handleEmailRegistration = async (userRepository, newUser) => {
    if (process.env.EMAIL_VERIFICATION_ENABLED === 'true') {
        if (process.env.OTP_VERIFICATION_ENABLED === 'true') {
            // Send OTP to email
            const otp = generateOtp();
            newUser.otp = otp;
            await saveUser(userRepository, newUser);

            try {
                await sendEmail({
                    to: newUser.email,
                    subject: 'OTP for Registration',
                    text: `Your OTP is ${otp}. Use this code to complete your registration.`,
                });
                return { success: true, message: 'ব্যবহারকারী সফলভাবে নিবন্ধিত। OTP পাঠানো হয়েছে, দয়া করে যাচাই করুন।' };
            } catch (emailError) {
                return { success: false, message: 'ব্যবহারকারী নিবন্ধিত হয়েছে, কিন্তু ইমেল পাঠাতে ব্যর্থ হয়েছে।', error: emailError.message };
            }
        } else {
            // Send activation email
             token = generateToken(newUser);

            const activationLink = `${process.env.EMAIL_VERIFY_ACTIVATION_LINK}/${token}`;

            try {
                await sendEmail({
                    to: newUser.email,
                    subject: 'Account Activation',
                    text: `Please click the following link to activate your account: ${activationLink}`,
                });
                return { success: true, message: 'ব্যবহারকারী সফলভাবে নিবন্ধিত। অ্যাকাউন্ট সক্রিয়করণের জন্য ইমেল পাঠানো হয়েছে।' };
            } catch (emailError) {
                return { success: false, message: 'ব্যবহারকারী নিবন্ধিত হয়েছে, কিন্তু অ্যাকাউন্ট সক্রিয়করণ ইমেল পাঠাতে ব্যর্থ হয়েছে।', error: emailError.message };
            }
        }
    } else {
        // Default activation
        newUser.isActivated = true;
        await saveUser(userRepository, newUser);
        return { success: true, message: 'ব্যবহারকারী সফলভাবে নিবন্ধিত। প্রয়োজনীয় যাচাই সম্পন্ন করুন।' };
    }
};

ফোন দিয়ে রেজিস্ট্রেশন :

// utils/sendSms.js
const twilio = require('twilio');

const accountSid = process.env.TWILIO_ACCOUNT_SID;
const authToken = process.env.TWILIO_AUTH_TOKEN;
const client = new twilio(accountSid, authToken);

const sendSms = async (phone, otp) => {
    try {
        await client.messages.create({
            body: `Your OTP is: ${otp}`,
            from: process.env.TWILIO_PHONE_NUMBER,
            to: phone,
        });
        console.log('OTP sent successfully');
    } catch (err) {
        console.error('Error sending OTP:', err);
    }
};

module.exports = { sendSms };

// Phone Registration Handler
const handlePhoneRegistration = async (userRepository, newUser, phone) => {
    if (process.env.PHONE_VERIFICATION_ENABLED === 'true') {
        // Send OTP to phone
        const otp = generateOtp();
        newUser.otp = otp;
        await saveUser(userRepository, newUser);

        await sendSms(phone, `Your OTP is ${otp}. Use this code to complete your registration.`);
        return { success: true, message: 'ব্যবহারকারী সফলভাবে নিবন্ধিত। অ্যাকাউন্ট সক্রিয়করণের জন্য OTP পাঠানো হয়েছে।' };
    } else {
        // Default activation
        newUser.isActivated = true;
        await saveUser(userRepository, newUser);
        return { success: true, message: 'ব্যবহারকারী সফলভাবে নিবন্ধিত। প্রয়োজনীয় যাচাই সম্পন্ন করুন।' };
    }
};

/**
 * নতুন ব্যবহারকারী নিবন্ধন করার জন্য এই ফাংশনটি ব্যবহৃত হয়।
 * @function register
 * @description ইউজার ডিভাইস থেকে প্রাপ্ত প্যারামিটারগুলো (যেমন ইউজারনেম, ইমেইল, পাসওয়ার্ড, ফোন নম্বর) প্রক্রিয়া করে এবং নতুন ব্যবহারকারী তৈরি করে।
 * @param {object} req - রিকোয়েস্ট অবজেক্ট যা ব্যবহারকারীর ইনপুট ডেটা ধারণ করে।
 * @param {object} res - রেসপন্স অবজেক্ট যা ক্লায়েন্টকে রেসপন্স পাঠাতে ব্যবহৃত হয়।
 * @returns {Promise<void>} - ফাংশনটি একটি প্রমিস রিটার্ন করে যা রিকোয়েস্ট সম্পন্ন হওয়ার পর রেসপন্স প্রদান করে।
 */
const register = async (req, res) => {
    // রিকোয়েস্ট বডি থেকে প্যারামিটারগুলো গ্রহণ করা হচ্ছে
    const { username, email, password, phone } = req.body;

    // ইউজার রেপোজিটরি তৈরি করা হচ্ছে যা ডাটাবেজে ব্যবহারকারীর তথ্য পরিচালনা করবে
    const userRepository = AppDataSource.getRepository(User);

    try {
        // চেক করা হচ্ছে ব্যবহারকারী ইতোমধ্যে বিদ্যমান কিনা
        const existingUser = await checkIfUserExists(userRepository, username, email, phone);
        if (existingUser) {
            // যদি ব্যবহারকারী ইতোমধ্যে বিদ্যমান থাকে, তাহলে 400 স্টেটাস কোড এবং একটি মেসেজ পাঠানো হচ্ছে
            return res.status(400).json({ message: 'ব্যবহারকারী ইতোমধ্যে নিবন্ধিত' });
        }

        // পাসওয়ার্ড হ্যাশ করা হচ্ছে যাতে নিরাপত্তা নিশ্চিত করা যায়
        const hashedPassword = await hashPassword(password);

        // নতুন ব্যবহারকারী তৈরি করা হচ্ছে দেওয়া ইনপুট তথ্য ব্যবহার করে
        const newUser = createNewUser(userRepository, username, email, hashedPassword, phone);

        // নতুন ব্যবহারকারী ডাটাবেজে সংরক্ষণ করা হচ্ছে
        await saveUser(userRepository, newUser);

        let response;
        if (email) {
            // যদি ইমেইল প্রদান করা হয়, তাহলে ইমেইল দ্বারা নিবন্ধন পরিচালনা করা হচ্ছে
            response = await handleEmailRegistration(userRepository, newUser);
        } else if (phone) {
            // যদি ফোন নম্বর প্রদান করা হয়, তাহলে ফোন দ্বারা নিবন্ধন পরিচালনা করা হচ্ছে
            response = await handlePhoneRegistration(userRepository, newUser, phone);
        } else {
            // যদি না ইমেইল না ফোন প্রদান করা হয়, তাহলে ব্যবহারকারী সরাসরি সক্রিয় করা হচ্ছে
            newUser.isActivated = true;
            await saveUser(userRepository, newUser);
            response = { success: true, message: 'ব্যবহারকারী সফলভাবে নিবন্ধিত। প্রয়োজনীয় যাচাই সম্পন্ন করুন।' };
        }

        // যদি নিবন্ধন সফল হয়, তাহলে 201 স্টেটাস কোড এবং সফলতার মেসেজ পাঠানো হচ্ছে
        if (response.success) {
            return res.status(201).json({ message: response.message });
        } else {
            // যদি কোন ত্রূটি  ঘটে, তাহলে 500 স্টেটাস কোড এবং ত্রূটি র মেসেজ পাঠানো হচ্ছে
            return res.status(500).json({ message: response.message, error: response.error });
        }
    } catch (err) {
        // যদি কোন অপ্রত্যাশিত ত্রূটি  ঘটে, তাহলে 500 স্টেটাস কোড এবং ত্রূটি র মেসেজ পাঠানো হচ্ছে
        return res.status(500).json({ message: 'ব্যবহারকারী নিবন্ধন করতে ত্রূটি ', error: err.message });
    }
};

Activate Account by Token

/**
 * @function activateAccount
 * @description ব্যবহারকারীর অ্যাকাউন্ট অ্যাক্টিভেশনের জন্য ফাংশনটি ব্যবহৃত হয়। অ্যাক্টিভেশন টোকেনের মাধ্যমে অ্যাকাউন্ট অ্যাক্টিভ করা হয়।
 * @param {object} req - রিকোয়েস্ট অবজেক্ট যা ক্লায়েন্ট থেকে প্রেরিত ডেটা ধারণ করে। এখানে প্যারামস থেকে অ্যাক্টিভেশন টোকেন গ্রহণ করা হয়।
 * @param {object} res - রেসপন্স অবজেক্ট যা ক্লায়েন্টকে রেসপন্স পাঠাতে ব্যবহৃত হয়।
 * @returns {Promise<void>} - ফাংশনটি একটি প্রমিস রিটার্ন করে যা রিকোয়েস্ট সম্পন্ন হওয়ার পর রেসপন্স প্রদান করে।
 */
const activateAccount = async (req, res) => {
    // রিকোয়েস্ট প্যারামস থেকে টোকেন গ্রহণ করা হচ্ছে
    const { token } = req.params;
    
    try {
        // টোকেন যাচাই করা হচ্ছে এবং এর মাধ্যমে ইউজার আইডি ডিকোড করা হচ্ছে
        const decoded = await verifyToken(token);
        
        // ডাটাবেজ থেকে ইউজার আইডি দ্বারা ব্যবহারকারী খুঁজে বের করা হচ্ছে
        const user = await findUserById(decoded.id);

        // যদি ব্যবহারকারী পাওয়া না যায়, তাহলে ইনভ্যালিড অ্যাক্টিভেশন লিঙ্ক বার্তা রিটার্ন করা হচ্ছে
        if (!user) {
            return res.status(400).json({ message: 'অবৈধ অ্যাক্টিভেশন লিঙ্ক' });
        }

        // ব্যবহারকারীর অ্যাকাউন্ট অ্যাক্টিভ করা হচ্ছে
        await activateUserAccount(user);

        // সফল অ্যাক্টিভেশনের পর ব্যবহারকারীকে সাফল্যের বার্তা পাঠানো হচ্ছে
        res.status(200).json({ message: 'অ্যাকাউন্ট সফলভাবে অ্যাক্টিভ করা হয়েছে' });
    } catch (err) {
        // যদি কোন ত্রূটি  ঘটে, তাহলে ত্রূটি র বার্তা সহ রেসপন্স পাঠানো হচ্ছে
        res.status(400).json({ message: 'অবৈধ বা মেয়াদোত্তীর্ণ অ্যাক্টিভেশন লিঙ্ক', error: err.message });
    }
};

How can we help?