আমাদের প্রজেক্ট টি একটি 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 });
}
};